├── .cargo └── config.toml ├── .editorconfig ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .vscode └── tasks.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── fuzz ├── .gitignore └── _examples ├── rust-toolchain.toml ├── rustfmt.toml ├── scripts ├── checkalexa1m └── checktop500 ├── sectxtbin ├── Cargo.toml ├── README.md └── src │ ├── main.rs │ ├── network.rs │ ├── settings.rs │ ├── status.rs │ └── website.rs ├── sectxtfuzz ├── Cargo.toml └── src │ └── main.rs └── sectxtlib ├── Cargo.toml ├── README.md ├── resources └── test │ ├── .gitattributes │ ├── gen_unsigned │ ├── gen_unsigned_0001.stxt │ ├── gen_unsigned_0002.stxt │ ├── gen_unsigned_0003.stxt │ ├── gen_unsigned_0004.stxt │ ├── gen_unsigned_0005.stxt │ ├── gen_unsigned_0006.stxt │ ├── gen_unsigned_0007.stxt │ ├── gen_unsigned_0008.stxt │ ├── gen_unsigned_0009.stxt │ ├── gen_unsigned_0010.stxt │ ├── gen_unsigned_0011.stxt │ ├── gen_unsigned_0012.stxt │ ├── gen_unsigned_0013.stxt │ ├── gen_unsigned_0014.stxt │ ├── gen_unsigned_0015.stxt │ ├── gen_unsigned_0016.stxt │ ├── gen_unsigned_0017.stxt │ ├── gen_unsigned_0018.stxt │ ├── gen_unsigned_0019.stxt │ ├── gen_unsigned_0020.stxt │ ├── gen_unsigned_0021.stxt │ ├── gen_unsigned_0022.stxt │ ├── gen_unsigned_0023.stxt │ ├── gen_unsigned_0024.stxt │ ├── gen_unsigned_0025.stxt │ ├── gen_unsigned_0026.stxt │ ├── gen_unsigned_0027.stxt │ ├── gen_unsigned_0028.stxt │ ├── gen_unsigned_0029.stxt │ ├── gen_unsigned_0030.stxt │ ├── gen_unsigned_0031.stxt │ ├── gen_unsigned_0032.stxt │ ├── gen_unsigned_0033.stxt │ ├── gen_unsigned_0034.stxt │ ├── gen_unsigned_0035.stxt │ ├── gen_unsigned_0036.stxt │ ├── gen_unsigned_0037.stxt │ ├── gen_unsigned_0038.stxt │ ├── gen_unsigned_0039.stxt │ ├── gen_unsigned_0040.stxt │ ├── gen_unsigned_0041.stxt │ ├── gen_unsigned_0042.stxt │ ├── gen_unsigned_0043.stxt │ ├── gen_unsigned_0044.stxt │ ├── gen_unsigned_0045.stxt │ ├── gen_unsigned_0046.stxt │ ├── gen_unsigned_0047.stxt │ ├── gen_unsigned_0048.stxt │ ├── gen_unsigned_0049.stxt │ ├── gen_unsigned_0050.stxt │ ├── gen_unsigned_0051.stxt │ ├── gen_unsigned_0052.stxt │ ├── gen_unsigned_0053.stxt │ ├── gen_unsigned_0054.stxt │ ├── gen_unsigned_0055.stxt │ ├── gen_unsigned_0056.stxt │ ├── gen_unsigned_0057.stxt │ ├── gen_unsigned_0058.stxt │ ├── gen_unsigned_0059.stxt │ ├── gen_unsigned_0060.stxt │ ├── gen_unsigned_0061.stxt │ ├── gen_unsigned_0062.stxt │ ├── gen_unsigned_0063.stxt │ ├── gen_unsigned_0064.stxt │ ├── gen_unsigned_0065.stxt │ ├── gen_unsigned_0066.stxt │ ├── gen_unsigned_0067.stxt │ ├── gen_unsigned_0068.stxt │ ├── gen_unsigned_0069.stxt │ ├── gen_unsigned_0070.stxt │ ├── gen_unsigned_0071.stxt │ ├── gen_unsigned_0072.stxt │ ├── gen_unsigned_0073.stxt │ ├── gen_unsigned_0074.stxt │ ├── gen_unsigned_0075.stxt │ ├── gen_unsigned_0076.stxt │ ├── gen_unsigned_0077.stxt │ ├── gen_unsigned_0078.stxt │ ├── gen_unsigned_0079.stxt │ ├── gen_unsigned_0080.stxt │ ├── gen_unsigned_0081.stxt │ ├── gen_unsigned_0082.stxt │ ├── gen_unsigned_0083.stxt │ ├── gen_unsigned_0084.stxt │ ├── gen_unsigned_0085.stxt │ ├── gen_unsigned_0086.stxt │ ├── gen_unsigned_0087.stxt │ ├── gen_unsigned_0088.stxt │ ├── gen_unsigned_0089.stxt │ ├── gen_unsigned_0090.stxt │ ├── gen_unsigned_0091.stxt │ ├── gen_unsigned_0092.stxt │ ├── gen_unsigned_0093.stxt │ ├── gen_unsigned_0094.stxt │ ├── gen_unsigned_0095.stxt │ ├── gen_unsigned_0096.stxt │ ├── gen_unsigned_0097.stxt │ ├── gen_unsigned_0098.stxt │ ├── gen_unsigned_0099.stxt │ └── gen_unsigned_0100.stxt │ ├── valid_signed │ ├── opera_com.stxt │ ├── redhat.stxt │ ├── rfc_signed.stxt │ └── securitytxt_org.stxt │ └── valid_unsigned │ ├── github_com.stxt │ ├── rfc_unsigned.stxt │ ├── wordpress_org.stxt │ └── www_gov_uk.stxt └── src ├── fields.rs ├── lib.rs ├── parse_error.rs ├── parsers.rs ├── pgpcleartextmessage.rs ├── raw_field.rs ├── securitytxt.rs └── securitytxt_options.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | rustflags = ["--cfg", "tracing_unstable"] 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | max_line_length = 120 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Main 2 | 3 | on: push 4 | 5 | jobs: 6 | test_publish: 7 | name: Test and publish 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v3 12 | 13 | - name: Install dependencies 14 | run: make setup 15 | 16 | - name: Run tests 17 | run: make test 18 | 19 | - name: Build 20 | run: make build 21 | 22 | - name: Publish (dry run) 23 | if: ${{ !startsWith(github.ref, 'refs/tags/v') }} # Only dry-run if not a release. 24 | run: | 25 | cargo publish --dry-run -p sectxtlib 26 | #cargo publish --dry-run -p sectxt # Fails if sectxtlib is not actually published 27 | 28 | - name: Publish 29 | if: startsWith(github.ref, 'refs/tags/v') # Only publish for new release. 30 | env: 31 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 32 | run: make publish 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Rust 2 | # Generated by Cargo 3 | # will have compiled files and executables 4 | /target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | #Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # IntelliJ/RustRover files 14 | .idea/ 15 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "cargo", 6 | "subcommand": "build", 7 | "presentation": { 8 | "panel": "shared", 9 | "reveal": "always", 10 | "focus": true 11 | }, 12 | "problemMatcher": [ 13 | "$rustc" 14 | ], 15 | "group": { 16 | "kind": "build", 17 | "isDefault": true 18 | } 19 | }, 20 | { 21 | "label": "test", 22 | "type": "shell", 23 | "command": "make test", 24 | "presentation": { 25 | "panel": "shared", 26 | "reveal": "always", 27 | "focus": true 28 | }, 29 | "problemMatcher": [], 30 | "group": { 31 | "kind": "test", 32 | "isDefault": true 33 | } 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "afl" 22 | version = "0.15.13" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "b784d6332a6978dd29861676de9df37aa37ed8852341db6340bd75eb82bc7a69" 25 | dependencies = [ 26 | "home", 27 | "libc", 28 | "rustc_version", 29 | "xdg", 30 | ] 31 | 32 | [[package]] 33 | name = "aho-corasick" 34 | version = "1.1.3" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 37 | dependencies = [ 38 | "memchr", 39 | ] 40 | 41 | [[package]] 42 | name = "android-tzdata" 43 | version = "0.1.1" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 46 | 47 | [[package]] 48 | name = "android_system_properties" 49 | version = "0.1.5" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 52 | dependencies = [ 53 | "libc", 54 | ] 55 | 56 | [[package]] 57 | name = "anstream" 58 | version = "0.6.18" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" 61 | dependencies = [ 62 | "anstyle", 63 | "anstyle-parse", 64 | "anstyle-query", 65 | "anstyle-wincon", 66 | "colorchoice", 67 | "is_terminal_polyfill", 68 | "utf8parse", 69 | ] 70 | 71 | [[package]] 72 | name = "anstyle" 73 | version = "1.0.10" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" 76 | 77 | [[package]] 78 | name = "anstyle-parse" 79 | version = "0.2.6" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" 82 | dependencies = [ 83 | "utf8parse", 84 | ] 85 | 86 | [[package]] 87 | name = "anstyle-query" 88 | version = "1.1.2" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" 91 | dependencies = [ 92 | "windows-sys 0.59.0", 93 | ] 94 | 95 | [[package]] 96 | name = "anstyle-wincon" 97 | version = "3.0.7" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" 100 | dependencies = [ 101 | "anstyle", 102 | "once_cell", 103 | "windows-sys 0.59.0", 104 | ] 105 | 106 | [[package]] 107 | name = "anyhow" 108 | version = "1.0.95" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" 111 | 112 | [[package]] 113 | name = "argh" 114 | version = "0.1.13" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "34ff18325c8a36b82f992e533ece1ec9f9a9db446bd1c14d4f936bac88fcd240" 117 | dependencies = [ 118 | "argh_derive", 119 | "argh_shared", 120 | "rust-fuzzy-search", 121 | ] 122 | 123 | [[package]] 124 | name = "argh_derive" 125 | version = "0.1.13" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "adb7b2b83a50d329d5d8ccc620f5c7064028828538bdf5646acd60dc1f767803" 128 | dependencies = [ 129 | "argh_shared", 130 | "proc-macro2", 131 | "quote", 132 | "syn", 133 | ] 134 | 135 | [[package]] 136 | name = "argh_shared" 137 | version = "0.1.13" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "a464143cc82dedcdc3928737445362466b7674b5db4e2eb8e869846d6d84f4f6" 140 | dependencies = [ 141 | "serde", 142 | ] 143 | 144 | [[package]] 145 | name = "atomic-waker" 146 | version = "1.1.2" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 149 | 150 | [[package]] 151 | name = "autocfg" 152 | version = "1.4.0" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 155 | 156 | [[package]] 157 | name = "backtrace" 158 | version = "0.3.74" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 161 | dependencies = [ 162 | "addr2line", 163 | "cfg-if", 164 | "libc", 165 | "miniz_oxide", 166 | "object", 167 | "rustc-demangle", 168 | "windows-targets", 169 | ] 170 | 171 | [[package]] 172 | name = "base64" 173 | version = "0.22.1" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 176 | 177 | [[package]] 178 | name = "bitflags" 179 | version = "2.8.0" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" 182 | 183 | [[package]] 184 | name = "bumpalo" 185 | version = "3.17.0" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 188 | 189 | [[package]] 190 | name = "bytes" 191 | version = "1.10.0" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" 194 | 195 | [[package]] 196 | name = "cc" 197 | version = "1.2.11" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "e4730490333d58093109dc02c23174c3f4d490998c3fed3cc8e82d57afedb9cf" 200 | dependencies = [ 201 | "shlex", 202 | ] 203 | 204 | [[package]] 205 | name = "cfg-if" 206 | version = "1.0.0" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 209 | 210 | [[package]] 211 | name = "chrono" 212 | version = "0.4.39" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" 215 | dependencies = [ 216 | "android-tzdata", 217 | "iana-time-zone", 218 | "js-sys", 219 | "num-traits", 220 | "wasm-bindgen", 221 | "windows-targets", 222 | ] 223 | 224 | [[package]] 225 | name = "colorchoice" 226 | version = "1.0.3" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" 229 | 230 | [[package]] 231 | name = "core-foundation" 232 | version = "0.9.4" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 235 | dependencies = [ 236 | "core-foundation-sys", 237 | "libc", 238 | ] 239 | 240 | [[package]] 241 | name = "core-foundation-sys" 242 | version = "0.8.7" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 245 | 246 | [[package]] 247 | name = "displaydoc" 248 | version = "0.2.5" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 251 | dependencies = [ 252 | "proc-macro2", 253 | "quote", 254 | "syn", 255 | ] 256 | 257 | [[package]] 258 | name = "encoding_rs" 259 | version = "0.8.35" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 262 | dependencies = [ 263 | "cfg-if", 264 | ] 265 | 266 | [[package]] 267 | name = "equivalent" 268 | version = "1.0.1" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 271 | 272 | [[package]] 273 | name = "errno" 274 | version = "0.3.10" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 277 | dependencies = [ 278 | "libc", 279 | "windows-sys 0.59.0", 280 | ] 281 | 282 | [[package]] 283 | name = "fastrand" 284 | version = "2.3.0" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 287 | 288 | [[package]] 289 | name = "fnv" 290 | version = "1.0.7" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 293 | 294 | [[package]] 295 | name = "foreign-types" 296 | version = "0.3.2" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 299 | dependencies = [ 300 | "foreign-types-shared", 301 | ] 302 | 303 | [[package]] 304 | name = "foreign-types-shared" 305 | version = "0.1.1" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 308 | 309 | [[package]] 310 | name = "form_urlencoded" 311 | version = "1.2.1" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 314 | dependencies = [ 315 | "percent-encoding", 316 | ] 317 | 318 | [[package]] 319 | name = "futures" 320 | version = "0.3.31" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 323 | dependencies = [ 324 | "futures-channel", 325 | "futures-core", 326 | "futures-executor", 327 | "futures-io", 328 | "futures-sink", 329 | "futures-task", 330 | "futures-util", 331 | ] 332 | 333 | [[package]] 334 | name = "futures-channel" 335 | version = "0.3.31" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 338 | dependencies = [ 339 | "futures-core", 340 | "futures-sink", 341 | ] 342 | 343 | [[package]] 344 | name = "futures-core" 345 | version = "0.3.31" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 348 | 349 | [[package]] 350 | name = "futures-executor" 351 | version = "0.3.31" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 354 | dependencies = [ 355 | "futures-core", 356 | "futures-task", 357 | "futures-util", 358 | ] 359 | 360 | [[package]] 361 | name = "futures-io" 362 | version = "0.3.31" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 365 | 366 | [[package]] 367 | name = "futures-macro" 368 | version = "0.3.31" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 371 | dependencies = [ 372 | "proc-macro2", 373 | "quote", 374 | "syn", 375 | ] 376 | 377 | [[package]] 378 | name = "futures-sink" 379 | version = "0.3.31" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 382 | 383 | [[package]] 384 | name = "futures-task" 385 | version = "0.3.31" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 388 | 389 | [[package]] 390 | name = "futures-util" 391 | version = "0.3.31" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 394 | dependencies = [ 395 | "futures-channel", 396 | "futures-core", 397 | "futures-io", 398 | "futures-macro", 399 | "futures-sink", 400 | "futures-task", 401 | "memchr", 402 | "pin-project-lite", 403 | "pin-utils", 404 | "slab", 405 | ] 406 | 407 | [[package]] 408 | name = "getrandom" 409 | version = "0.2.15" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 412 | dependencies = [ 413 | "cfg-if", 414 | "libc", 415 | "wasi 0.11.0+wasi-snapshot-preview1", 416 | ] 417 | 418 | [[package]] 419 | name = "getrandom" 420 | version = "0.3.1" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" 423 | dependencies = [ 424 | "cfg-if", 425 | "libc", 426 | "wasi 0.13.3+wasi-0.2.2", 427 | "windows-targets", 428 | ] 429 | 430 | [[package]] 431 | name = "gimli" 432 | version = "0.31.1" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 435 | 436 | [[package]] 437 | name = "h2" 438 | version = "0.4.7" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" 441 | dependencies = [ 442 | "atomic-waker", 443 | "bytes", 444 | "fnv", 445 | "futures-core", 446 | "futures-sink", 447 | "http", 448 | "indexmap", 449 | "slab", 450 | "tokio", 451 | "tokio-util", 452 | "tracing", 453 | ] 454 | 455 | [[package]] 456 | name = "hashbrown" 457 | version = "0.15.2" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 460 | 461 | [[package]] 462 | name = "home" 463 | version = "0.5.11" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" 466 | dependencies = [ 467 | "windows-sys 0.59.0", 468 | ] 469 | 470 | [[package]] 471 | name = "http" 472 | version = "1.2.0" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" 475 | dependencies = [ 476 | "bytes", 477 | "fnv", 478 | "itoa", 479 | ] 480 | 481 | [[package]] 482 | name = "http-body" 483 | version = "1.0.1" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 486 | dependencies = [ 487 | "bytes", 488 | "http", 489 | ] 490 | 491 | [[package]] 492 | name = "http-body-util" 493 | version = "0.1.2" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" 496 | dependencies = [ 497 | "bytes", 498 | "futures-util", 499 | "http", 500 | "http-body", 501 | "pin-project-lite", 502 | ] 503 | 504 | [[package]] 505 | name = "httparse" 506 | version = "1.10.0" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" 509 | 510 | [[package]] 511 | name = "human-panic" 512 | version = "2.0.2" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | checksum = "80b84a66a325082740043a6c28bbea400c129eac0d3a27673a1de971e44bf1f7" 515 | dependencies = [ 516 | "anstream", 517 | "anstyle", 518 | "backtrace", 519 | "os_info", 520 | "serde", 521 | "serde_derive", 522 | "toml", 523 | "uuid", 524 | ] 525 | 526 | [[package]] 527 | name = "hyper" 528 | version = "1.6.0" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" 531 | dependencies = [ 532 | "bytes", 533 | "futures-channel", 534 | "futures-util", 535 | "h2", 536 | "http", 537 | "http-body", 538 | "httparse", 539 | "itoa", 540 | "pin-project-lite", 541 | "smallvec", 542 | "tokio", 543 | "want", 544 | ] 545 | 546 | [[package]] 547 | name = "hyper-rustls" 548 | version = "0.27.5" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" 551 | dependencies = [ 552 | "futures-util", 553 | "http", 554 | "hyper", 555 | "hyper-util", 556 | "rustls", 557 | "rustls-pki-types", 558 | "tokio", 559 | "tokio-rustls", 560 | "tower-service", 561 | ] 562 | 563 | [[package]] 564 | name = "hyper-tls" 565 | version = "0.6.0" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 568 | dependencies = [ 569 | "bytes", 570 | "http-body-util", 571 | "hyper", 572 | "hyper-util", 573 | "native-tls", 574 | "tokio", 575 | "tokio-native-tls", 576 | "tower-service", 577 | ] 578 | 579 | [[package]] 580 | name = "hyper-util" 581 | version = "0.1.10" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" 584 | dependencies = [ 585 | "bytes", 586 | "futures-channel", 587 | "futures-util", 588 | "http", 589 | "http-body", 590 | "hyper", 591 | "pin-project-lite", 592 | "socket2", 593 | "tokio", 594 | "tower-service", 595 | "tracing", 596 | ] 597 | 598 | [[package]] 599 | name = "iana-time-zone" 600 | version = "0.1.61" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" 603 | dependencies = [ 604 | "android_system_properties", 605 | "core-foundation-sys", 606 | "iana-time-zone-haiku", 607 | "js-sys", 608 | "wasm-bindgen", 609 | "windows-core", 610 | ] 611 | 612 | [[package]] 613 | name = "iana-time-zone-haiku" 614 | version = "0.1.2" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 617 | dependencies = [ 618 | "cc", 619 | ] 620 | 621 | [[package]] 622 | name = "icu_collections" 623 | version = "1.5.0" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 626 | dependencies = [ 627 | "displaydoc", 628 | "yoke", 629 | "zerofrom", 630 | "zerovec", 631 | ] 632 | 633 | [[package]] 634 | name = "icu_locid" 635 | version = "1.5.0" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 638 | dependencies = [ 639 | "displaydoc", 640 | "litemap", 641 | "tinystr", 642 | "writeable", 643 | "zerovec", 644 | ] 645 | 646 | [[package]] 647 | name = "icu_locid_transform" 648 | version = "1.5.0" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" 651 | dependencies = [ 652 | "displaydoc", 653 | "icu_locid", 654 | "icu_locid_transform_data", 655 | "icu_provider", 656 | "tinystr", 657 | "zerovec", 658 | ] 659 | 660 | [[package]] 661 | name = "icu_locid_transform_data" 662 | version = "1.5.0" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" 665 | 666 | [[package]] 667 | name = "icu_normalizer" 668 | version = "1.5.0" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" 671 | dependencies = [ 672 | "displaydoc", 673 | "icu_collections", 674 | "icu_normalizer_data", 675 | "icu_properties", 676 | "icu_provider", 677 | "smallvec", 678 | "utf16_iter", 679 | "utf8_iter", 680 | "write16", 681 | "zerovec", 682 | ] 683 | 684 | [[package]] 685 | name = "icu_normalizer_data" 686 | version = "1.5.0" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" 689 | 690 | [[package]] 691 | name = "icu_properties" 692 | version = "1.5.1" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" 695 | dependencies = [ 696 | "displaydoc", 697 | "icu_collections", 698 | "icu_locid_transform", 699 | "icu_properties_data", 700 | "icu_provider", 701 | "tinystr", 702 | "zerovec", 703 | ] 704 | 705 | [[package]] 706 | name = "icu_properties_data" 707 | version = "1.5.0" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" 710 | 711 | [[package]] 712 | name = "icu_provider" 713 | version = "1.5.0" 714 | source = "registry+https://github.com/rust-lang/crates.io-index" 715 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 716 | dependencies = [ 717 | "displaydoc", 718 | "icu_locid", 719 | "icu_provider_macros", 720 | "stable_deref_trait", 721 | "tinystr", 722 | "writeable", 723 | "yoke", 724 | "zerofrom", 725 | "zerovec", 726 | ] 727 | 728 | [[package]] 729 | name = "icu_provider_macros" 730 | version = "1.5.0" 731 | source = "registry+https://github.com/rust-lang/crates.io-index" 732 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 733 | dependencies = [ 734 | "proc-macro2", 735 | "quote", 736 | "syn", 737 | ] 738 | 739 | [[package]] 740 | name = "idna" 741 | version = "1.0.3" 742 | source = "registry+https://github.com/rust-lang/crates.io-index" 743 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 744 | dependencies = [ 745 | "idna_adapter", 746 | "smallvec", 747 | "utf8_iter", 748 | ] 749 | 750 | [[package]] 751 | name = "idna_adapter" 752 | version = "1.2.0" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" 755 | dependencies = [ 756 | "icu_normalizer", 757 | "icu_properties", 758 | ] 759 | 760 | [[package]] 761 | name = "indexmap" 762 | version = "2.7.1" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" 765 | dependencies = [ 766 | "equivalent", 767 | "hashbrown", 768 | ] 769 | 770 | [[package]] 771 | name = "ipnet" 772 | version = "2.11.0" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 775 | 776 | [[package]] 777 | name = "iri-string" 778 | version = "0.7.7" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "dc0f0a572e8ffe56e2ff4f769f32ffe919282c3916799f8b68688b6030063bea" 781 | dependencies = [ 782 | "memchr", 783 | "serde", 784 | ] 785 | 786 | [[package]] 787 | name = "is_terminal_polyfill" 788 | version = "1.70.1" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 791 | 792 | [[package]] 793 | name = "itoa" 794 | version = "1.0.14" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" 797 | 798 | [[package]] 799 | name = "js-sys" 800 | version = "0.3.77" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 803 | dependencies = [ 804 | "once_cell", 805 | "wasm-bindgen", 806 | ] 807 | 808 | [[package]] 809 | name = "lazy_static" 810 | version = "1.5.0" 811 | source = "registry+https://github.com/rust-lang/crates.io-index" 812 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 813 | 814 | [[package]] 815 | name = "libc" 816 | version = "0.2.169" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 819 | 820 | [[package]] 821 | name = "linux-raw-sys" 822 | version = "0.4.15" 823 | source = "registry+https://github.com/rust-lang/crates.io-index" 824 | checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 825 | 826 | [[package]] 827 | name = "litemap" 828 | version = "0.7.4" 829 | source = "registry+https://github.com/rust-lang/crates.io-index" 830 | checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" 831 | 832 | [[package]] 833 | name = "log" 834 | version = "0.4.25" 835 | source = "registry+https://github.com/rust-lang/crates.io-index" 836 | checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" 837 | 838 | [[package]] 839 | name = "matchers" 840 | version = "0.1.0" 841 | source = "registry+https://github.com/rust-lang/crates.io-index" 842 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 843 | dependencies = [ 844 | "regex-automata 0.1.10", 845 | ] 846 | 847 | [[package]] 848 | name = "memchr" 849 | version = "2.7.4" 850 | source = "registry+https://github.com/rust-lang/crates.io-index" 851 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 852 | 853 | [[package]] 854 | name = "mime" 855 | version = "0.3.17" 856 | source = "registry+https://github.com/rust-lang/crates.io-index" 857 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 858 | 859 | [[package]] 860 | name = "minimal-lexical" 861 | version = "0.2.1" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 864 | 865 | [[package]] 866 | name = "miniz_oxide" 867 | version = "0.8.3" 868 | source = "registry+https://github.com/rust-lang/crates.io-index" 869 | checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" 870 | dependencies = [ 871 | "adler2", 872 | ] 873 | 874 | [[package]] 875 | name = "mio" 876 | version = "1.0.3" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 879 | dependencies = [ 880 | "libc", 881 | "wasi 0.11.0+wasi-snapshot-preview1", 882 | "windows-sys 0.52.0", 883 | ] 884 | 885 | [[package]] 886 | name = "native-tls" 887 | version = "0.2.13" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" 890 | dependencies = [ 891 | "libc", 892 | "log", 893 | "openssl", 894 | "openssl-probe", 895 | "openssl-sys", 896 | "schannel", 897 | "security-framework", 898 | "security-framework-sys", 899 | "tempfile", 900 | ] 901 | 902 | [[package]] 903 | name = "nom" 904 | version = "7.1.3" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 907 | dependencies = [ 908 | "memchr", 909 | "minimal-lexical", 910 | ] 911 | 912 | [[package]] 913 | name = "nu-ansi-term" 914 | version = "0.46.0" 915 | source = "registry+https://github.com/rust-lang/crates.io-index" 916 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 917 | dependencies = [ 918 | "overload", 919 | "winapi", 920 | ] 921 | 922 | [[package]] 923 | name = "num-traits" 924 | version = "0.2.19" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 927 | dependencies = [ 928 | "autocfg", 929 | ] 930 | 931 | [[package]] 932 | name = "object" 933 | version = "0.36.7" 934 | source = "registry+https://github.com/rust-lang/crates.io-index" 935 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 936 | dependencies = [ 937 | "memchr", 938 | ] 939 | 940 | [[package]] 941 | name = "once_cell" 942 | version = "1.20.2" 943 | source = "registry+https://github.com/rust-lang/crates.io-index" 944 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 945 | 946 | [[package]] 947 | name = "openssl" 948 | version = "0.10.70" 949 | source = "registry+https://github.com/rust-lang/crates.io-index" 950 | checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" 951 | dependencies = [ 952 | "bitflags", 953 | "cfg-if", 954 | "foreign-types", 955 | "libc", 956 | "once_cell", 957 | "openssl-macros", 958 | "openssl-sys", 959 | ] 960 | 961 | [[package]] 962 | name = "openssl-macros" 963 | version = "0.1.1" 964 | source = "registry+https://github.com/rust-lang/crates.io-index" 965 | checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 966 | dependencies = [ 967 | "proc-macro2", 968 | "quote", 969 | "syn", 970 | ] 971 | 972 | [[package]] 973 | name = "openssl-probe" 974 | version = "0.1.6" 975 | source = "registry+https://github.com/rust-lang/crates.io-index" 976 | checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" 977 | 978 | [[package]] 979 | name = "openssl-sys" 980 | version = "0.9.105" 981 | source = "registry+https://github.com/rust-lang/crates.io-index" 982 | checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" 983 | dependencies = [ 984 | "cc", 985 | "libc", 986 | "pkg-config", 987 | "vcpkg", 988 | ] 989 | 990 | [[package]] 991 | name = "os_info" 992 | version = "3.9.2" 993 | source = "registry+https://github.com/rust-lang/crates.io-index" 994 | checksum = "6e6520c8cc998c5741ee68ec1dc369fc47e5f0ea5320018ecf2a1ccd6328f48b" 995 | dependencies = [ 996 | "log", 997 | "serde", 998 | "windows-sys 0.52.0", 999 | ] 1000 | 1001 | [[package]] 1002 | name = "overload" 1003 | version = "0.1.1" 1004 | source = "registry+https://github.com/rust-lang/crates.io-index" 1005 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 1006 | 1007 | [[package]] 1008 | name = "oxilangtag" 1009 | version = "0.1.5" 1010 | source = "registry+https://github.com/rust-lang/crates.io-index" 1011 | checksum = "23f3f87617a86af77fa3691e6350483e7154c2ead9f1261b75130e21ca0f8acb" 1012 | dependencies = [ 1013 | "serde", 1014 | ] 1015 | 1016 | [[package]] 1017 | name = "percent-encoding" 1018 | version = "2.3.1" 1019 | source = "registry+https://github.com/rust-lang/crates.io-index" 1020 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1021 | 1022 | [[package]] 1023 | name = "pin-project-lite" 1024 | version = "0.2.16" 1025 | source = "registry+https://github.com/rust-lang/crates.io-index" 1026 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 1027 | 1028 | [[package]] 1029 | name = "pin-utils" 1030 | version = "0.1.0" 1031 | source = "registry+https://github.com/rust-lang/crates.io-index" 1032 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1033 | 1034 | [[package]] 1035 | name = "pkg-config" 1036 | version = "0.3.31" 1037 | source = "registry+https://github.com/rust-lang/crates.io-index" 1038 | checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" 1039 | 1040 | [[package]] 1041 | name = "proc-macro2" 1042 | version = "1.0.93" 1043 | source = "registry+https://github.com/rust-lang/crates.io-index" 1044 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" 1045 | dependencies = [ 1046 | "unicode-ident", 1047 | ] 1048 | 1049 | [[package]] 1050 | name = "quote" 1051 | version = "1.0.38" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 1054 | dependencies = [ 1055 | "proc-macro2", 1056 | ] 1057 | 1058 | [[package]] 1059 | name = "regex" 1060 | version = "1.11.1" 1061 | source = "registry+https://github.com/rust-lang/crates.io-index" 1062 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 1063 | dependencies = [ 1064 | "aho-corasick", 1065 | "memchr", 1066 | "regex-automata 0.4.9", 1067 | "regex-syntax 0.8.5", 1068 | ] 1069 | 1070 | [[package]] 1071 | name = "regex-automata" 1072 | version = "0.1.10" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 1075 | dependencies = [ 1076 | "regex-syntax 0.6.29", 1077 | ] 1078 | 1079 | [[package]] 1080 | name = "regex-automata" 1081 | version = "0.4.9" 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" 1083 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 1084 | dependencies = [ 1085 | "aho-corasick", 1086 | "memchr", 1087 | "regex-syntax 0.8.5", 1088 | ] 1089 | 1090 | [[package]] 1091 | name = "regex-syntax" 1092 | version = "0.6.29" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 1095 | 1096 | [[package]] 1097 | name = "regex-syntax" 1098 | version = "0.8.5" 1099 | source = "registry+https://github.com/rust-lang/crates.io-index" 1100 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 1101 | 1102 | [[package]] 1103 | name = "reqwest" 1104 | version = "0.12.12" 1105 | source = "registry+https://github.com/rust-lang/crates.io-index" 1106 | checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" 1107 | dependencies = [ 1108 | "base64", 1109 | "bytes", 1110 | "encoding_rs", 1111 | "futures-core", 1112 | "futures-util", 1113 | "h2", 1114 | "http", 1115 | "http-body", 1116 | "http-body-util", 1117 | "hyper", 1118 | "hyper-rustls", 1119 | "hyper-tls", 1120 | "hyper-util", 1121 | "ipnet", 1122 | "js-sys", 1123 | "log", 1124 | "mime", 1125 | "native-tls", 1126 | "once_cell", 1127 | "percent-encoding", 1128 | "pin-project-lite", 1129 | "rustls-pemfile", 1130 | "serde", 1131 | "serde_json", 1132 | "serde_urlencoded", 1133 | "sync_wrapper", 1134 | "system-configuration", 1135 | "tokio", 1136 | "tokio-native-tls", 1137 | "tower", 1138 | "tower-service", 1139 | "url", 1140 | "wasm-bindgen", 1141 | "wasm-bindgen-futures", 1142 | "web-sys", 1143 | "windows-registry", 1144 | ] 1145 | 1146 | [[package]] 1147 | name = "ring" 1148 | version = "0.17.8" 1149 | source = "registry+https://github.com/rust-lang/crates.io-index" 1150 | checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" 1151 | dependencies = [ 1152 | "cc", 1153 | "cfg-if", 1154 | "getrandom 0.2.15", 1155 | "libc", 1156 | "spin", 1157 | "untrusted", 1158 | "windows-sys 0.52.0", 1159 | ] 1160 | 1161 | [[package]] 1162 | name = "rust-fuzzy-search" 1163 | version = "0.1.1" 1164 | source = "registry+https://github.com/rust-lang/crates.io-index" 1165 | checksum = "a157657054ffe556d8858504af8a672a054a6e0bd9e8ee531059100c0fa11bb2" 1166 | 1167 | [[package]] 1168 | name = "rustc-demangle" 1169 | version = "0.1.24" 1170 | source = "registry+https://github.com/rust-lang/crates.io-index" 1171 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1172 | 1173 | [[package]] 1174 | name = "rustc_version" 1175 | version = "0.4.1" 1176 | source = "registry+https://github.com/rust-lang/crates.io-index" 1177 | checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 1178 | dependencies = [ 1179 | "semver", 1180 | ] 1181 | 1182 | [[package]] 1183 | name = "rustix" 1184 | version = "0.38.44" 1185 | source = "registry+https://github.com/rust-lang/crates.io-index" 1186 | checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 1187 | dependencies = [ 1188 | "bitflags", 1189 | "errno", 1190 | "libc", 1191 | "linux-raw-sys", 1192 | "windows-sys 0.59.0", 1193 | ] 1194 | 1195 | [[package]] 1196 | name = "rustls" 1197 | version = "0.23.22" 1198 | source = "registry+https://github.com/rust-lang/crates.io-index" 1199 | checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7" 1200 | dependencies = [ 1201 | "once_cell", 1202 | "rustls-pki-types", 1203 | "rustls-webpki", 1204 | "subtle", 1205 | "zeroize", 1206 | ] 1207 | 1208 | [[package]] 1209 | name = "rustls-pemfile" 1210 | version = "2.2.0" 1211 | source = "registry+https://github.com/rust-lang/crates.io-index" 1212 | checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" 1213 | dependencies = [ 1214 | "rustls-pki-types", 1215 | ] 1216 | 1217 | [[package]] 1218 | name = "rustls-pki-types" 1219 | version = "1.11.0" 1220 | source = "registry+https://github.com/rust-lang/crates.io-index" 1221 | checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" 1222 | 1223 | [[package]] 1224 | name = "rustls-webpki" 1225 | version = "0.102.8" 1226 | source = "registry+https://github.com/rust-lang/crates.io-index" 1227 | checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" 1228 | dependencies = [ 1229 | "ring", 1230 | "rustls-pki-types", 1231 | "untrusted", 1232 | ] 1233 | 1234 | [[package]] 1235 | name = "rustversion" 1236 | version = "1.0.19" 1237 | source = "registry+https://github.com/rust-lang/crates.io-index" 1238 | checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" 1239 | 1240 | [[package]] 1241 | name = "ryu" 1242 | version = "1.0.19" 1243 | source = "registry+https://github.com/rust-lang/crates.io-index" 1244 | checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" 1245 | 1246 | [[package]] 1247 | name = "schannel" 1248 | version = "0.1.27" 1249 | source = "registry+https://github.com/rust-lang/crates.io-index" 1250 | checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" 1251 | dependencies = [ 1252 | "windows-sys 0.59.0", 1253 | ] 1254 | 1255 | [[package]] 1256 | name = "sectxt" 1257 | version = "0.3.1" 1258 | dependencies = [ 1259 | "anyhow", 1260 | "argh", 1261 | "futures", 1262 | "human-panic", 1263 | "lazy_static", 1264 | "reqwest", 1265 | "sectxtlib", 1266 | "tokio", 1267 | "tracing", 1268 | "tracing-subscriber", 1269 | "url", 1270 | "valuable", 1271 | ] 1272 | 1273 | [[package]] 1274 | name = "sectxtfuzz" 1275 | version = "0.3.1" 1276 | dependencies = [ 1277 | "afl", 1278 | "sectxtlib", 1279 | ] 1280 | 1281 | [[package]] 1282 | name = "sectxtlib" 1283 | version = "0.3.1" 1284 | dependencies = [ 1285 | "chrono", 1286 | "iri-string", 1287 | "nom", 1288 | "oxilangtag", 1289 | "thiserror", 1290 | "valuable", 1291 | ] 1292 | 1293 | [[package]] 1294 | name = "security-framework" 1295 | version = "2.11.1" 1296 | source = "registry+https://github.com/rust-lang/crates.io-index" 1297 | checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 1298 | dependencies = [ 1299 | "bitflags", 1300 | "core-foundation", 1301 | "core-foundation-sys", 1302 | "libc", 1303 | "security-framework-sys", 1304 | ] 1305 | 1306 | [[package]] 1307 | name = "security-framework-sys" 1308 | version = "2.14.0" 1309 | source = "registry+https://github.com/rust-lang/crates.io-index" 1310 | checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" 1311 | dependencies = [ 1312 | "core-foundation-sys", 1313 | "libc", 1314 | ] 1315 | 1316 | [[package]] 1317 | name = "semver" 1318 | version = "1.0.25" 1319 | source = "registry+https://github.com/rust-lang/crates.io-index" 1320 | checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" 1321 | 1322 | [[package]] 1323 | name = "serde" 1324 | version = "1.0.217" 1325 | source = "registry+https://github.com/rust-lang/crates.io-index" 1326 | checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" 1327 | dependencies = [ 1328 | "serde_derive", 1329 | ] 1330 | 1331 | [[package]] 1332 | name = "serde_derive" 1333 | version = "1.0.217" 1334 | source = "registry+https://github.com/rust-lang/crates.io-index" 1335 | checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" 1336 | dependencies = [ 1337 | "proc-macro2", 1338 | "quote", 1339 | "syn", 1340 | ] 1341 | 1342 | [[package]] 1343 | name = "serde_json" 1344 | version = "1.0.138" 1345 | source = "registry+https://github.com/rust-lang/crates.io-index" 1346 | checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" 1347 | dependencies = [ 1348 | "itoa", 1349 | "memchr", 1350 | "ryu", 1351 | "serde", 1352 | ] 1353 | 1354 | [[package]] 1355 | name = "serde_spanned" 1356 | version = "0.6.8" 1357 | source = "registry+https://github.com/rust-lang/crates.io-index" 1358 | checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" 1359 | dependencies = [ 1360 | "serde", 1361 | ] 1362 | 1363 | [[package]] 1364 | name = "serde_urlencoded" 1365 | version = "0.7.1" 1366 | source = "registry+https://github.com/rust-lang/crates.io-index" 1367 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1368 | dependencies = [ 1369 | "form_urlencoded", 1370 | "itoa", 1371 | "ryu", 1372 | "serde", 1373 | ] 1374 | 1375 | [[package]] 1376 | name = "sharded-slab" 1377 | version = "0.1.7" 1378 | source = "registry+https://github.com/rust-lang/crates.io-index" 1379 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 1380 | dependencies = [ 1381 | "lazy_static", 1382 | ] 1383 | 1384 | [[package]] 1385 | name = "shlex" 1386 | version = "1.3.0" 1387 | source = "registry+https://github.com/rust-lang/crates.io-index" 1388 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1389 | 1390 | [[package]] 1391 | name = "slab" 1392 | version = "0.4.9" 1393 | source = "registry+https://github.com/rust-lang/crates.io-index" 1394 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1395 | dependencies = [ 1396 | "autocfg", 1397 | ] 1398 | 1399 | [[package]] 1400 | name = "smallvec" 1401 | version = "1.13.2" 1402 | source = "registry+https://github.com/rust-lang/crates.io-index" 1403 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1404 | 1405 | [[package]] 1406 | name = "socket2" 1407 | version = "0.5.8" 1408 | source = "registry+https://github.com/rust-lang/crates.io-index" 1409 | checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" 1410 | dependencies = [ 1411 | "libc", 1412 | "windows-sys 0.52.0", 1413 | ] 1414 | 1415 | [[package]] 1416 | name = "spin" 1417 | version = "0.9.8" 1418 | source = "registry+https://github.com/rust-lang/crates.io-index" 1419 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 1420 | 1421 | [[package]] 1422 | name = "stable_deref_trait" 1423 | version = "1.2.0" 1424 | source = "registry+https://github.com/rust-lang/crates.io-index" 1425 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1426 | 1427 | [[package]] 1428 | name = "subtle" 1429 | version = "2.6.1" 1430 | source = "registry+https://github.com/rust-lang/crates.io-index" 1431 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1432 | 1433 | [[package]] 1434 | name = "syn" 1435 | version = "2.0.98" 1436 | source = "registry+https://github.com/rust-lang/crates.io-index" 1437 | checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" 1438 | dependencies = [ 1439 | "proc-macro2", 1440 | "quote", 1441 | "unicode-ident", 1442 | ] 1443 | 1444 | [[package]] 1445 | name = "sync_wrapper" 1446 | version = "1.0.2" 1447 | source = "registry+https://github.com/rust-lang/crates.io-index" 1448 | checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 1449 | dependencies = [ 1450 | "futures-core", 1451 | ] 1452 | 1453 | [[package]] 1454 | name = "synstructure" 1455 | version = "0.13.1" 1456 | source = "registry+https://github.com/rust-lang/crates.io-index" 1457 | checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" 1458 | dependencies = [ 1459 | "proc-macro2", 1460 | "quote", 1461 | "syn", 1462 | ] 1463 | 1464 | [[package]] 1465 | name = "system-configuration" 1466 | version = "0.6.1" 1467 | source = "registry+https://github.com/rust-lang/crates.io-index" 1468 | checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" 1469 | dependencies = [ 1470 | "bitflags", 1471 | "core-foundation", 1472 | "system-configuration-sys", 1473 | ] 1474 | 1475 | [[package]] 1476 | name = "system-configuration-sys" 1477 | version = "0.6.0" 1478 | source = "registry+https://github.com/rust-lang/crates.io-index" 1479 | checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" 1480 | dependencies = [ 1481 | "core-foundation-sys", 1482 | "libc", 1483 | ] 1484 | 1485 | [[package]] 1486 | name = "tempfile" 1487 | version = "3.16.0" 1488 | source = "registry+https://github.com/rust-lang/crates.io-index" 1489 | checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" 1490 | dependencies = [ 1491 | "cfg-if", 1492 | "fastrand", 1493 | "getrandom 0.3.1", 1494 | "once_cell", 1495 | "rustix", 1496 | "windows-sys 0.59.0", 1497 | ] 1498 | 1499 | [[package]] 1500 | name = "thiserror" 1501 | version = "2.0.11" 1502 | source = "registry+https://github.com/rust-lang/crates.io-index" 1503 | checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" 1504 | dependencies = [ 1505 | "thiserror-impl", 1506 | ] 1507 | 1508 | [[package]] 1509 | name = "thiserror-impl" 1510 | version = "2.0.11" 1511 | source = "registry+https://github.com/rust-lang/crates.io-index" 1512 | checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" 1513 | dependencies = [ 1514 | "proc-macro2", 1515 | "quote", 1516 | "syn", 1517 | ] 1518 | 1519 | [[package]] 1520 | name = "thread_local" 1521 | version = "1.1.8" 1522 | source = "registry+https://github.com/rust-lang/crates.io-index" 1523 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 1524 | dependencies = [ 1525 | "cfg-if", 1526 | "once_cell", 1527 | ] 1528 | 1529 | [[package]] 1530 | name = "tinystr" 1531 | version = "0.7.6" 1532 | source = "registry+https://github.com/rust-lang/crates.io-index" 1533 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 1534 | dependencies = [ 1535 | "displaydoc", 1536 | "zerovec", 1537 | ] 1538 | 1539 | [[package]] 1540 | name = "tokio" 1541 | version = "1.43.0" 1542 | source = "registry+https://github.com/rust-lang/crates.io-index" 1543 | checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" 1544 | dependencies = [ 1545 | "backtrace", 1546 | "bytes", 1547 | "libc", 1548 | "mio", 1549 | "pin-project-lite", 1550 | "socket2", 1551 | "tokio-macros", 1552 | "windows-sys 0.52.0", 1553 | ] 1554 | 1555 | [[package]] 1556 | name = "tokio-macros" 1557 | version = "2.5.0" 1558 | source = "registry+https://github.com/rust-lang/crates.io-index" 1559 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 1560 | dependencies = [ 1561 | "proc-macro2", 1562 | "quote", 1563 | "syn", 1564 | ] 1565 | 1566 | [[package]] 1567 | name = "tokio-native-tls" 1568 | version = "0.3.1" 1569 | source = "registry+https://github.com/rust-lang/crates.io-index" 1570 | checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 1571 | dependencies = [ 1572 | "native-tls", 1573 | "tokio", 1574 | ] 1575 | 1576 | [[package]] 1577 | name = "tokio-rustls" 1578 | version = "0.26.1" 1579 | source = "registry+https://github.com/rust-lang/crates.io-index" 1580 | checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" 1581 | dependencies = [ 1582 | "rustls", 1583 | "tokio", 1584 | ] 1585 | 1586 | [[package]] 1587 | name = "tokio-util" 1588 | version = "0.7.13" 1589 | source = "registry+https://github.com/rust-lang/crates.io-index" 1590 | checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" 1591 | dependencies = [ 1592 | "bytes", 1593 | "futures-core", 1594 | "futures-sink", 1595 | "pin-project-lite", 1596 | "tokio", 1597 | ] 1598 | 1599 | [[package]] 1600 | name = "toml" 1601 | version = "0.8.19" 1602 | source = "registry+https://github.com/rust-lang/crates.io-index" 1603 | checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" 1604 | dependencies = [ 1605 | "serde", 1606 | "serde_spanned", 1607 | "toml_datetime", 1608 | "toml_edit", 1609 | ] 1610 | 1611 | [[package]] 1612 | name = "toml_datetime" 1613 | version = "0.6.8" 1614 | source = "registry+https://github.com/rust-lang/crates.io-index" 1615 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" 1616 | dependencies = [ 1617 | "serde", 1618 | ] 1619 | 1620 | [[package]] 1621 | name = "toml_edit" 1622 | version = "0.22.23" 1623 | source = "registry+https://github.com/rust-lang/crates.io-index" 1624 | checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" 1625 | dependencies = [ 1626 | "indexmap", 1627 | "serde", 1628 | "serde_spanned", 1629 | "toml_datetime", 1630 | ] 1631 | 1632 | [[package]] 1633 | name = "tower" 1634 | version = "0.5.2" 1635 | source = "registry+https://github.com/rust-lang/crates.io-index" 1636 | checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 1637 | dependencies = [ 1638 | "futures-core", 1639 | "futures-util", 1640 | "pin-project-lite", 1641 | "sync_wrapper", 1642 | "tokio", 1643 | "tower-layer", 1644 | "tower-service", 1645 | ] 1646 | 1647 | [[package]] 1648 | name = "tower-layer" 1649 | version = "0.3.3" 1650 | source = "registry+https://github.com/rust-lang/crates.io-index" 1651 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 1652 | 1653 | [[package]] 1654 | name = "tower-service" 1655 | version = "0.3.3" 1656 | source = "registry+https://github.com/rust-lang/crates.io-index" 1657 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1658 | 1659 | [[package]] 1660 | name = "tracing" 1661 | version = "0.1.41" 1662 | source = "registry+https://github.com/rust-lang/crates.io-index" 1663 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1664 | dependencies = [ 1665 | "pin-project-lite", 1666 | "tracing-attributes", 1667 | "tracing-core", 1668 | ] 1669 | 1670 | [[package]] 1671 | name = "tracing-attributes" 1672 | version = "0.1.28" 1673 | source = "registry+https://github.com/rust-lang/crates.io-index" 1674 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" 1675 | dependencies = [ 1676 | "proc-macro2", 1677 | "quote", 1678 | "syn", 1679 | ] 1680 | 1681 | [[package]] 1682 | name = "tracing-core" 1683 | version = "0.1.33" 1684 | source = "registry+https://github.com/rust-lang/crates.io-index" 1685 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 1686 | dependencies = [ 1687 | "once_cell", 1688 | "valuable", 1689 | ] 1690 | 1691 | [[package]] 1692 | name = "tracing-log" 1693 | version = "0.2.0" 1694 | source = "registry+https://github.com/rust-lang/crates.io-index" 1695 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 1696 | dependencies = [ 1697 | "log", 1698 | "once_cell", 1699 | "tracing-core", 1700 | ] 1701 | 1702 | [[package]] 1703 | name = "tracing-serde" 1704 | version = "0.2.0" 1705 | source = "registry+https://github.com/rust-lang/crates.io-index" 1706 | checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" 1707 | dependencies = [ 1708 | "serde", 1709 | "tracing-core", 1710 | "valuable", 1711 | "valuable-serde", 1712 | ] 1713 | 1714 | [[package]] 1715 | name = "tracing-subscriber" 1716 | version = "0.3.19" 1717 | source = "registry+https://github.com/rust-lang/crates.io-index" 1718 | checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" 1719 | dependencies = [ 1720 | "matchers", 1721 | "nu-ansi-term", 1722 | "once_cell", 1723 | "regex", 1724 | "serde", 1725 | "serde_json", 1726 | "sharded-slab", 1727 | "smallvec", 1728 | "thread_local", 1729 | "tracing", 1730 | "tracing-core", 1731 | "tracing-log", 1732 | "tracing-serde", 1733 | "valuable", 1734 | "valuable-serde", 1735 | ] 1736 | 1737 | [[package]] 1738 | name = "try-lock" 1739 | version = "0.2.5" 1740 | source = "registry+https://github.com/rust-lang/crates.io-index" 1741 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1742 | 1743 | [[package]] 1744 | name = "unicode-ident" 1745 | version = "1.0.16" 1746 | source = "registry+https://github.com/rust-lang/crates.io-index" 1747 | checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" 1748 | 1749 | [[package]] 1750 | name = "untrusted" 1751 | version = "0.9.0" 1752 | source = "registry+https://github.com/rust-lang/crates.io-index" 1753 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1754 | 1755 | [[package]] 1756 | name = "url" 1757 | version = "2.5.4" 1758 | source = "registry+https://github.com/rust-lang/crates.io-index" 1759 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 1760 | dependencies = [ 1761 | "form_urlencoded", 1762 | "idna", 1763 | "percent-encoding", 1764 | ] 1765 | 1766 | [[package]] 1767 | name = "utf16_iter" 1768 | version = "1.0.5" 1769 | source = "registry+https://github.com/rust-lang/crates.io-index" 1770 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" 1771 | 1772 | [[package]] 1773 | name = "utf8_iter" 1774 | version = "1.0.4" 1775 | source = "registry+https://github.com/rust-lang/crates.io-index" 1776 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1777 | 1778 | [[package]] 1779 | name = "utf8parse" 1780 | version = "0.2.2" 1781 | source = "registry+https://github.com/rust-lang/crates.io-index" 1782 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1783 | 1784 | [[package]] 1785 | name = "uuid" 1786 | version = "1.12.1" 1787 | source = "registry+https://github.com/rust-lang/crates.io-index" 1788 | checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" 1789 | dependencies = [ 1790 | "getrandom 0.2.15", 1791 | ] 1792 | 1793 | [[package]] 1794 | name = "valuable" 1795 | version = "0.1.1" 1796 | source = "registry+https://github.com/rust-lang/crates.io-index" 1797 | checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 1798 | dependencies = [ 1799 | "valuable-derive", 1800 | ] 1801 | 1802 | [[package]] 1803 | name = "valuable-derive" 1804 | version = "0.1.1" 1805 | source = "registry+https://github.com/rust-lang/crates.io-index" 1806 | checksum = "4e3a32a9bcc0f6c6ccfd5b27bcf298c58e753bcc9eeff268157a303393183a6d" 1807 | dependencies = [ 1808 | "proc-macro2", 1809 | "quote", 1810 | "syn", 1811 | ] 1812 | 1813 | [[package]] 1814 | name = "valuable-serde" 1815 | version = "0.1.1" 1816 | source = "registry+https://github.com/rust-lang/crates.io-index" 1817 | checksum = "2ee0548edecd1b907be7e67789923b7d02275b9ba4a33ebc33300e2c947a8cb1" 1818 | dependencies = [ 1819 | "serde", 1820 | "valuable", 1821 | ] 1822 | 1823 | [[package]] 1824 | name = "vcpkg" 1825 | version = "0.2.15" 1826 | source = "registry+https://github.com/rust-lang/crates.io-index" 1827 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1828 | 1829 | [[package]] 1830 | name = "want" 1831 | version = "0.3.1" 1832 | source = "registry+https://github.com/rust-lang/crates.io-index" 1833 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1834 | dependencies = [ 1835 | "try-lock", 1836 | ] 1837 | 1838 | [[package]] 1839 | name = "wasi" 1840 | version = "0.11.0+wasi-snapshot-preview1" 1841 | source = "registry+https://github.com/rust-lang/crates.io-index" 1842 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1843 | 1844 | [[package]] 1845 | name = "wasi" 1846 | version = "0.13.3+wasi-0.2.2" 1847 | source = "registry+https://github.com/rust-lang/crates.io-index" 1848 | checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" 1849 | dependencies = [ 1850 | "wit-bindgen-rt", 1851 | ] 1852 | 1853 | [[package]] 1854 | name = "wasm-bindgen" 1855 | version = "0.2.100" 1856 | source = "registry+https://github.com/rust-lang/crates.io-index" 1857 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 1858 | dependencies = [ 1859 | "cfg-if", 1860 | "once_cell", 1861 | "rustversion", 1862 | "wasm-bindgen-macro", 1863 | ] 1864 | 1865 | [[package]] 1866 | name = "wasm-bindgen-backend" 1867 | version = "0.2.100" 1868 | source = "registry+https://github.com/rust-lang/crates.io-index" 1869 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 1870 | dependencies = [ 1871 | "bumpalo", 1872 | "log", 1873 | "proc-macro2", 1874 | "quote", 1875 | "syn", 1876 | "wasm-bindgen-shared", 1877 | ] 1878 | 1879 | [[package]] 1880 | name = "wasm-bindgen-futures" 1881 | version = "0.4.50" 1882 | source = "registry+https://github.com/rust-lang/crates.io-index" 1883 | checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 1884 | dependencies = [ 1885 | "cfg-if", 1886 | "js-sys", 1887 | "once_cell", 1888 | "wasm-bindgen", 1889 | "web-sys", 1890 | ] 1891 | 1892 | [[package]] 1893 | name = "wasm-bindgen-macro" 1894 | version = "0.2.100" 1895 | source = "registry+https://github.com/rust-lang/crates.io-index" 1896 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 1897 | dependencies = [ 1898 | "quote", 1899 | "wasm-bindgen-macro-support", 1900 | ] 1901 | 1902 | [[package]] 1903 | name = "wasm-bindgen-macro-support" 1904 | version = "0.2.100" 1905 | source = "registry+https://github.com/rust-lang/crates.io-index" 1906 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 1907 | dependencies = [ 1908 | "proc-macro2", 1909 | "quote", 1910 | "syn", 1911 | "wasm-bindgen-backend", 1912 | "wasm-bindgen-shared", 1913 | ] 1914 | 1915 | [[package]] 1916 | name = "wasm-bindgen-shared" 1917 | version = "0.2.100" 1918 | source = "registry+https://github.com/rust-lang/crates.io-index" 1919 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 1920 | dependencies = [ 1921 | "unicode-ident", 1922 | ] 1923 | 1924 | [[package]] 1925 | name = "web-sys" 1926 | version = "0.3.77" 1927 | source = "registry+https://github.com/rust-lang/crates.io-index" 1928 | checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 1929 | dependencies = [ 1930 | "js-sys", 1931 | "wasm-bindgen", 1932 | ] 1933 | 1934 | [[package]] 1935 | name = "winapi" 1936 | version = "0.3.9" 1937 | source = "registry+https://github.com/rust-lang/crates.io-index" 1938 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1939 | dependencies = [ 1940 | "winapi-i686-pc-windows-gnu", 1941 | "winapi-x86_64-pc-windows-gnu", 1942 | ] 1943 | 1944 | [[package]] 1945 | name = "winapi-i686-pc-windows-gnu" 1946 | version = "0.4.0" 1947 | source = "registry+https://github.com/rust-lang/crates.io-index" 1948 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1949 | 1950 | [[package]] 1951 | name = "winapi-x86_64-pc-windows-gnu" 1952 | version = "0.4.0" 1953 | source = "registry+https://github.com/rust-lang/crates.io-index" 1954 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1955 | 1956 | [[package]] 1957 | name = "windows-core" 1958 | version = "0.52.0" 1959 | source = "registry+https://github.com/rust-lang/crates.io-index" 1960 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 1961 | dependencies = [ 1962 | "windows-targets", 1963 | ] 1964 | 1965 | [[package]] 1966 | name = "windows-registry" 1967 | version = "0.2.0" 1968 | source = "registry+https://github.com/rust-lang/crates.io-index" 1969 | checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" 1970 | dependencies = [ 1971 | "windows-result", 1972 | "windows-strings", 1973 | "windows-targets", 1974 | ] 1975 | 1976 | [[package]] 1977 | name = "windows-result" 1978 | version = "0.2.0" 1979 | source = "registry+https://github.com/rust-lang/crates.io-index" 1980 | checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" 1981 | dependencies = [ 1982 | "windows-targets", 1983 | ] 1984 | 1985 | [[package]] 1986 | name = "windows-strings" 1987 | version = "0.1.0" 1988 | source = "registry+https://github.com/rust-lang/crates.io-index" 1989 | checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" 1990 | dependencies = [ 1991 | "windows-result", 1992 | "windows-targets", 1993 | ] 1994 | 1995 | [[package]] 1996 | name = "windows-sys" 1997 | version = "0.52.0" 1998 | source = "registry+https://github.com/rust-lang/crates.io-index" 1999 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2000 | dependencies = [ 2001 | "windows-targets", 2002 | ] 2003 | 2004 | [[package]] 2005 | name = "windows-sys" 2006 | version = "0.59.0" 2007 | source = "registry+https://github.com/rust-lang/crates.io-index" 2008 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2009 | dependencies = [ 2010 | "windows-targets", 2011 | ] 2012 | 2013 | [[package]] 2014 | name = "windows-targets" 2015 | version = "0.52.6" 2016 | source = "registry+https://github.com/rust-lang/crates.io-index" 2017 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2018 | dependencies = [ 2019 | "windows_aarch64_gnullvm", 2020 | "windows_aarch64_msvc", 2021 | "windows_i686_gnu", 2022 | "windows_i686_gnullvm", 2023 | "windows_i686_msvc", 2024 | "windows_x86_64_gnu", 2025 | "windows_x86_64_gnullvm", 2026 | "windows_x86_64_msvc", 2027 | ] 2028 | 2029 | [[package]] 2030 | name = "windows_aarch64_gnullvm" 2031 | version = "0.52.6" 2032 | source = "registry+https://github.com/rust-lang/crates.io-index" 2033 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2034 | 2035 | [[package]] 2036 | name = "windows_aarch64_msvc" 2037 | version = "0.52.6" 2038 | source = "registry+https://github.com/rust-lang/crates.io-index" 2039 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2040 | 2041 | [[package]] 2042 | name = "windows_i686_gnu" 2043 | version = "0.52.6" 2044 | source = "registry+https://github.com/rust-lang/crates.io-index" 2045 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2046 | 2047 | [[package]] 2048 | name = "windows_i686_gnullvm" 2049 | version = "0.52.6" 2050 | source = "registry+https://github.com/rust-lang/crates.io-index" 2051 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2052 | 2053 | [[package]] 2054 | name = "windows_i686_msvc" 2055 | version = "0.52.6" 2056 | source = "registry+https://github.com/rust-lang/crates.io-index" 2057 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2058 | 2059 | [[package]] 2060 | name = "windows_x86_64_gnu" 2061 | version = "0.52.6" 2062 | source = "registry+https://github.com/rust-lang/crates.io-index" 2063 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2064 | 2065 | [[package]] 2066 | name = "windows_x86_64_gnullvm" 2067 | version = "0.52.6" 2068 | source = "registry+https://github.com/rust-lang/crates.io-index" 2069 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2070 | 2071 | [[package]] 2072 | name = "windows_x86_64_msvc" 2073 | version = "0.52.6" 2074 | source = "registry+https://github.com/rust-lang/crates.io-index" 2075 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2076 | 2077 | [[package]] 2078 | name = "wit-bindgen-rt" 2079 | version = "0.33.0" 2080 | source = "registry+https://github.com/rust-lang/crates.io-index" 2081 | checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" 2082 | dependencies = [ 2083 | "bitflags", 2084 | ] 2085 | 2086 | [[package]] 2087 | name = "write16" 2088 | version = "1.0.0" 2089 | source = "registry+https://github.com/rust-lang/crates.io-index" 2090 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" 2091 | 2092 | [[package]] 2093 | name = "writeable" 2094 | version = "0.5.5" 2095 | source = "registry+https://github.com/rust-lang/crates.io-index" 2096 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 2097 | 2098 | [[package]] 2099 | name = "xdg" 2100 | version = "2.5.2" 2101 | source = "registry+https://github.com/rust-lang/crates.io-index" 2102 | checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" 2103 | 2104 | [[package]] 2105 | name = "yoke" 2106 | version = "0.7.5" 2107 | source = "registry+https://github.com/rust-lang/crates.io-index" 2108 | checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" 2109 | dependencies = [ 2110 | "serde", 2111 | "stable_deref_trait", 2112 | "yoke-derive", 2113 | "zerofrom", 2114 | ] 2115 | 2116 | [[package]] 2117 | name = "yoke-derive" 2118 | version = "0.7.5" 2119 | source = "registry+https://github.com/rust-lang/crates.io-index" 2120 | checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" 2121 | dependencies = [ 2122 | "proc-macro2", 2123 | "quote", 2124 | "syn", 2125 | "synstructure", 2126 | ] 2127 | 2128 | [[package]] 2129 | name = "zerofrom" 2130 | version = "0.1.5" 2131 | source = "registry+https://github.com/rust-lang/crates.io-index" 2132 | checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" 2133 | dependencies = [ 2134 | "zerofrom-derive", 2135 | ] 2136 | 2137 | [[package]] 2138 | name = "zerofrom-derive" 2139 | version = "0.1.5" 2140 | source = "registry+https://github.com/rust-lang/crates.io-index" 2141 | checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" 2142 | dependencies = [ 2143 | "proc-macro2", 2144 | "quote", 2145 | "syn", 2146 | "synstructure", 2147 | ] 2148 | 2149 | [[package]] 2150 | name = "zeroize" 2151 | version = "1.8.1" 2152 | source = "registry+https://github.com/rust-lang/crates.io-index" 2153 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 2154 | 2155 | [[package]] 2156 | name = "zerovec" 2157 | version = "0.10.4" 2158 | source = "registry+https://github.com/rust-lang/crates.io-index" 2159 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 2160 | dependencies = [ 2161 | "yoke", 2162 | "zerofrom", 2163 | "zerovec-derive", 2164 | ] 2165 | 2166 | [[package]] 2167 | name = "zerovec-derive" 2168 | version = "0.10.3" 2169 | source = "registry+https://github.com/rust-lang/crates.io-index" 2170 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 2171 | dependencies = [ 2172 | "proc-macro2", 2173 | "quote", 2174 | "syn", 2175 | ] 2176 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | resolver = "2" 4 | members = [ 5 | "sectxtbin", 6 | "sectxtfuzz", 7 | "sectxtlib", 8 | ] 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License (ISC) 2 | 3 | Copyright 2020-2023 eikendev 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET_DIR := ./target 2 | FUZZ_DIR := ./fuzz 3 | 4 | .PHONY: build 5 | build: build_bin docs 6 | 7 | .PHONY: build_bin 8 | build_bin: 9 | cargo build -p sectxtlib 10 | cargo build -p sectxt 11 | 12 | .PHONY: docs 13 | docs: 14 | cargo doc -p sectxtlib 15 | 16 | .PHONY: test 17 | test: 18 | cargo fmt -p sectxtlib --check 19 | cargo fmt -p sectxt --check 20 | cargo clippy -p sectxtlib --all-features -- -D warnings 21 | cargo clippy -p sectxt --all-features -- -D warnings 22 | cargo test 23 | 24 | .PHONY: setup 25 | setup: 26 | rustup update 27 | rustup component add clippy 28 | rustup component add rustfmt 29 | rustup show 30 | cargo install cargo-afl 31 | 32 | .PHONY: publish 33 | publish: 34 | cargo publish -p sectxtlib 35 | cargo publish -p sectxt 36 | 37 | .PHONY: fuzz 38 | fuzz: 39 | cargo afl build -p sectxtfuzz 40 | AFL_SKIP_CPUFREQ=1 cargo afl fuzz -i $(FUZZ_DIR)/_examples -o $(FUZZ_DIR)/afl $(TARGET_DIR)/debug/sectxtfuzz 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sectxtbin/README.md -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | */ 2 | !_examples/ 3 | -------------------------------------------------------------------------------- /fuzz/_examples: -------------------------------------------------------------------------------- 1 | ../sectxtlib/resources/test/valid -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "stable" 3 | components = [ "clippy", "rustfmt" ] 4 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2021" 2 | max_width = 120 3 | -------------------------------------------------------------------------------- /scripts/checkalexa1m: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o errtrace 5 | 6 | SOURCEURL='http://s3.amazonaws.com/alexa-static/top-1m.csv.zip' 7 | 8 | cargo build --release -p sectxt 9 | 10 | tmp_directory="$(mktemp -d)" 11 | 12 | exit_handler() { 13 | rm -r "$tmp_directory" 14 | } 15 | 16 | trap exit_handler EXIT 17 | 18 | curl \ 19 | --disable \ 20 | --location \ 21 | --max-time 60 \ 22 | --output "$tmp_directory/top.csv.zip" \ 23 | "$SOURCEURL" 24 | 25 | unzip "$tmp_directory/top.csv.zip" -d "$tmp_directory" 26 | 27 | xsv select 2 "$tmp_directory/top-1m.csv" | sort -u > "$tmp_directory/top.txt" 28 | 29 | ./target/release/sectxt --threads 50 --timeout 5 --quiet < "$tmp_directory/top.txt" 30 | -------------------------------------------------------------------------------- /scripts/checktop500: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o errtrace 5 | 6 | SOURCEURL='https://moz.com/top-500/download/?table=top500Domains' 7 | 8 | cargo build --release -p sectxt 9 | 10 | tmp_directory="$(mktemp -d)" 11 | 12 | exit_handler() { 13 | rm -r "$tmp_directory" 14 | } 15 | 16 | trap exit_handler EXIT 17 | 18 | curl \ 19 | --disable \ 20 | --location \ 21 | --max-time 60 \ 22 | --output "$tmp_directory/top.csv" \ 23 | "$SOURCEURL" 24 | 25 | xsv select 'Root Domain' "$tmp_directory/top.csv" | tail -n +2 | sort -u > "$tmp_directory/top.txt" 26 | 27 | ./target/release/sectxt --threads 50 --timeout 5 --quiet < "$tmp_directory/top.txt" 28 | -------------------------------------------------------------------------------- /sectxtbin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sectxt" 3 | version = "0.3.1" 4 | authors = ["eikendev"] 5 | edition = "2021" 6 | description = "A tool for working with security.txt files as specified in RFC 9116" 7 | homepage = "https://github.com/eikendev/sectxt" 8 | repository = "https://github.com/eikendev/sectxt.git" 9 | readme = "README.md" 10 | license = "ISC" 11 | 12 | [dependencies] 13 | sectxtlib = { path = "../sectxtlib", version = "0.3.1" } 14 | anyhow = ">=1.0" 15 | argh = ">=0.1" 16 | futures = ">=0.3" 17 | human-panic = ">=1.1" 18 | lazy_static = ">=1.4" 19 | reqwest = ">=0.11" 20 | tracing = ">=0.1.37" 21 | tracing-subscriber = { version = ">=0.3.16", features = ["env-filter", "json", "valuable"] } 22 | url = ">=2.3.0" 23 | valuable = ">=0.1.0" 24 | 25 | [dependencies.tokio] 26 | version = "1" 27 | features = ["rt-multi-thread", "macros"] 28 | -------------------------------------------------------------------------------- /sectxtbin/README.md: -------------------------------------------------------------------------------- 1 |
2 |

sectxt

3 |

4 | The security.txt standard helps us make the Internet more secure. 5 |

6 |

sectxt lets you work with security.txt files on the command line.

7 |
8 | 9 |

10 | Build status  11 | License  12 | Version  13 | Downloads  14 |

15 | 16 | ## 🚀 Installation 17 | 18 | ```bash 19 | RUSTFLAGS="--cfg tracing_unstable" cargo install sectxt 20 | ``` 21 | 22 | Please refer to [issue #15](https://github.com/eikendev/sectxt/issues/15) for details. 23 | 24 | ## 📄 Usage 25 | 26 | Feed `sectxt` a list of domains and it tells you which of them implement [RFC 9116](https://www.rfc-editor.org/rfc/rfc9116) correctly. 27 | ```bash 28 | sectxt < domains.txt 29 | ``` 30 | 31 | ## 👮 Acknowledgments 32 | 33 | The idea was ~~shamelessly stolen from~~ inspired by [haksecuritytxt](https://github.com/hakluke/haksecuritytxt). 34 | The main motivation was to play around with [Rust](https://www.rust-lang.org/)'s new `async`/`await` syntax and learn something new. 35 | Besides, `sectxt` enforces stricter checks against the [RFC 9116](https://www.rfc-editor.org/rfc/rfc9116). 36 | -------------------------------------------------------------------------------- /sectxtbin/src/main.rs: -------------------------------------------------------------------------------- 1 | mod network; 2 | mod settings; 3 | mod status; 4 | mod website; 5 | 6 | use futures::channel::mpsc::channel; 7 | use futures::{Stream, StreamExt}; 8 | use lazy_static::*; 9 | use reqwest::Client; 10 | use sectxtlib::SecurityTxtOptions; 11 | use settings::Settings; 12 | use status::Status; 13 | use std::io::BufRead; 14 | use std::time::Duration; 15 | use tracing::{debug, info}; 16 | use tracing_subscriber::prelude::*; 17 | use tracing_subscriber::{fmt, EnvFilter}; 18 | use website::Website; 19 | 20 | fn stdin(threads: usize) -> impl Stream { 21 | let (mut tx, rx) = channel(threads); 22 | 23 | std::thread::spawn(move || { 24 | for line in std::io::stdin().lock().lines().map_while(Result::ok) { 25 | loop { 26 | let status = tx.try_send(line.to_owned()); 27 | 28 | match status { 29 | Err(e) if e.is_full() => continue, 30 | _ => break, 31 | } 32 | } 33 | } 34 | }); 35 | 36 | rx 37 | } 38 | 39 | async fn process_line(line: String, client: &Client, options: &SecurityTxtOptions, quiet: bool) -> Status { 40 | let mut line = line.trim().to_lowercase(); 41 | if !line.starts_with("http") { 42 | line = format!("https://{line}"); 43 | } 44 | let website = Website::try_from(&line[..]); 45 | 46 | match website { 47 | Ok(website) => website.get_status(client, options, quiet).await, 48 | Err(e) => { 49 | if !quiet { 50 | info!(domain = &line, error = e.to_string(), status = "ERR"); 51 | } 52 | 53 | Status { 54 | domain: line, 55 | available: false, 56 | } 57 | } 58 | } 59 | } 60 | 61 | #[tokio::main] 62 | async fn process_domains(s: &'static Settings) -> (u64, u64) { 63 | let client = reqwest::Client::builder() 64 | .timeout(Duration::from_secs(s.timeout)) 65 | .build() 66 | .unwrap(); 67 | 68 | let options: SecurityTxtOptions = SecurityTxtOptions::new(s.strict); 69 | 70 | let statuses = stdin(s.threads) 71 | .map(|input| { 72 | let client = &client; 73 | let options = &options; 74 | async move { process_line(input, client, options, s.quiet).await } 75 | }) 76 | .buffer_unordered(s.threads); 77 | 78 | let count: (u64, u64) = statuses 79 | .fold((0, 0), |acc, status: Status| async move { 80 | debug!(domain = &status.domain, available = status.available); 81 | match s { 82 | _ if status.available => (acc.0 + 1, acc.1 + 1), 83 | _ => (acc.0 + 1, acc.1), 84 | } 85 | }) 86 | .await; 87 | 88 | count 89 | } 90 | 91 | fn setup_logger() { 92 | let format_layer = fmt::layer() 93 | .with_level(true) 94 | .with_target(false) 95 | .with_thread_ids(false) 96 | .with_thread_names(false) 97 | .without_time() 98 | .json(); 99 | 100 | let filter_layer = EnvFilter::try_from_default_env() 101 | .or_else(|_| EnvFilter::try_new("info")) 102 | .unwrap(); 103 | 104 | tracing_subscriber::registry() 105 | .with(filter_layer) 106 | .with(format_layer) 107 | .init(); 108 | } 109 | 110 | fn main() { 111 | human_panic::setup_panic!(); 112 | 113 | lazy_static! { 114 | static ref SETTINGS: Settings = argh::from_env(); 115 | } 116 | 117 | setup_logger(); 118 | 119 | let count = process_domains(&SETTINGS); 120 | 121 | if SETTINGS.print_stats { 122 | println!("{}/{}", count.0, count.1); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /sectxtbin/src/network.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | use reqwest::Response; 3 | use sectxtlib::{SecurityTxt, SecurityTxtOptions}; 4 | 5 | pub fn is_file_present(result: Result) -> Result { 6 | let resp = result.context("HTTP request failed")?; 7 | 8 | if resp.status() != reqwest::StatusCode::OK { 9 | anyhow::bail!("HTTP status code not OK"); 10 | } 11 | 12 | Ok(resp) 13 | } 14 | 15 | pub async fn is_securitytxt(resp: Response, options: &SecurityTxtOptions) -> Result { 16 | if let Some(content_type) = resp.headers().get("Content-Type") { 17 | let value: &str = content_type.to_str().context("error parsing HTTP body")?; 18 | 19 | if value.starts_with("text/plain") && value.contains("charset=utf-8") { 20 | let s = resp.text().await.context("error parsing HTTP body")?; 21 | Ok(SecurityTxt::parse_with(&s, options)?) 22 | } else { 23 | anyhow::bail!("invalid HTTP content type"); 24 | } 25 | } else { 26 | anyhow::bail!("HTTP content type not specified"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sectxtbin/src/settings.rs: -------------------------------------------------------------------------------- 1 | use argh::FromArgs; 2 | 3 | #[derive(FromArgs)] 4 | /// A tool for working with security.txt files. 5 | pub struct Settings { 6 | /// number of simultaneous domains to process 7 | #[argh(option, default = "30")] 8 | pub threads: usize, 9 | 10 | /// seconds to wait before giving up a domain 11 | #[argh(option, default = "3")] 12 | pub timeout: u64, 13 | 14 | /// whether to be strict with line endings or more relaxed 15 | #[argh(switch)] 16 | pub strict: bool, 17 | 18 | /// only print domains for which the run was successful 19 | #[argh(switch, short = 'q')] 20 | pub quiet: bool, 21 | 22 | /// print statistics before exit 23 | #[argh(switch)] 24 | pub print_stats: bool, 25 | } 26 | -------------------------------------------------------------------------------- /sectxtbin/src/status.rs: -------------------------------------------------------------------------------- 1 | pub struct Status { 2 | pub domain: String, 3 | pub available: bool, 4 | } 5 | -------------------------------------------------------------------------------- /sectxtbin/src/website.rs: -------------------------------------------------------------------------------- 1 | use super::network::{is_file_present, is_securitytxt}; 2 | use super::status::Status; 3 | use anyhow::{Context, Result}; 4 | use sectxtlib::SecurityTxtOptions; 5 | use std::convert::TryFrom; 6 | use tracing::info; 7 | use url::Url; 8 | use valuable::Valuable; 9 | 10 | pub struct Website { 11 | pub domain: String, 12 | pub urls: Vec, 13 | } 14 | 15 | impl Website { 16 | fn make_status(&self, available: bool) -> Status { 17 | Status { 18 | domain: self.domain.to_owned(), 19 | available, 20 | } 21 | } 22 | 23 | pub async fn get_status(&self, client: &reqwest::Client, options: &SecurityTxtOptions, quiet: bool) -> Status { 24 | let mut first_error: Option = None; 25 | 26 | for url in &self.urls { 27 | let response = client.get(&url[..]).send().await; 28 | 29 | match is_file_present(response) { 30 | Ok(response) => match is_securitytxt(response, options).await { 31 | Ok(txt) => { 32 | // Location exists and file is parsable. 33 | info!(domain = self.domain, content = txt.as_value(), status = "OK"); 34 | return self.make_status(true); 35 | } 36 | Err(err) => { 37 | // Location exists but file is not parsable. 38 | if !quiet { 39 | info!(domain = self.domain, error = err.to_string(), status = "ERR"); 40 | } 41 | return self.make_status(false); 42 | } 43 | }, 44 | Err(err) => { 45 | // Location does not exists. 46 | if first_error.is_none() { 47 | first_error = Some(err); 48 | } 49 | } 50 | } 51 | } 52 | 53 | if !quiet { 54 | let err = first_error.unwrap(); // self.urls is never empty 55 | info!(domain = self.domain, error = err.to_string(), status = "ERR"); 56 | } 57 | 58 | self.make_status(false) 59 | } 60 | } 61 | 62 | impl TryFrom<&str> for Website { 63 | type Error = anyhow::Error; 64 | 65 | fn try_from(s: &str) -> Result { 66 | let url = Url::parse(s).context("unable to parse input as URL")?; 67 | let host = url.host_str().context("cannot parse hostname in input")?; 68 | 69 | Ok(Website { 70 | domain: host.to_owned(), 71 | urls: vec![ 72 | format!("https://{host}/.well-known/security.txt"), 73 | format!("https://{host}/security.txt"), 74 | ], 75 | }) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /sectxtfuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sectxtfuzz" 3 | version = "0.3.1" 4 | authors = ["eikendev"] 5 | edition = "2021" 6 | description = "A fuzzer for sectxtlib" 7 | homepage = "https://github.com/eikendev/sectxt" 8 | repository = "https://github.com/eikendev/sectxt.git" 9 | readme = "README.md" 10 | license = "ISC" 11 | 12 | [dependencies] 13 | afl = "*" 14 | sectxtlib = { path = "../sectxtlib", version = "0.3.1" } 15 | -------------------------------------------------------------------------------- /sectxtfuzz/src/main.rs: -------------------------------------------------------------------------------- 1 | use afl::*; 2 | use sectxtlib::SecurityTxt; 3 | 4 | fn main() { 5 | fuzz!(|data: &[u8]| { 6 | if let Ok(s) = std::str::from_utf8(data) { 7 | let _ = s.parse::(); 8 | } 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /sectxtlib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sectxtlib" 3 | version = "0.3.1" 4 | authors = ["eikendev"] 5 | edition = "2021" 6 | description = "A library for parsing and validating security.txt files as specified in RFC 9116" 7 | homepage = "https://github.com/eikendev/sectxt" 8 | repository = "https://github.com/eikendev/sectxt.git" 9 | readme = "README.md" 10 | license = "ISC" 11 | 12 | [dependencies] 13 | chrono = "0.4.10" 14 | iri-string = "0.7.0" 15 | nom = ">=5.1.2, <8" 16 | oxilangtag = "0.1.0" 17 | thiserror = "2.0" 18 | valuable = { version = "0.1.0", features = ["derive"] } 19 | -------------------------------------------------------------------------------- /sectxtlib/README.md: -------------------------------------------------------------------------------- 1 |
2 |

sectxtlib

3 |

4 | The security.txt standard helps us make the Internet more secure. 5 |

6 |

sectxtlib parses and validates security.txt files as specified in RFC 9116.

7 |
8 | 9 |

10 | Build status  11 | License  12 | Docs  13 | Version  14 | Downloads  15 |

16 | 17 | ## 📄 Usage 18 | 19 | Best have a look at [the documentation](https://docs.rs/sectxtlib/latest/sectxtlib/) for examples. 20 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/.gitattributes: -------------------------------------------------------------------------------- 1 | *.stxt binary 2 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0001.stxt: -------------------------------------------------------------------------------- 1 | cONTACt: https://www.rfc-editor.org/rfc/rfc3986 2 | ExpIReS: 2030-04-12T23:20:50.52Z 3 | 4 | prEFerrEd-LanguageS: fr ,fr 5 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0002.stxt: -------------------------------------------------------------------------------- 1 | CoNTaCT: https://www.rfc-editor.org/rfc/rfc3986 2 | EXPires: 2030-04-12T23:20:50.52Z 3 | ## 4 | 5 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0003.stxt: -------------------------------------------------------------------------------- 1 | ContacT: https://www.rfc-editor.org/rfc/rfc3986 2 | 3 | eXPIRes: 2030-04-12T23:20:50.52Z 4 | 5 | HIriNG: https://www.rfc-editor.org/rfc/rfc3986 6 | hIriNg: https://www.rfc-editor.org/rfc/rfc3986 7 | CanoNicAL: https://www.rfc-editor.org/rfc/rfc3986 8 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0004.stxt: -------------------------------------------------------------------------------- 1 | COntACT: https://www.rfc-editor.org/rfc/rfc3986 2 | EXPiREs: 2030-04-12T23:20:50.52Z 3 | 4 | encryPTiOn: https://www.rfc-editor.org/rfc/rfc3986 5 | policY: https://www.rfc-editor.org/rfc/rfc3986 6 | # 7 | 8 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0005.stxt: -------------------------------------------------------------------------------- 1 | cONtACT: https://www.rfc-editor.org/rfc/rfc3986 2 | eXpIRes: 2030-04-12T23:20:50.52Z 3 | 4 | HiRiNg: https://www.rfc-editor.org/rfc/rfc3986 5 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0006.stxt: -------------------------------------------------------------------------------- 1 | conTACT: https://www.rfc-editor.org/rfc/rfc3986 2 | exPIREs: 2030-04-12T23:20:50.52Z 3 | 4 | 5 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0007.stxt: -------------------------------------------------------------------------------- 1 | # 2 | #񪿢7 3 | enCrypTIoN: https://www.rfc-editor.org/rfc/rfc3986 4 | COnTAct: https://www.rfc-editor.org/rfc/rfc3986 5 | 6 | # 7 | #| 8 | #򤯂 9 | eXPIREs: 2030-04-12T23:20:50.52Z 10 | cONtAcT: https://www.rfc-editor.org/rfc/rfc3986 11 | !9: FVF f? 12 | # 13 | > 14 | ^ O>.|Qd 15 | # 16 | 17 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0008.stxt: -------------------------------------------------------------------------------- 1 | ConTaCt: https://www.rfc-editor.org/rfc/rfc3986 2 | eXpiReS: 2030-04-12T23:20:50.52Z 3 | 4 | pREferReD-LanGuAgES: fr 5 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0009.stxt: -------------------------------------------------------------------------------- 1 | cONTACt: https://www.rfc-editor.org/rfc/rfc3986 2 | HIring: https://www.rfc-editor.org/rfc/rfc3986 3 | eXPIReS: 2030-04-12T23:20:50.52Z 4 | 5: T 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0010.stxt: -------------------------------------------------------------------------------- 1 | cONTaCt: https://www.rfc-editor.org/rfc/rfc3986 2 | eXpIREs: 2030-04-12T23:20:50.52Z 3 | #R 4 | # 5 | 6 | enCryptIOn: https://www.rfc-editor.org/rfc/rfc3986 7 | hiRinG: https://www.rfc-editor.org/rfc/rfc3986 8 | 9 | 10 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0011.stxt: -------------------------------------------------------------------------------- 1 | 2 | 3 | contaCt: https://www.rfc-editor.org/rfc/rfc3986 4 | # 5 | EXPIRes: 2030-04-12T23:20:50.52Z 6 | prEFerrED-LAnguAGeS: fr ,fr, fr 7 | PolicY: https://www.rfc-editor.org/rfc/rfc3986 8 | # 񹔱񫒁X 9 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0012.stxt: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 4 | CONTACt: https://www.rfc-editor.org/rfc/rfc3986 5 | 6 | # 7 | ExpIrES: 2030-04-12T23:20:50.52Z 8 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0013.stxt: -------------------------------------------------------------------------------- 1 | coNtacT: https://www.rfc-editor.org/rfc/rfc3986 2 | eXPirEs: 2030-04-12T23:20:50.52Z 3 | #𐥘b񫌄 4 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0014.stxt: -------------------------------------------------------------------------------- 1 | COnTACt: https://www.rfc-editor.org/rfc/rfc3986 2 | # 3 | exPIrES: 2030-04-12T23:20:50.52Z 4 | pREFErrED-LanGuAgES: fr 5 | CAnONicAL: https://www.rfc-editor.org/rfc/rfc3986 6 | 7 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0015.stxt: -------------------------------------------------------------------------------- 1 | CoNtAct: https://www.rfc-editor.org/rfc/rfc3986 2 | AcKNOWLEdGMeNTS: https://www.rfc-editor.org/rfc/rfc3986 3 | expIRes: 2030-04-12T23:20:50.52Z 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0016.stxt: -------------------------------------------------------------------------------- 1 | 2 | cOnTAct: https://www.rfc-editor.org/rfc/rfc3986 3 | PoLicY: https://www.rfc-editor.org/rfc/rfc3986 4 | eXPIrES: 2030-04-12T23:20:50.52Z 5 | 6 | ConTACt: https://www.rfc-editor.org/rfc/rfc3986 7 | PrefERRED-lAnGuAges: fr,fr 8 | 9 | PoliCY: https://www.rfc-editor.org/rfc/rfc3986 10 | 11 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0017.stxt: -------------------------------------------------------------------------------- 1 | conTACt: https://www.rfc-editor.org/rfc/rfc3986 2 | conTACT: https://www.rfc-editor.org/rfc/rfc3986 3 | expiRES: 2030-04-12T23:20:50.52Z 4 | 5 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0018.stxt: -------------------------------------------------------------------------------- 1 | 2 | COntAct: https://www.rfc-editor.org/rfc/rfc3986 3 | 4 | ExpIrES: 2030-04-12T23:20:50.52Z 5 | POlIcy: https://www.rfc-editor.org/rfc/rfc3986 6 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0019.stxt: -------------------------------------------------------------------------------- 1 | cOntaCt: https://www.rfc-editor.org/rfc/rfc3986 2 | EnCrYpTIon: https://www.rfc-editor.org/rfc/rfc3986 3 | EXPiREs: 2030-04-12T23:20:50.52Z 4 | PREFERreD-lAnGuAGes: fr , fr 5 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0020.stxt: -------------------------------------------------------------------------------- 1 | CONTACt: https://www.rfc-editor.org/rfc/rfc3986 2 | (-4q: 3 | 4 | EXPIrEs: 2030-04-12T23:20:50.52Z 5 | 6 | 7 | 6m'5n: 8 | ACKNOwLEDGmENts: https://www.rfc-editor.org/rfc/rfc3986 9 | 10 | pOliCY: https://www.rfc-editor.org/rfc/rfc3986 11 | PREFeRRED-lAnguagEs: fr, fr 12 | 13 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0021.stxt: -------------------------------------------------------------------------------- 1 | # 2 | cOnTact: https://www.rfc-editor.org/rfc/rfc3986 3 | # 4 | eXPIrES: 2030-04-12T23:20:50.52Z 5 | PreFERred-laNgUAgEs: fr,fr 6 | #󿍧 7 | enCrYpTiON: https://www.rfc-editor.org/rfc/rfc3986 8 | 9 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0022.stxt: -------------------------------------------------------------------------------- 1 | coNTACT: https://www.rfc-editor.org/rfc/rfc3986 2 | eXpiREs: 2030-04-12T23:20:50.52Z 3 | POLicY: https://www.rfc-editor.org/rfc/rfc3986 4 | 5 | prEfERrED-lanGUAgEs: fr 6 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0023.stxt: -------------------------------------------------------------------------------- 1 | conTacT: https://www.rfc-editor.org/rfc/rfc3986 2 | eXPireS: 2030-04-12T23:20:50.52Z 3 | 4 | 5 | #\Aᷛ񝽗 6 | #򲐷 7 | coNtACT: https://www.rfc-editor.org/rfc/rfc3986 8 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0024.stxt: -------------------------------------------------------------------------------- 1 | 2 | CoNTACT: https://www.rfc-editor.org/rfc/rfc3986 3 | ExPireS: 2030-04-12T23:20:50.52Z 4 | preFerrEd-languagEs: fr 5 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0025.stxt: -------------------------------------------------------------------------------- 1 | COnTACt: https://www.rfc-editor.org/rfc/rfc3986 2 | ExpiREs: 2030-04-12T23:20:50.52Z 3 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0026.stxt: -------------------------------------------------------------------------------- 1 | 2 | CONTACT: https://www.rfc-editor.org/rfc/rfc3986 3 | cONtAct: https://www.rfc-editor.org/rfc/rfc3986 4 | eXpirEs: 2030-04-12T23:20:50.52Z 5 | 6 | pRefeRReD-lAngUAges: fr, fr, fr, fr ,fr 7 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0027.stxt: -------------------------------------------------------------------------------- 1 | conTacT: https://www.rfc-editor.org/rfc/rfc3986 2 | w.nK&: % | 3 | 6 4 | ExPIReS: 2030-04-12T23:20:50.52Z 5 | enCRYptIon: https://www.rfc-editor.org/rfc/rfc3986 6 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0028.stxt: -------------------------------------------------------------------------------- 1 | contACT: https://www.rfc-editor.org/rfc/rfc3986 2 | 3 | # 4 | # 5 | 6 | 7 | 8 | B-3.: 9 | ExPIreS: 2030-04-12T23:20:50.52Z 10 | PrEFeRRED-LanGuagES: fr 11 | 12 | 13 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0029.stxt: -------------------------------------------------------------------------------- 1 | COntAct: https://www.rfc-editor.org/rfc/rfc3986 2 | exPiReS: 2030-04-12T23:20:50.52Z 3 | 4 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0030.stxt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CoNTaCT: https://www.rfc-editor.org/rfc/rfc3986 6 | 7 | eXpIRes: 2030-04-12T23:20:50.52Z 8 | 9 | 10 | CONtaCT: https://www.rfc-editor.org/rfc/rfc3986 11 | ConTACT: https://www.rfc-editor.org/rfc/rfc3986 12 | caNONical: https://www.rfc-editor.org/rfc/rfc3986 13 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0031.stxt: -------------------------------------------------------------------------------- 1 | 2 | 3 | contAct: https://www.rfc-editor.org/rfc/rfc3986 4 | ExPireS: 2030-04-12T23:20:50.52Z 5 | eNCrYPtiOn: https://www.rfc-editor.org/rfc/rfc3986 6 | 7 | # 8 | ackNoWLeDgMenTS: https://www.rfc-editor.org/rfc/rfc3986 9 | pREFErrED-lAnGUageS: fr 10 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0032.stxt: -------------------------------------------------------------------------------- 1 | acKNoWLEDGmenTs: https://www.rfc-editor.org/rfc/rfc3986 2 | COnTAct: https://www.rfc-editor.org/rfc/rfc3986 3 | ExPiReS: 2030-04-12T23:20:50.52Z 4 | pRefErRed-languages: fr,fr , fr,fr, fr , fr 5 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0033.stxt: -------------------------------------------------------------------------------- 1 | 2 | coNtACT: https://www.rfc-editor.org/rfc/rfc3986 3 | exPiRes: 2030-04-12T23:20:50.52Z 4 | PreFeRREd-lAnguaGES: fr ,fr ,fr 5 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0034.stxt: -------------------------------------------------------------------------------- 1 | # 2 | cOnTaCT: https://www.rfc-editor.org/rfc/rfc3986 3 | # 4 | EXPirEs: 2030-04-12T23:20:50.52Z 5 | 6 | 7 | # 8 | 9 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0035.stxt: -------------------------------------------------------------------------------- 1 | CONTAcT: https://www.rfc-editor.org/rfc/rfc3986 2 | EXpiRes: 2030-04-12T23:20:50.52Z 3 | 4 | # 5 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0036.stxt: -------------------------------------------------------------------------------- 1 | contacT: https://www.rfc-editor.org/rfc/rfc3986 2 | #0p: QI 3 | EXpires: 2030-04-12T23:20:50.52Z 4 | pREFErRed-LAnguaGEs: fr 5 | # 6 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0037.stxt: -------------------------------------------------------------------------------- 1 | PolIcy: https://www.rfc-editor.org/rfc/rfc3986 2 | 3 | cONtAct: https://www.rfc-editor.org/rfc/rfc3986 4 | #O񏑾 x 5 | # 6 | # 7 | # i 8 | # 9 | # 򾊃 10 | # 11 | 12 | #򈶒󛜥 ' 13 | CANoNiCaL: https://www.rfc-editor.org/rfc/rfc3986 14 | EXpIres: 2030-04-12T23:20:50.52Z 15 | # 򿾵 16 | conTaCT: https://www.rfc-editor.org/rfc/rfc3986 17 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0038.stxt: -------------------------------------------------------------------------------- 1 | cOnTact: https://www.rfc-editor.org/rfc/rfc3986 2 | 3 | 4 | 5 | aCKNOWlEdgmenTS: https://www.rfc-editor.org/rfc/rfc3986 6 | expirES: 2030-04-12T23:20:50.52Z 7 | 8 | pREFErrED-lAnGuAgeS: fr 9 | eNcRyPTION: https://www.rfc-editor.org/rfc/rfc3986 10 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0039.stxt: -------------------------------------------------------------------------------- 1 | CONtAct: https://www.rfc-editor.org/rfc/rfc3986 2 | # 3 | # 4 | cOntaCt: https://www.rfc-editor.org/rfc/rfc3986 5 | ExPireS: 2030-04-12T23:20:50.52Z 6 | 7 | # T 󓙟 8 | 9 | preFerREd-laNgUagEs: fr,fr ,fr , fr 10 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0040.stxt: -------------------------------------------------------------------------------- 1 | CoNTAct: https://www.rfc-editor.org/rfc/rfc3986 2 | # 3 | # 4 | ExPIreS: 2030-04-12T23:20:50.52Z 5 | 6 | pOLicY: https://www.rfc-editor.org/rfc/rfc3986 7 | # 8 | prEfeRRed-LAngUAges: fr 9 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0041.stxt: -------------------------------------------------------------------------------- 1 | CONTaCT: https://www.rfc-editor.org/rfc/rfc3986 2 | 3 | EXpIrEs: 2030-04-12T23:20:50.52Z 4 | 5 | # 6 | 7 | #󬫠") 8 | r!M: 6 | 9 | 10 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0042.stxt: -------------------------------------------------------------------------------- 1 | coNtacT: https://www.rfc-editor.org/rfc/rfc3986 2 | 3 | eXpIReS: 2030-04-12T23:20:50.52Z 4 | cONTACt: https://www.rfc-editor.org/rfc/rfc3986 5 | pOlicy: https://www.rfc-editor.org/rfc/rfc3986 6 | pRefErReD-lAnGuaGEs: fr , fr 7 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0043.stxt: -------------------------------------------------------------------------------- 1 | CONtaCT: https://www.rfc-editor.org/rfc/rfc3986 2 | 3 | EXpIRes: 2030-04-12T23:20:50.52Z 4 | 5 | pREFeRred-LaNGUAgeS: fr,fr , fr 6 | 7 | 8 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0044.stxt: -------------------------------------------------------------------------------- 1 | 2 | # 3 | cONtACT: https://www.rfc-editor.org/rfc/rfc3986 4 | ExpiREs: 2030-04-12T23:20:50.52Z 5 | PREfErred-LaNguaGES: fr ,fr 6 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0045.stxt: -------------------------------------------------------------------------------- 1 | CONtact: https://www.rfc-editor.org/rfc/rfc3986 2 | ExPireS: 2030-04-12T23:20:50.52Z 3 | CANoniCAl: https://www.rfc-editor.org/rfc/rfc3986 4 | 5 | 6 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0046.stxt: -------------------------------------------------------------------------------- 1 | cONtact: https://www.rfc-editor.org/rfc/rfc3986 2 | 3 | expIrEs: 2030-04-12T23:20:50.52Z 4 | 5 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0047.stxt: -------------------------------------------------------------------------------- 1 | enCRYPtioN: https://www.rfc-editor.org/rfc/rfc3986 2 | 3 | # 4 | ConTaCT: https://www.rfc-editor.org/rfc/rfc3986 5 | eXpireS: 2030-04-12T23:20:50.52Z 6 | PRefeRRed-LaNGUAGeS: fr 7 | 8 | 9 | COntaCT: https://www.rfc-editor.org/rfc/rfc3986 10 | ENCRyptIOn: https://www.rfc-editor.org/rfc/rfc3986 11 | CoNTACT: https://www.rfc-editor.org/rfc/rfc3986 12 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0048.stxt: -------------------------------------------------------------------------------- 1 | cOnTACT: https://www.rfc-editor.org/rfc/rfc3986 2 | poLICy: https://www.rfc-editor.org/rfc/rfc3986 3 | # 4 | eXPires: 2030-04-12T23:20:50.52Z 5 | prEfERreD-LAnGUaGES: fr , fr, fr 6 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0049.stxt: -------------------------------------------------------------------------------- 1 | coNtAct: https://www.rfc-editor.org/rfc/rfc3986 2 | CoNtacT: https://www.rfc-editor.org/rfc/rfc3986 3 | EXpIRes: 2030-04-12T23:20:50.52Z 4 | #𪼿 5 | PrefERRed-LanGuAGeS: fr 6 | #/ 7 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0050.stxt: -------------------------------------------------------------------------------- 1 | 2 | COnTaCt: https://www.rfc-editor.org/rfc/rfc3986 3 | # s 4 | eXPIrEs: 2030-04-12T23:20:50.52Z 5 | 6 | caNONicAL: https://www.rfc-editor.org/rfc/rfc3986 7 | PRefeRred-LaNgUagES: fr 8 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0051.stxt: -------------------------------------------------------------------------------- 1 | ConTaCt: https://www.rfc-editor.org/rfc/rfc3986 2 | exPireS: 2030-04-12T23:20:50.52Z 3 | 4 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0052.stxt: -------------------------------------------------------------------------------- 1 | enCryptIoN: https://www.rfc-editor.org/rfc/rfc3986 2 | contACt: https://www.rfc-editor.org/rfc/rfc3986 3 | 4 | expIREs: 2030-04-12T23:20:50.52Z 5 | .*`: 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0053.stxt: -------------------------------------------------------------------------------- 1 | COntAct: https://www.rfc-editor.org/rfc/rfc3986 2 | ExpIREs: 2030-04-12T23:20:50.52Z 3 | pReferrED-lANgUageS: fr , fr 4 | #H V 5 | # 6 | 7 | eNCrYPtION: https://www.rfc-editor.org/rfc/rfc3986 8 | ACknowLEdgMents: https://www.rfc-editor.org/rfc/rfc3986 9 | 10 | 11 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0054.stxt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CONtacT: https://www.rfc-editor.org/rfc/rfc3986 5 | 6 | # 7 | 8 | 9 | EXpIrES: 2030-04-12T23:20:50.52Z 10 | canOnicAL: https://www.rfc-editor.org/rfc/rfc3986 11 | # 12 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0055.stxt: -------------------------------------------------------------------------------- 1 | .*!W: 2 | cONtACT: https://www.rfc-editor.org/rfc/rfc3986 3 | exPIreS: 2030-04-12T23:20:50.52Z 4 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0056.stxt: -------------------------------------------------------------------------------- 1 | CoNTACT: https://www.rfc-editor.org/rfc/rfc3986 2 | EXPiRES: 2030-04-12T23:20:50.52Z 3 | PReFERRED-lAnGUAGEs: fr 4 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0057.stxt: -------------------------------------------------------------------------------- 1 | COnTaCT: https://www.rfc-editor.org/rfc/rfc3986 2 | eXpireS: 2030-04-12T23:20:50.52Z 3 | 4 | 5 | AcKNoWLEDgmenTS: https://www.rfc-editor.org/rfc/rfc3986 6 | # 7 | ENCrYptIon: https://www.rfc-editor.org/rfc/rfc3986 8 | AcKNoWLedGMenTs: https://www.rfc-editor.org/rfc/rfc3986 9 | 10 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0058.stxt: -------------------------------------------------------------------------------- 1 | #c U 2 | # 3 | coNTact: https://www.rfc-editor.org/rfc/rfc3986 4 | cAnonicaL: https://www.rfc-editor.org/rfc/rfc3986 5 | 6 | expirEs: 2030-04-12T23:20:50.52Z 7 | prEfERRed-LaNguAges: fr,fr ,fr,fr 8 | 9 | COntAct: https://www.rfc-editor.org/rfc/rfc3986 10 | 11 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0059.stxt: -------------------------------------------------------------------------------- 1 | 2 | hiriNG: https://www.rfc-editor.org/rfc/rfc3986 3 | 4 | 5 | CoNTACT: https://www.rfc-editor.org/rfc/rfc3986 6 | 7 | ': 8 | F 9 | exPiReS: 2030-04-12T23:20:50.52Z 10 | PRefERreD-lANGuaGes: fr ,fr, fr 11 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0060.stxt: -------------------------------------------------------------------------------- 1 | # 2 | cONtAct: https://www.rfc-editor.org/rfc/rfc3986 3 | 4 | eXPIreS: 2030-04-12T23:20:50.52Z 5 | PrEFeRrEd-LANGuAgEs: fr 6 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0061.stxt: -------------------------------------------------------------------------------- 1 | cOnTACT: https://www.rfc-editor.org/rfc/rfc3986 2 | 3 | eXPIrEs: 2030-04-12T23:20:50.52Z 4 | # 5 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0062.stxt: -------------------------------------------------------------------------------- 1 | cOntaCt: https://www.rfc-editor.org/rfc/rfc3986 2 | exPiRes: 2030-04-12T23:20:50.52Z 3 | 4 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0063.stxt: -------------------------------------------------------------------------------- 1 | ConTaCT: https://www.rfc-editor.org/rfc/rfc3986 2 | ExPiREs: 2030-04-12T23:20:50.52Z 3 | #𩛞 򿷏󨸨4 4 | # 񝻘 5 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0064.stxt: -------------------------------------------------------------------------------- 1 | 2 | EnCRyPtION: https://www.rfc-editor.org/rfc/rfc3986 3 | 4 | #򚺼 5 | # 6 | # 7 | conTAct: https://www.rfc-editor.org/rfc/rfc3986 8 | ExPIreS: 2030-04-12T23:20:50.52Z 9 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0065.stxt: -------------------------------------------------------------------------------- 1 | COnTaCt: https://www.rfc-editor.org/rfc/rfc3986 2 | EncRYPtiON: https://www.rfc-editor.org/rfc/rfc3986 3 | contACT: https://www.rfc-editor.org/rfc/rfc3986 4 | exPiReS: 2030-04-12T23:20:50.52Z 5 | 6 | CANonicaL: https://www.rfc-editor.org/rfc/rfc3986 7 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0066.stxt: -------------------------------------------------------------------------------- 1 | # 2 | ConTaCt: https://www.rfc-editor.org/rfc/rfc3986 3 | eXPIreS: 2030-04-12T23:20:50.52Z 4 | encrYPTIOn: https://www.rfc-editor.org/rfc/rfc3986 5 | CAnoNICAL: https://www.rfc-editor.org/rfc/rfc3986 6 | HiRIng: https://www.rfc-editor.org/rfc/rfc3986 7 | # 8 | # 9 | 10 | 11 | 12 | PREfErRED-LaNguAgeS: fr,fr , fr 13 | 14 | policY: https://www.rfc-editor.org/rfc/rfc3986 15 | 16 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0067.stxt: -------------------------------------------------------------------------------- 1 | 2 | cONTaCt: https://www.rfc-editor.org/rfc/rfc3986 3 | eXPiRES: 2030-04-12T23:20:50.52Z 4 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0068.stxt: -------------------------------------------------------------------------------- 1 | #򮬚 2 | cOntaCt: https://www.rfc-editor.org/rfc/rfc3986 3 | eXpiRES: 2030-04-12T23:20:50.52Z 4 | # 5 | 6 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0069.stxt: -------------------------------------------------------------------------------- 1 | EnCRYpTIon: https://www.rfc-editor.org/rfc/rfc3986 2 | cONTaCT: https://www.rfc-editor.org/rfc/rfc3986 3 | ExPiRES: 2030-04-12T23:20:50.52Z 4 | ENcRYpTIoN: https://www.rfc-editor.org/rfc/rfc3986 5 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0070.stxt: -------------------------------------------------------------------------------- 1 | eNCRYptiOn: https://www.rfc-editor.org/rfc/rfc3986 2 | ContaCT: https://www.rfc-editor.org/rfc/rfc3986 3 | 4 | conTACt: https://www.rfc-editor.org/rfc/rfc3986 5 | #m 6 | ExpIReS: 2030-04-12T23:20:50.52Z 7 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0071.stxt: -------------------------------------------------------------------------------- 1 | ENCryptiON: https://www.rfc-editor.org/rfc/rfc3986 2 | coNtact: https://www.rfc-editor.org/rfc/rfc3986 3 | 4 | 5 | # 6 | 7 | EXpirES: 2030-04-12T23:20:50.52Z 8 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0072.stxt: -------------------------------------------------------------------------------- 1 | cONtaCT: https://www.rfc-editor.org/rfc/rfc3986 2 | aCkNowLEDgMentS: https://www.rfc-editor.org/rfc/rfc3986 3 | 4 | 4\u: 5 | exPiRES: 2030-04-12T23:20:50.52Z 6 | 7 | 8 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0073.stxt: -------------------------------------------------------------------------------- 1 | cONtACt: https://www.rfc-editor.org/rfc/rfc3986 2 | expIRes: 2030-04-12T23:20:50.52Z 3 | HirINg: https://www.rfc-editor.org/rfc/rfc3986 4 | caNOniCal: https://www.rfc-editor.org/rfc/rfc3986 5 | 6 | cONTacT: https://www.rfc-editor.org/rfc/rfc3986 7 | 8 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0074.stxt: -------------------------------------------------------------------------------- 1 | POLicY: https://www.rfc-editor.org/rfc/rfc3986 2 | CoNTact: https://www.rfc-editor.org/rfc/rfc3986 3 | caNOnIcal: https://www.rfc-editor.org/rfc/rfc3986 4 | 5 | exPiREs: 2030-04-12T23:20:50.52Z 6 | AcKnowLEDGmENts: https://www.rfc-editor.org/rfc/rfc3986 7 | 8 | 9 | pREFerREd-lAnGuagEs: fr 10 | 11 | 12 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/gen_unsigned/gen_unsigned_0075.stxt: -------------------------------------------------------------------------------- 1 | CoNTaCT: https://www.rfc-editor.org/rfc/rfc3986 2 | EXPIREs: 2030-04-12T23:20:50.52Z 3 | Hiring: https://www.rfc-editor.org/rfc/rfc3986 4 | # 5 | 6 | CaNONicAl: https://www.rfc-editor.org/rfc/rfc3986 7 | HIRInG: https://www.rfc-editor.org/rfc/rfc3986 8 | 21 | # Available at: 22 | Encryption: https://security.opera.com/gpg-key/ 23 | # Or via PGP keyservers: hkps://keyserver.ubuntu.com and hkps://pgp.mit.edu 24 | # Example: gpg --keyserver hkps://pgp.mit.edu --recv-keys 62cd131bb064942708acfe624a1bb804224de5a4 25 | Encryption: openpgp4fpr:62cd131bb064942708acfe624a1bb804224de5a4 26 | 27 | # Hall of Fame honouring resarchers that helped Opera improve security can be found at: 28 | Acknowledgments: https://security.opera.com/hall-of-fame/ 29 | # Up to date Opera Public Bug Bounty hall of fame can be found at: 30 | Acknowledgments: https://bugcrowd.com/opera/hall-of-fame 31 | 32 | # We can offer you a proper response in the following languages: 33 | Preferred-Languages: en, pl 34 | 35 | # If you would like to report a security issue please first read our responsible disclosure policy: 36 | Policy: https://security.opera.com/policy/ 37 | 38 | # Opera is hiring and looking for people like you! Current opening can be found at: 39 | Hiring: https://jobs.opera.com 40 | Hiring: https://linkedin.com/company/opera-software/jobs 41 | -----BEGIN PGP SIGNATURE----- 42 | 43 | iQIzBAEBCAAdFiEEYs0TG7BklCcIrP5iShu4BCJN5aQFAmQiw0QACgkQShu4BCJN 44 | 5aTXEg//RVPbpuXYp7tYLAhhlwqucq1+30DwLODRpXgxKrRUONbTbt4P6Mznsnrz 45 | W1GB0lM3oMaN85gq0PG1hwRIx6k5GoF1XpROg1D3F+B8H53XVPdqHX3HT/82C73o 46 | Q7zDahlmD183CifPIGtA1RIba/Z1SGel6o4m3JZjBRCeDDHJgV5E4mM5AtFtMWvC 47 | jCIPaZ9rmlWT/TY95y6Sq3J9KsnWzoRPbm724Sz6HeMa2z7KzU/9CWkixm1DIK8A 48 | yQyqGxB8JkLqQRumkoarQ/H2qoB03Pr4zIo8wLaErMxsU6kNqenH1WPZCsYjLKqD 49 | dwubk+fnPRPMAUEEpvFgpHgY8sBDQsXeffZBBw+7qMXj/tnznLezoPqFwLb3dgB8 50 | /Vk/pfEH1TKZhUIvu83wb8p4o3UG51ErRvhCTvL+Mn2FDb6bYKqMuO1uIXEcwkjn 51 | ZgbzAl7r3iIRByYcL2sC1cNJGUuzH6Y48u4EL0tl85KN0XChaq5hAZ5+GQBdrEE3 52 | kgHY90Vi5qcLVx9lw/5t6UElnzz9AagALAP66mgKOAb00ZfOuXSRS5lKcTCnghN0 53 | YpxoFO1LG9O2QaE2XhHo26VBARlXbVf1dEB2oSBzYHB7Fti9qpUn82zeidXYqhPJ 54 | hFT/zl5/X4kM7sQsJUncGXjO4w3CzsUwEgv7bnvWja4x6KANDso= 55 | =BfcH 56 | -----END PGP SIGNATURE----- 57 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/valid_signed/redhat.stxt: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP SIGNED MESSAGE----- 2 | Hash: SHA256 3 | 4 | # Report any suspected security vulnerability in Red Hat software to Red Hat Product Security at: 5 | Contact: https://access.redhat.com/security/team/contact/ 6 | Contact: mailto:secalert@redhat.com 7 | # Report an issue in any Red Hat branded website or online service to Red Hat Information Security 8 | # by following the instructions at: 9 | # https://www.redhat.com/en/trust/RFC-2350 10 | 11 | Preferred-Languages: en 12 | 13 | # Red Hat Product Security OpenPGP key fingerprint: 14 | # 77E7 9ABE 9367 3533 ED09 EBE2 DCE3 8235 97F5 EAC4 15 | Encryption: https://access.redhat.com/security/data/97f5eac4.txt 16 | 17 | # Vulnerability acknowledgments for Red Hat online services: 18 | Acknowledgments: https://access.redhat.com/articles/66234 19 | # Vulnerability acknowledgments for Red Hat offerings are listed on individual CVE pages at: 20 | # https://access.redhat.com/security/security-updates/#/cve 21 | 22 | CSAF: https://www.redhat.com/.well-known/csaf/provider-metadata.json 23 | 24 | # https://www.cve.org/PartnerInformation/ListofPartners/partner/redhat 25 | CNA: mailto:secalert@redhat.com 26 | CNA: mailto:RootCNA-Coordination@redhat.com 27 | 28 | Expires: 2025-03-15T00:00:00.000Z 29 | -----BEGIN PGP SIGNATURE----- 30 | Version: GnuPG v1 31 | 32 | iQIcBAEBCAAGBQJmC9AYAAoJENzjgjWX9erEAbcQAKHhwzlY+ZGre5Z+2CJkDlU8 33 | oYep+JN2C6T1heo6f79thsxXfu5SInLW3QQjiswAS7IzWzpoha/L41HzV2BoyraQ 34 | D++R+4wi7bMhNqhAotDQ58KoKqNPPsY3FM9pthcoGyZNvsODxlzbMwGgUKciyBde 35 | NPIa2z48auHI9raGyWFfT3EA5Vh6sCn2GrquCx+2EPndUR/4rsYak0kCWhXDKsoK 36 | xtpx5Dk7xTFUr7gl1g2wDUb/mx5xkT8OGHagbD1zfKfwdDukNV/8biy5gPdn+xzY 37 | oq1j7j/fcqJko6ALsMf7WzoPJfvC/xLn9czEh3EESLGcTUvA9UTt1Lutk4cMdOd7 38 | +VjdnyGqm7thBd1WqJ2Zx4+7JgQ+qqa1pgo70w63lfzkZCz5Tpin2Gy/+jZSIqPA 39 | 2SM0fw3jHbrAa8WHum+aYrgZVDl8JGLpYC6vZfVzysO6jJXT6zdFB/iYYVPDJn9G 40 | Z1zogkrlqLAkTCruoTMeUZkLd6BmMQ8mOy7rB+Fy0W1c55c4i7uR+SpIdLl5ylZl 41 | 8t9z0rHbq8oDaoxliN30Xi+kCio7vmwqx+fpJDATY8a8kGqcOpRBaFGGAeLWwcxZ 42 | XvHAhiINI9VINAddGRedtQXEXBAkuzPxL31ej7Xjp+HUaAVtxA53p62/CfqSSJp1 43 | FnQbWMEamIs/PDNIpTEv 44 | =K59c 45 | -----END PGP SIGNATURE----- 46 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/valid_signed/rfc_signed.stxt: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP SIGNED MESSAGE----- 2 | Hash: SHA256 3 | 4 | # Canonical URI 5 | Canonical: https://example.com/.well-known/security.txt 6 | 7 | # Our security address 8 | Contact: mailto:security@example.com 9 | 10 | # Our OpenPGP key 11 | Encryption: https://example.com/pgp-key.txt 12 | 13 | # Our security policy 14 | Policy: https://example.com/security-policy.html 15 | 16 | # Our security acknowledgments page 17 | Acknowledgments: https://example.com/hall-of-fame.html 18 | 19 | Expires: 2023-12-31T18:37:07z 20 | -----BEGIN PGP SIGNATURE----- 21 | Version: GnuPG v2.2 22 | 23 | iHUEARYKAB0WIQSsP2kEdoKDVFpSg6u3rK+YCkjapwUCY9qRaQAKCRC3rK+YCkja 24 | pwALAP9LEHSYMDW4h8QRHg4MwCzUdnbjBLIvpq4QTo3dIqCUPwEA31MsEf95OKCh 25 | MTHYHajOzjwpwlQVrjkK419igx4imgk= 26 | =KONn 27 | -----END PGP SIGNATURE----- 28 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/valid_signed/securitytxt_org.stxt: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP SIGNED MESSAGE----- 2 | Hash: SHA512 3 | 4 | Contact: https://hackerone.com/ed 5 | Expires: 2024-03-14T00:00:00.000Z 6 | Acknowledgments: https://hackerone.com/ed/thanks 7 | Preferred-Languages: en, fr, de 8 | Canonical: https://securitytxt.org/.well-known/security.txt 9 | Policy: https://hackerone.com/ed?type=team&view_policy=true 10 | -----BEGIN PGP SIGNATURE----- 11 | 12 | iHUEARYKAB0WIQSsP2kEdoKDVFpSg6u3rK+YCkjapwUCY9qRaQAKCRC3rK+YCkja 13 | pwALAP9LEHSYMDW4h8QRHg4MwCzUdnbjBLIvpq4QTo3dIqCUPwEA31MsEf95OKCh 14 | MTHYHajOzjwpwlQVrjkK419igx4imgk= 15 | =KONn 16 | -----END PGP SIGNATURE----- 17 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/valid_unsigned/github_com.stxt: -------------------------------------------------------------------------------- 1 | Contact: https://hackerone.com/github 2 | Acknowledgments: https://hackerone.com/github/hacktivity 3 | Preferred-Languages: en 4 | Canonical: https://github.com/.well-known/security.txt 5 | Policy: https://bounty.github.com 6 | Hiring: https://github.com/about/careers 7 | Expires: 2023-04-10T22:38:09z 8 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/valid_unsigned/rfc_unsigned.stxt: -------------------------------------------------------------------------------- 1 | # Our security address 2 | Contact: mailto:security@example.com 3 | 4 | # Our OpenPGP key 5 | Encryption: https://example.com/pgp-key.txt 6 | 7 | # Our security policy 8 | Policy: https://example.com/security-policy.html 9 | 10 | # Our security acknowledgments page 11 | Acknowledgments: https://example.com/hall-of-fame.html 12 | 13 | Expires: 2023-12-31T18:37:07z 14 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/valid_unsigned/wordpress_org.stxt: -------------------------------------------------------------------------------- 1 | Contact: https://hackerone.com/wordpress 2 | Expires: 2024-12-31T15:00:00.000Z 3 | Acknowledgments: https://hackerone.com/wordpress/thanks 4 | Canonical: https://wordpress.org/.well-known/security.txt 5 | Policy: https://make.wordpress.org/core/handbook/testing/reporting-security-vulnerabilities/ 6 | 7 | # The above contact is for reporting security issues in core WordPress software itself. 8 | # For reporting issues in a plugin hosted at wordpress.org, contact plugins@wordpress.org 9 | # If your website is hacked, please contact your site administrator or hosting provider. 10 | # Additionally, community support forums are a good resource at https://wordpress.org/support/ 11 | -------------------------------------------------------------------------------- /sectxtlib/resources/test/valid_unsigned/www_gov_uk.stxt: -------------------------------------------------------------------------------- 1 | Policy: https://www.gov.uk/help/report-vulnerability 2 | 3 | Contact: https://hackerone.com/44c348eb-e030-4273-b445-d4a2f6f83ba8/embedded_submissions/new 4 | Contact: https://www.gov.uk/contact/govuk 5 | 6 | Acknowledgments: https://vdp.cabinetoffice.gov.uk/thanks.txt 7 | 8 | Hiring: https://www.civilservicejobs.service.gov.uk/ 9 | 10 | Last-Updated: 2023-03-07 13:08:19+00:00 11 | Expires: 2023-06-07 13:08:19+00:00 12 | 13 | # Generated at: https://github.com/alphagov/security.txt 14 | -------------------------------------------------------------------------------- /sectxtlib/src/fields.rs: -------------------------------------------------------------------------------- 1 | use super::parse_error::ParseError; 2 | use chrono::{DateTime, Utc}; 3 | use iri_string::types::IriString; 4 | use oxilangtag::{LanguageTag, LanguageTagParseError}; 5 | use std::cmp::Ordering; 6 | use valuable::{Valuable, Value, Visit}; 7 | 8 | macro_rules! IriStringImpl { 9 | ($structname:ident) => { 10 | impl $structname { 11 | pub(crate) fn new(uri: &str) -> Result { 12 | let uri = uri.trim().parse::()?; 13 | 14 | if uri.scheme_str() == "http" { 15 | return Err(ParseError::InsecureHTTP); 16 | } 17 | 18 | let log_value = uri.as_str().to_string(); 19 | 20 | Ok(Self { uri, log_value }) 21 | } 22 | } 23 | 24 | impl Valuable for $structname { 25 | fn as_value(&self) -> Value<'_> { 26 | self.log_value.as_value() 27 | } 28 | 29 | fn visit(&self, _visit: &mut dyn Visit) {} 30 | } 31 | }; 32 | } 33 | 34 | /// An [Acknowledgments field](https://www.rfc-editor.org/rfc/rfc9116#name-acknowledgments) links to a page where security researchers are recognized 35 | #[derive(Debug, PartialEq)] 36 | pub struct AcknowledgmentsField { 37 | /// The URI of the link according to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) 38 | pub uri: IriString, 39 | 40 | log_value: String, 41 | } 42 | IriStringImpl!(AcknowledgmentsField); 43 | 44 | /// A [Canonical field](https://www.rfc-editor.org/rfc/rfc9116#name-canonical) contains a canonical URI for the security.txt file 45 | #[derive(Debug, PartialEq)] 46 | pub struct CanonicalField { 47 | /// The URI of the link according to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) 48 | pub uri: IriString, 49 | 50 | log_value: String, 51 | } 52 | IriStringImpl!(CanonicalField); 53 | 54 | /// A [Contact field](https://www.rfc-editor.org/rfc/rfc9116#name-contact) contains contact information to use for reporting vulnerabilities 55 | #[derive(Debug, PartialEq)] 56 | pub struct ContactField { 57 | /// The URI of the link according to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) 58 | pub uri: IriString, 59 | 60 | log_value: String, 61 | } 62 | IriStringImpl!(ContactField); 63 | 64 | /// An [Encryption field](https://www.rfc-editor.org/rfc/rfc9116#name-encryption) links to a key to be used for encrypted communication 65 | #[derive(Debug, PartialEq)] 66 | pub struct EncryptionField { 67 | /// The URI of the link according to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) 68 | pub uri: IriString, 69 | 70 | log_value: String, 71 | } 72 | IriStringImpl!(EncryptionField); 73 | 74 | /// The [Expires field](https://www.rfc-editor.org/rfc/rfc9116#name-expires) represents the date and time after which the security.txt file is considered stale 75 | #[derive(Debug, PartialEq)] 76 | pub struct ExpiresField { 77 | /// The date and time from which the security.txt file is considered stale 78 | pub datetime: DateTime, 79 | 80 | log_value: String, 81 | } 82 | 83 | impl ExpiresField { 84 | pub(crate) fn new(datetime: &str, now: DateTime) -> Result { 85 | let datetime: DateTime = datetime.trim().parse()?; 86 | 87 | if datetime < now { 88 | return Err(ParseError::ExpiresFieldExpired); 89 | } 90 | 91 | let log_value = datetime.to_rfc3339(); 92 | 93 | Ok(Self { datetime, log_value }) 94 | } 95 | } 96 | 97 | impl PartialOrd for ExpiresField { 98 | #[inline] 99 | fn partial_cmp(&self, other: &Self) -> Option { 100 | self.datetime.partial_cmp(&other.datetime) 101 | } 102 | } 103 | 104 | impl Valuable for ExpiresField { 105 | fn as_value(&self) -> Value<'_> { 106 | self.log_value.as_value() 107 | } 108 | 109 | fn visit(&self, _visit: &mut dyn Visit) {} 110 | } 111 | 112 | /// A [Hiring field](https://www.rfc-editor.org/rfc/rfc9116#name-hiring) links to the vendor's security-related job positions 113 | #[derive(Debug, PartialEq)] 114 | pub struct HiringField { 115 | /// The URI of the link according to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) 116 | pub uri: IriString, 117 | 118 | log_value: String, 119 | } 120 | IriStringImpl!(HiringField); 121 | 122 | /// A [Policy field](https://www.rfc-editor.org/rfc/rfc9116#name-policy) links to the security policy page 123 | #[derive(Debug, PartialEq)] 124 | pub struct PolicyField { 125 | /// The URI of the link according to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) 126 | pub uri: IriString, 127 | 128 | log_value: String, 129 | } 130 | IriStringImpl!(PolicyField); 131 | 132 | /// The [Preferred-Languages field](https://www.rfc-editor.org/rfc/rfc9116#name-preferred-languages) lists the preferred languages for security reports 133 | #[derive(Debug, PartialEq)] 134 | pub struct PreferredLanguagesField { 135 | /// The set of preferred languages according to [RFC 5646](https://www.rfc-editor.org/rfc/rfc5646) 136 | pub languages: Vec>, 137 | 138 | log_value: String, 139 | } 140 | 141 | impl PreferredLanguagesField { 142 | pub(crate) fn new(languages: &str) -> Result { 143 | let languages = languages 144 | .split(',') 145 | .map(str::trim) 146 | .map(LanguageTag::parse_and_normalize) 147 | .collect::>, LanguageTagParseError>>()?; 148 | 149 | if languages.is_empty() { 150 | return Err(ParseError::IllegalField); 151 | } 152 | 153 | let log_value = languages.join(", "); 154 | 155 | Ok(Self { languages, log_value }) 156 | } 157 | } 158 | 159 | impl Valuable for PreferredLanguagesField { 160 | fn as_value(&self) -> Value<'_> { 161 | self.log_value.as_value() 162 | } 163 | 164 | fn visit(&self, _visit: &mut dyn Visit) {} 165 | } 166 | 167 | /// The "Extension" field acts as a catch-all for any fields not explicitly supported by this library 168 | /// 169 | /// This feature accommodates [section 2.4 on Extensibility](https://www.rfc-editor.org/rfc/rfc9116#name-extensibility) in the specification. 170 | #[derive(Debug, PartialEq, Valuable)] 171 | pub struct ExtensionField { 172 | /// Name of the extension field 173 | pub name: String, 174 | /// Value of the extension field 175 | pub value: String, 176 | } 177 | 178 | impl ExtensionField { 179 | pub(crate) fn new(name: String, value: String) -> Result { 180 | Ok(Self { name, value }) 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /sectxtlib/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod fields; 2 | mod parse_error; 3 | mod parsers; 4 | mod pgpcleartextmessage; 5 | mod raw_field; 6 | mod securitytxt; 7 | mod securitytxt_options; 8 | 9 | pub use fields::{ 10 | AcknowledgmentsField, CanonicalField, ContactField, EncryptionField, ExpiresField, ExtensionField, HiringField, 11 | PolicyField, PreferredLanguagesField, 12 | }; 13 | pub use parse_error::ParseError; 14 | pub use securitytxt::SecurityTxt; 15 | pub use securitytxt_options::SecurityTxtOptions; 16 | 17 | #[cfg(test)] 18 | mod tests { 19 | use super::*; 20 | use chrono::{DateTime, Utc}; 21 | use std::{fs, path::PathBuf}; 22 | 23 | const URL: &str = "https://securitytxt.org/"; 24 | const INSECURE_URL: &str = "http://securitytxt.org/"; 25 | const EXPIRES: &str = "2030-01-01T08:19:03.000Z"; 26 | 27 | fn now_dt() -> DateTime { 28 | DateTime::parse_from_rfc3339("2023-01-01T08:19:03.000Z").unwrap().into() 29 | } 30 | 31 | fn expires_dt() -> ExpiresField { 32 | ExpiresField::new(EXPIRES, now_dt()).unwrap() 33 | } 34 | 35 | fn get_parse_options() -> SecurityTxtOptions { 36 | SecurityTxtOptions { 37 | now: now_dt(), 38 | strict: true, 39 | } 40 | } 41 | 42 | fn get_tests_dir(category: &str) -> PathBuf { 43 | let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 44 | d.push(format!("resources/test/{category}")); 45 | d 46 | } 47 | 48 | #[test] 49 | fn test_contact_and_expires() { 50 | let file = format!("Contact: {URL}\nExpires: {EXPIRES}\n"); 51 | let sec = SecurityTxt { 52 | acknowledgments: vec![], 53 | canonical: vec![], 54 | contact: vec![ContactField::new(URL).unwrap()], 55 | encryption: vec![], 56 | expires: expires_dt(), 57 | extension: vec![], 58 | hiring: vec![], 59 | policy: vec![], 60 | preferred_languages: None, 61 | }; 62 | 63 | assert_eq!(file.parse(), Ok(sec)); 64 | } 65 | 66 | #[test] 67 | fn test_comment() { 68 | let file = format!("# this is a comment\n#\nContact: {URL}\nExpires: {EXPIRES}\n#\n"); 69 | let sec = SecurityTxt { 70 | acknowledgments: vec![], 71 | canonical: vec![], 72 | contact: vec![ContactField::new(URL).unwrap()], 73 | encryption: vec![], 74 | expires: expires_dt(), 75 | extension: vec![], 76 | hiring: vec![], 77 | policy: vec![], 78 | preferred_languages: None, 79 | }; 80 | 81 | assert_eq!(file.parse(), Ok(sec)); 82 | } 83 | 84 | #[test] 85 | fn test_newlines() { 86 | let file = format!("\n\n\nContact: {URL}\n\n\nExpires: {EXPIRES}\n\n\n"); 87 | let sec = SecurityTxt { 88 | acknowledgments: vec![], 89 | canonical: vec![], 90 | contact: vec![ContactField::new(URL).unwrap()], 91 | encryption: vec![], 92 | expires: expires_dt(), 93 | extension: vec![], 94 | hiring: vec![], 95 | policy: vec![], 96 | preferred_languages: None, 97 | }; 98 | 99 | assert_eq!(file.parse(), Ok(sec)); 100 | } 101 | 102 | #[test] 103 | fn test_acknowledgements() { 104 | let file = format!("Contact: {URL}\nExpires: {EXPIRES}\nAcknowledgments: {URL}\n"); 105 | let sec = SecurityTxt { 106 | acknowledgments: vec![AcknowledgmentsField::new(URL).unwrap()], 107 | canonical: vec![], 108 | contact: vec![ContactField::new(URL).unwrap()], 109 | encryption: vec![], 110 | expires: expires_dt(), 111 | extension: vec![], 112 | hiring: vec![], 113 | policy: vec![], 114 | preferred_languages: None, 115 | }; 116 | 117 | assert_eq!(file.parse(), Ok(sec)); 118 | } 119 | 120 | #[test] 121 | fn test_contact_missing() { 122 | let file = format!("Expires: {EXPIRES}\n"); 123 | 124 | assert_eq!(file.parse::(), Err(ParseError::ContactFieldMissing)); 125 | } 126 | 127 | #[test] 128 | fn test_expires_missing() { 129 | let file = format!("Contact: {URL}\n"); 130 | 131 | assert_eq!(file.parse::(), Err(ParseError::ExpiresFieldMissing)); 132 | } 133 | 134 | #[test] 135 | fn test_trailing_content() { 136 | let file = format!("Contact: {URL}\nExpires: {EXPIRES}\nfoo"); 137 | 138 | assert_eq!(file.parse::(), Err(ParseError::Malformed)); 139 | } 140 | 141 | #[test] 142 | fn test_preferred_languages() { 143 | let file = format!("Contact: {URL}\nExpires: {EXPIRES}\nPreferred-Languages: en, fr\n"); 144 | let sec = SecurityTxt { 145 | acknowledgments: vec![], 146 | canonical: vec![], 147 | contact: vec![ContactField::new(URL).unwrap()], 148 | encryption: vec![], 149 | expires: expires_dt(), 150 | extension: vec![], 151 | hiring: vec![], 152 | policy: vec![], 153 | preferred_languages: Some(PreferredLanguagesField::new("en, fr").unwrap()), 154 | }; 155 | 156 | assert_eq!(file.parse::(), Ok(sec)); 157 | } 158 | 159 | #[test] 160 | fn test_preferred_languages_multiple() { 161 | let file = format!("Contact: {URL}\nExpires: {EXPIRES}\nPreferred-Languages: en\nPreferred-Languages: de\n"); 162 | 163 | assert_eq!( 164 | file.parse::(), 165 | Err(ParseError::PreferredLanguagesFieldMultiple) 166 | ); 167 | } 168 | 169 | #[test] 170 | fn test_expires_multiple() { 171 | let file = format!("Contact: {URL}\nExpires: {EXPIRES}\nExpires: {EXPIRES}\n"); 172 | 173 | assert_eq!(file.parse::(), Err(ParseError::ExpiresFieldMultiple)); 174 | } 175 | 176 | #[test] 177 | fn test_insecure_http() { 178 | let file = format!("Contact: {INSECURE_URL}\nExpires: {EXPIRES}\n"); 179 | 180 | assert_eq!(file.parse::(), Err(ParseError::InsecureHTTP)); 181 | } 182 | 183 | #[test] 184 | fn test_signed_contact() { 185 | let file = format!( 186 | "-----BEGIN PGP SIGNED MESSAGE-----\r 187 | Hash: SHA256\r 188 | \r 189 | Contact: {URL} 190 | Contact: {URL}\r 191 | Expires: {EXPIRES}\r 192 | -----BEGIN PGP SIGNATURE-----\r 193 | Version: GnuPG v2.2\r 194 | \r 195 | abcdefABCDEF/+==\r 196 | -----END PGP SIGNATURE-----\r 197 | " 198 | ); 199 | let sec = SecurityTxt { 200 | acknowledgments: vec![], 201 | canonical: vec![], 202 | contact: vec![ContactField::new(URL).unwrap(), ContactField::new(URL).unwrap()], 203 | encryption: vec![], 204 | expires: expires_dt(), 205 | extension: vec![], 206 | hiring: vec![], 207 | policy: vec![], 208 | preferred_languages: None, 209 | }; 210 | 211 | assert_eq!(file.parse(), Ok(sec)); 212 | } 213 | 214 | fn _test_category(category: &str) { 215 | let paths = get_tests_dir(category).read_dir().unwrap(); 216 | 217 | for path in paths { 218 | let buf = fs::read_to_string(path.unwrap().path()).unwrap(); 219 | let parse_options = get_parse_options(); 220 | let txt = SecurityTxt::parse_with(&buf, &parse_options); 221 | assert_eq!(txt.is_ok(), true); 222 | } 223 | } 224 | 225 | #[test] 226 | fn test_category_valid_unsigned() { 227 | _test_category("valid_unsigned") 228 | } 229 | 230 | #[test] 231 | fn test_category_valid_signed() { 232 | _test_category("valid_signed") 233 | } 234 | 235 | #[test] 236 | fn test_category_gen_unsigned() { 237 | _test_category("gen_unsigned") 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /sectxtlib/src/parse_error.rs: -------------------------------------------------------------------------------- 1 | use iri_string::validate; 2 | use oxilangtag::LanguageTagParseError; 3 | use thiserror::Error; 4 | 5 | #[derive(Error, Debug, PartialEq)] 6 | pub enum ParseError { 7 | #[error("invalid syntax")] 8 | Malformed, 9 | #[error("invalid date format")] 10 | InvalidDatetime(#[from] chrono::format::ParseError), 11 | #[error("field specified in an illegal way")] 12 | IllegalField, 13 | #[error("contact field must be specified")] 14 | ContactFieldMissing, 15 | #[error("expires field must be specified")] 16 | ExpiresFieldMissing, 17 | #[error("expires field may only be specified once")] 18 | ExpiresFieldExpired, 19 | #[error("expires field specifies time in the past")] 20 | ExpiresFieldMultiple, 21 | #[error("preferred languages field may only be specified once")] 22 | PreferredLanguagesFieldMultiple, 23 | #[error("links must use HTTPS")] 24 | InsecureHTTP, 25 | } 26 | 27 | macro_rules! impl_from { 28 | ( $for:path, $from:path, $to:path ) => { 29 | impl From<$from> for $for { 30 | fn from(_: $from) -> $for { 31 | $to 32 | } 33 | } 34 | }; 35 | } 36 | 37 | impl_from!(ParseError, validate::Error, ParseError::Malformed); 38 | impl_from!(ParseError, nom::Err>, ParseError::Malformed); 39 | impl_from!(ParseError, LanguageTagParseError, ParseError::Malformed); 40 | -------------------------------------------------------------------------------- /sectxtlib/src/parsers.rs: -------------------------------------------------------------------------------- 1 | use crate::{ParseError, SecurityTxtOptions}; 2 | 3 | use super::raw_field::RawField; 4 | 5 | use nom::{ 6 | branch::alt, 7 | bytes::complete::{take_while, take_while1}, 8 | character::complete::{char, crlf, satisfy}, 9 | combinator::{all_consuming, map, opt, recognize}, 10 | multi::{many0_count, many1}, 11 | sequence::{preceded, terminated, tuple}, 12 | IResult, 13 | }; 14 | 15 | pub(crate) struct SecurityTxtParser { 16 | _options: SecurityTxtOptions, 17 | } 18 | 19 | impl SecurityTxtParser { 20 | pub fn new(options: &SecurityTxtOptions) -> Self { 21 | Self { 22 | _options: options.clone(), 23 | } 24 | } 25 | 26 | pub fn parse<'a>(&'a self, text: &'a str) -> Result>>, ParseError> { 27 | let (_, msg) = self.body_parser(text)?; 28 | Ok(msg) 29 | } 30 | 31 | // body = signed / unsigned 32 | // signed is handled separately. 33 | fn body_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, Vec>>> { 34 | all_consuming(|x| self.unsigned_parser(x))(i) 35 | } 36 | 37 | // unsigned = *line (contact-field eol) ; one or more required 38 | // *line (expires-field eol) ; exactly one required 39 | // *line [lang-field eol] *line ; exactly one optional 40 | // ; order of fields within the file is not important 41 | // ; except that if contact-field appears more 42 | // ; than once, the order of those indicates 43 | // ; priority (see Section 3.5.3) 44 | fn unsigned_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, Vec>>> { 45 | many1(|x| self.line_parser(x))(i) 46 | } 47 | 48 | // line = [ (field / comment) ] eol 49 | fn line_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, Option>> { 50 | let field_parser_opt = map(|x| self.field_parser(x), Some); 51 | let comment_parser_opt = map(|x| self.comment_parser(x), |_| None); 52 | 53 | let (i, raw_field) = terminated(opt(alt((comment_parser_opt, field_parser_opt))), |x| self.eol_parser(x))(i)?; 54 | let flattened = raw_field.flatten(); 55 | Ok((i, flattened)) 56 | } 57 | 58 | // eol = *WSP [CR] LF 59 | fn eol_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, &'a str> { 60 | recognize(tuple((take_while(is_wsp), opt(|x| self.cr_parser(x)), |x| { 61 | self.lf_parser(x) 62 | })))(i) 63 | } 64 | 65 | // field = ; optional fields 66 | // ack-field / 67 | // can-field / 68 | // contact-field / ; optional repeated instances 69 | // encryption-field / 70 | // hiring-field / 71 | // policy-field / 72 | // ext-field 73 | fn field_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, RawField<'a>> { 74 | self.ext_name_parser(i) 75 | } 76 | 77 | // fs = ":" 78 | fn fs_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, char> { 79 | char(':')(i) 80 | } 81 | 82 | // comment = "#" *(WSP / VCHAR / %x80-FFFFF) 83 | fn comment_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, &'a str> { 84 | let matcher = |x| is_wsp(x) || is_vchar(x) || x >= '\u{80}'; 85 | preceded(char('#'), take_while(matcher))(i) 86 | } 87 | 88 | // ack-field = "Acknowledgments" fs SP uri 89 | // can-field = "Canonical" fs SP uri 90 | // contact-field = "Contact" fs SP uri 91 | // expires-field = "Expires" fs SP date-time 92 | // encryption-field = "Encryption" fs SP uri 93 | // hiring-field = "Hiring" fs SP uri 94 | // lang-field = "Preferred-Languages" fs SP lang-values 95 | // policy-field = "Policy" fs SP uri 96 | // date-time = < imported from Section 5.6 of [RFC3339] > 97 | // lang-tag = < Language-Tag from Section 2.1 of [RFC5646] > 98 | // lang-values = lang-tag *(*WSP "," *WSP lang-tag) 99 | // uri = < URI as per Section 3 of [RFC3986] > 100 | 101 | // ext-field = field-name fs SP unstructured 102 | fn ext_name_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, RawField<'a>> { 103 | let (i, (name, _, _, value)) = tuple(( 104 | |x| self.field_name_parser(x), 105 | |x| self.fs_parser(x), 106 | |x| self.sp_parser(x), 107 | |x| self.unstructured_parser(x), 108 | ))(i)?; 109 | Ok((i, RawField { name, value })) 110 | } 111 | 112 | // field-name = < imported from Section 3.6.8 of [RFC5322] > 113 | // field-name = 1*ftext 114 | fn field_name_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, &'a str> { 115 | take_while1(is_ftext_char)(i) 116 | } 117 | 118 | // < imported from [RFC5322] > 119 | // unstructured = *([FWS] VCHAR) *WSP 120 | // Ommitted obsolete part. 121 | fn unstructured_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, &'a str> { 122 | recognize(terminated( 123 | recognize(many0_count(preceded(opt(|x| self.fws_parser(x)), satisfy(is_vchar)))), 124 | take_while(is_wsp), 125 | ))(i) 126 | } 127 | 128 | // < imported from [RFC5322] > 129 | // FWS = [*WSP CRLF] 1*WSP 130 | // Ommitted obsolete part. 131 | fn fws_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, &'a str> { 132 | recognize(preceded( 133 | opt(tuple((take_while(is_wsp), |x| self.crlf_parser(x)))), 134 | take_while1(is_wsp), 135 | ))(i) 136 | } 137 | 138 | fn cr_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, char> { 139 | satisfy(is_cr)(i) 140 | } 141 | 142 | // CRLF = CR LF 143 | // ; Internet standard newline 144 | fn crlf_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, &'a str> { 145 | crlf(i) 146 | } 147 | 148 | fn lf_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, char> { 149 | satisfy(is_lf)(i) 150 | } 151 | 152 | // SP = %x20 153 | fn sp_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, char> { 154 | char(' ')(i) 155 | } 156 | } 157 | 158 | // field-name = < imported from Section 3.6.8 of [RFC5322] > 159 | // ftext = %d33-57 / ; Printable US-ASCII 160 | // %d59-126 ; characters not including 161 | // ; ":". 162 | fn is_ftext_char(i: char) -> bool { 163 | match i { 164 | '\x21'..='\x39' => true, // %d33-57 165 | '\x3B'..='\x7E' => true, // %d59-126 166 | _ => false, 167 | } 168 | } 169 | 170 | // CR = %x0D 171 | // ; carriage return 172 | fn is_cr(i: char) -> bool { 173 | i == '\r' 174 | } 175 | 176 | // LF = %x0A 177 | // ; linefeed 178 | fn is_lf(i: char) -> bool { 179 | i == '\n' 180 | } 181 | 182 | // VCHAR = %x21-7E 183 | // ; visible (printing) characters 184 | fn is_vchar(i: char) -> bool { 185 | matches!(i, '\x21'..='\x7E') 186 | } 187 | 188 | // WSP = SP / HTAB 189 | // ; white space 190 | fn is_wsp(i: char) -> bool { 191 | i == ' ' || i == '\t' 192 | } 193 | 194 | #[cfg(test)] 195 | mod tests { 196 | use super::*; 197 | use std::{fs, path::PathBuf}; 198 | 199 | fn get_tests_dir(category: &str) -> PathBuf { 200 | let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 201 | d.push(format!("resources/test/{category}")); 202 | d 203 | } 204 | 205 | fn run_tests_from_dir(dir: &str) { 206 | let unsigned_parser = SecurityTxtParser::new(&Default::default()); 207 | let paths = get_tests_dir(dir).read_dir().unwrap(); 208 | 209 | for path in paths { 210 | let file = path.unwrap().path(); 211 | println!("Input file: {:?}", file); 212 | let buf = fs::read_to_string(file).unwrap(); 213 | let txt = unsigned_parser.parse(&buf); 214 | assert!(txt.is_ok()); 215 | } 216 | } 217 | 218 | #[test] 219 | fn test_category_gen_unsigned() { 220 | run_tests_from_dir("gen_unsigned") 221 | } 222 | 223 | #[test] 224 | fn test_line_parser() { 225 | let unsigned_parser = SecurityTxtParser::new(&Default::default()); 226 | let test_vector = vec![ 227 | ("\n", None), 228 | ("\t \r\n", None), 229 | ("# This is a comment.\n", None), 230 | ( 231 | "foo: bar\r\n", 232 | Some(RawField { 233 | name: "foo", 234 | value: "bar", 235 | }), 236 | ), 237 | ]; 238 | 239 | for (input, result) in test_vector { 240 | assert_eq!(unsigned_parser.line_parser(input), Ok(("", result))); 241 | } 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /sectxtlib/src/pgpcleartextmessage.rs: -------------------------------------------------------------------------------- 1 | use crate::SecurityTxtOptions; 2 | 3 | use super::parse_error::ParseError; 4 | 5 | use nom::{ 6 | branch::alt, 7 | bytes::complete::{tag, take_while, take_while1}, 8 | character::complete::{line_ending, none_of, one_of}, 9 | combinator::{all_consuming, opt, peek, recognize}, 10 | multi::{many0, many1, many1_count, separated_list1}, 11 | sequence::{delimited, preceded, separated_pair, terminated, tuple}, 12 | IResult, 13 | }; 14 | 15 | #[derive(Debug, PartialEq)] 16 | pub(crate) struct PGPSignature<'a> { 17 | pub signature: &'a str, 18 | pub keys: Vec<(&'a str, &'a str)>, 19 | } 20 | 21 | #[derive(Debug, PartialEq)] 22 | pub(crate) struct PGPCleartextMessage<'a> { 23 | pub hash_armor_headers: Vec>, 24 | pub cleartext: String, 25 | pub signature: PGPSignature<'a>, 26 | } 27 | 28 | impl PGPCleartextMessage<'_> {} 29 | 30 | pub(crate) struct PGPCleartextMessageParser { 31 | options: SecurityTxtOptions, 32 | } 33 | 34 | impl PGPCleartextMessageParser { 35 | pub fn new(options: &SecurityTxtOptions) -> Self { 36 | Self { 37 | options: options.clone(), 38 | } 39 | } 40 | 41 | pub fn parse<'a>(&'a self, text: &'a str) -> Result, ParseError> { 42 | let (_, msg) = self.signed_parser(text)?; 43 | Ok(msg) 44 | } 45 | 46 | fn lf_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, &'a str> { 47 | match self.options.strict { 48 | true => line_ending(i), 49 | false => tag("\n")(i), 50 | } 51 | } 52 | 53 | // signed = cleartext-header 54 | // 1*(hash-header) 55 | // CRLF 56 | // cleartext 57 | // signature 58 | fn signed_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, PGPCleartextMessage<'a>> { 59 | let (_, (_, hash_armor_headers, _, cleartext, signature)) = all_consuming(tuple(( 60 | |x| self.cleartext_header_parser(x), 61 | many1(|x| self.hash_header_parser(x)), 62 | |x| self.lf_parser(x), 63 | |x| self.cleartext_parser(x), 64 | |x| self.signature_parser(x), 65 | )))(i)?; 66 | 67 | Ok(( 68 | i, 69 | PGPCleartextMessage { 70 | hash_armor_headers, 71 | cleartext, 72 | signature, 73 | }, 74 | )) 75 | } 76 | 77 | // cleartext-header = %s"-----BEGIN PGP SIGNED MESSAGE-----" CRLF 78 | fn cleartext_header_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, &'a str> { 79 | terminated(tag("-----BEGIN PGP SIGNED MESSAGE-----"), |x| self.lf_parser(x))(i) 80 | } 81 | 82 | // hash-header = %s"Hash: " hash-alg *("," hash-alg) CRLF 83 | fn hash_header_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, Vec<&'a str>> { 84 | delimited( 85 | tag("Hash: "), 86 | separated_list1(tag(","), |x| self.hash_alg_parser(x)), 87 | |x| self.lf_parser(x), 88 | )(i) 89 | } 90 | 91 | // hash-alg = token 92 | // ; imported from RFC 2045; see RFC 4880 Section 93 | // ; 10.3.3 for a pointer to the registry of 94 | // ; valid values 95 | fn hash_alg_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, &'a str> { 96 | self.token_parser(i) 97 | } 98 | 99 | // < Section 5.1 of [RFC2045] > 100 | // token := 1* 102 | fn token_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, &'a str> { 103 | take_while1(is_token_char)(i) 104 | } 105 | 106 | // cleartext = *((line-dash / line-from / line-nodash) [CR] LF) 107 | // EOL is handled in branches. 108 | fn cleartext_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, String> { 109 | let (i, lines) = many0(alt((|x| self.line_dash_parser(x), |x| self.line_nodash_parser(x))))(i)?; 110 | Ok((i, lines.join(""))) 111 | } 112 | 113 | // line-dash = ("- ") "-" *UTF8-char-not-cr 114 | // ; MUST include initial "- " 115 | fn line_dash_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, &'a str> { 116 | preceded( 117 | tag("- "), 118 | recognize(tuple(( 119 | one_of("-"), 120 | take_while(|x| x != '\r' && x != '\n'), 121 | line_ending, 122 | ))), 123 | )(i) 124 | } 125 | 126 | // line-nodash = ["- "] *UTF8-char-not-cr 127 | // ; MAY include initial "- " 128 | fn line_nodash_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, &'a str> { 129 | preceded( 130 | opt(tag("- ")), 131 | recognize(tuple(( 132 | peek(none_of("-")), 133 | take_while(|x| x != '\r' && x != '\n'), 134 | line_ending, 135 | ))), 136 | )(i) 137 | } 138 | 139 | // signature = armor-header 140 | // armor-keys 141 | // CRLF 142 | // signature-data 143 | // armor-tail 144 | fn signature_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, PGPSignature<'a>> { 145 | let (i, (_, keys, _, signature, _)) = tuple(( 146 | |x| self.armor_header_parser(x), 147 | |x| self.armor_keys_parser(x), 148 | |x| self.lf_parser(x), 149 | |x| self.signature_data_parser(x), 150 | |x| self.armor_tail_parser(x), 151 | ))(i)?; 152 | 153 | Ok((i, PGPSignature { signature, keys })) 154 | } 155 | 156 | // armor-header = %s"-----BEGIN PGP SIGNATURE-----" CRLF 157 | fn armor_header_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, &'a str> { 158 | terminated(tag("-----BEGIN PGP SIGNATURE-----"), |x| self.lf_parser(x))(i) 159 | } 160 | 161 | // armor-keys = *(token ": " *( VCHAR / WSP ) CRLF) 162 | // ; Armor Header Keys from RFC 4880 163 | fn armor_keys_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, Vec<(&'a str, &'a str)>> { 164 | many0(terminated( 165 | separated_pair( 166 | |x| self.token_parser(x), 167 | tag(": "), 168 | take_while(|x| is_vchar(x) || is_wsp(x)), 169 | ), 170 | |x| self.lf_parser(x), 171 | ))(i) 172 | } 173 | 174 | // armor-tail = %s"-----END PGP SIGNATURE-----" CRLF 175 | fn armor_tail_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, &'a str> { 176 | terminated(tag("-----END PGP SIGNATURE-----"), |x| self.lf_parser(x))(i) 177 | } 178 | 179 | // signature-data = 1*(1*(ALPHA / DIGIT / "=" / "+" / "/") CRLF) 180 | // ; base64; see RFC 4648 181 | // ; includes RFC 4880 checksum 182 | fn signature_data_parser<'a>(&'a self, i: &'a str) -> IResult<&'a str, &'a str> { 183 | recognize(many1_count(terminated(take_while1(is_signature_data_char), |x| { 184 | self.lf_parser(x) 185 | })))(i) 186 | } 187 | } 188 | 189 | // < Section 5.1 of [RFC2045] > 190 | // tspecials := "(" / ")" / "<" / ">" / "@" / 191 | // "," / ";" / ":" / "\" / <"> 192 | // "/" / "[" / "]" / "?" / "=" 193 | // ; Must be in quoted-string, 194 | // ; to use within parameter values 195 | fn is_token_char(i: char) -> bool { 196 | let tspecials = "()<>@,;:\\\"/[]?="; 197 | i != ' ' && !i.is_ascii_control() && tspecials.find(i).is_none() 198 | } 199 | 200 | fn is_signature_data_char(i: char) -> bool { 201 | matches!(i, 'a'..='z' | 'A'..='Z' | '0'..='9' | '=' | '+' | '/') 202 | } 203 | 204 | // VCHAR = %x21-7E 205 | // ; visible (printing) characters 206 | fn is_vchar(i: char) -> bool { 207 | matches!(i, '\x21'..='\x7E') 208 | } 209 | 210 | // WSP = SP / HTAB 211 | // ; white space 212 | fn is_wsp(i: char) -> bool { 213 | i == ' ' || i == '\t' 214 | } 215 | 216 | #[cfg(test)] 217 | mod tests { 218 | use super::*; 219 | 220 | const SIGNATURE_DATA: &str = "iHUEARYKAB0WIQSsP2kEdoKDVFpSg6u3rK+YCkjapwUCY9qRaQAKCRC3rK+YCkja\r 221 | pwALAP9LEHSYMDW4h8QRHg4MwCzUdnbjBLIvpq4QTo3dIqCUPwEA31MsEf95OKCh\r 222 | MTHYHajOzjwpwlQVrjkK419igx4imgk=\r 223 | =KONn\r 224 | "; 225 | 226 | #[test] 227 | fn test_parse() { 228 | let signed_parser = PGPCleartextMessageParser::new(&Default::default()); 229 | let txt = format!( 230 | "-----BEGIN PGP SIGNED MESSAGE-----\r 231 | Hash: SHA512\r 232 | \r 233 | Test\r 234 | - Test\r 235 | -----BEGIN PGP SIGNATURE-----\r 236 | \r 237 | {SIGNATURE_DATA}-----END PGP SIGNATURE-----\r 238 | " 239 | ); 240 | let msg = PGPCleartextMessage { 241 | hash_armor_headers: vec![vec!["SHA512"]], 242 | cleartext: "Test\r\nTest\r\n".into(), 243 | signature: PGPSignature { 244 | signature: SIGNATURE_DATA, 245 | keys: vec![], 246 | }, 247 | }; 248 | assert_eq!(signed_parser.parse(&txt), Ok(msg)); 249 | } 250 | 251 | #[test] 252 | fn test_hash_header_parser() { 253 | let signed_parser = PGPCleartextMessageParser::new(&Default::default()); 254 | let test_vector = vec![ 255 | ("Hash: SHA512\r\n", vec!["SHA512"]), 256 | ("Hash: SHA256,SHA512\r\n", vec!["SHA256", "SHA512"]), 257 | ]; 258 | 259 | for (input, result) in test_vector { 260 | assert_eq!(signed_parser.hash_header_parser(input), Ok(("", result))); 261 | } 262 | } 263 | 264 | #[test] 265 | fn test_token_parser() { 266 | let signed_parser = PGPCleartextMessageParser::new(&Default::default()); 267 | let test_vector = vec![("SHA512\r\n", "SHA512", "\r\n")]; 268 | 269 | for (input, result, leftover) in test_vector { 270 | assert_eq!(signed_parser.token_parser(input), Ok((leftover, result))); 271 | } 272 | } 273 | 274 | #[test] 275 | fn test_line_dash_parser() { 276 | let signed_parser = PGPCleartextMessageParser::new(&Default::default()); 277 | let test_vector = vec![("- -test\r\n", "-test\r\n")]; 278 | 279 | for (input, result) in test_vector { 280 | assert_eq!(signed_parser.line_dash_parser(input), Ok(("", result))); 281 | } 282 | } 283 | 284 | #[test] 285 | fn test_line_nodash_parser() { 286 | let signed_parser = PGPCleartextMessageParser::new(&Default::default()); 287 | let test_vector = vec![("test\r\n", "test\r\n")]; 288 | 289 | for (input, result) in test_vector { 290 | assert_eq!(signed_parser.line_nodash_parser(input), Ok(("", result))); 291 | } 292 | } 293 | 294 | #[test] 295 | fn test_signature_parser() { 296 | let signed_parser = PGPCleartextMessageParser::new(&Default::default()); 297 | let input = format!( 298 | "-----BEGIN PGP SIGNATURE-----\r 299 | \r 300 | {SIGNATURE_DATA}-----END PGP SIGNATURE-----\r 301 | " 302 | ); 303 | let signature = PGPSignature { 304 | signature: SIGNATURE_DATA, 305 | keys: vec![], 306 | }; 307 | 308 | assert_eq!(signed_parser.signature_parser(&input), Ok(("", signature))); 309 | } 310 | 311 | #[test] 312 | fn test_armor_header_parser() { 313 | let signed_parser = PGPCleartextMessageParser::new(&Default::default()); 314 | let input = "-----BEGIN PGP SIGNATURE-----\r\n"; 315 | assert_eq!( 316 | signed_parser.armor_header_parser(input), 317 | Ok(("", "-----BEGIN PGP SIGNATURE-----")) 318 | ); 319 | } 320 | 321 | #[test] 322 | fn test_armor_tail_parser() { 323 | let signed_parser = PGPCleartextMessageParser::new(&Default::default()); 324 | let input = "-----END PGP SIGNATURE-----\r\n"; 325 | assert_eq!( 326 | signed_parser.armor_tail_parser(input), 327 | Ok(("", "-----END PGP SIGNATURE-----")) 328 | ); 329 | } 330 | 331 | #[test] 332 | fn test_armor_keys_parser() { 333 | let signed_parser = PGPCleartextMessageParser::new(&Default::default()); 334 | let test_vector = vec![ 335 | ("", vec![]), 336 | ("test: \r\n", vec![("test", "")]), 337 | ("test: test\r\n", vec![("test", "test")]), 338 | ]; 339 | 340 | for (input, result) in test_vector { 341 | assert_eq!(signed_parser.armor_keys_parser(input), Ok(("", result))); 342 | } 343 | } 344 | 345 | #[test] 346 | fn test_signature_data_parser() { 347 | let signed_parser = PGPCleartextMessageParser::new(&Default::default()); 348 | let test_vector = vec![ 349 | "iHUEARYKAB0WIQSsP2kEdoKDVFpSg6u3rK+YCkjapwUCY9qRaQAKCRC3rK+YCkja\r\npwALAP9LEHSYMDW4h8QRHg4MwCzUdnbjBLIvpq4QTo3dIqCUPwEA31MsEf95OKCh\r\nMTHYHajOzjwpwlQVrjkK419igx4imgk\r\n=KONn\r\n", 350 | ]; 351 | 352 | for input in test_vector { 353 | assert_eq!(signed_parser.signature_data_parser(input), Ok(("", input))); 354 | } 355 | } 356 | } 357 | -------------------------------------------------------------------------------- /sectxtlib/src/raw_field.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq)] 2 | pub(crate) struct RawField<'a> { 3 | pub name: &'a str, 4 | pub value: &'a str, 5 | } 6 | -------------------------------------------------------------------------------- /sectxtlib/src/securitytxt.rs: -------------------------------------------------------------------------------- 1 | use crate::parsers::SecurityTxtParser; 2 | use crate::pgpcleartextmessage::PGPCleartextMessageParser; 3 | 4 | use super::fields::{ 5 | AcknowledgmentsField, CanonicalField, ContactField, EncryptionField, ExpiresField, ExtensionField, HiringField, 6 | PolicyField, PreferredLanguagesField, 7 | }; 8 | use super::parse_error::ParseError; 9 | use super::raw_field::RawField; 10 | use super::securitytxt_options::SecurityTxtOptions; 11 | use std::cmp::Ordering; 12 | use std::str::FromStr; 13 | use valuable::Valuable; 14 | 15 | /// A representation of an [RFC 9116](https://www.rfc-editor.org/rfc/rfc9116) security.txt file 16 | #[derive(Debug, PartialEq, Valuable)] 17 | pub struct SecurityTxt { 18 | /// A collection of "Acknowledgments" fields 19 | pub acknowledgments: Vec, 20 | 21 | /// A collection of "Canonical" fields 22 | pub canonical: Vec, 23 | 24 | /// A collection of "Contact" fields 25 | pub contact: Vec, 26 | 27 | /// A collection of "Encryption" fields 28 | pub encryption: Vec, 29 | 30 | /// The "Expires" field 31 | pub expires: ExpiresField, 32 | 33 | /// A collection of "Extension" fields 34 | pub extension: Vec, 35 | 36 | /// A collection of "Hiring" fields 37 | pub hiring: Vec, 38 | 39 | /// A collection of "Policy" fields 40 | pub policy: Vec, 41 | 42 | /// The "Preferred-Languages" field, if available 43 | pub preferred_languages: Option, 44 | } 45 | 46 | impl SecurityTxt { 47 | fn validate_contact_fields(fields: &[ContactField]) -> Result<(), ParseError> { 48 | if fields.is_empty() { 49 | return Err(ParseError::ContactFieldMissing); 50 | } 51 | 52 | Ok(()) 53 | } 54 | 55 | fn validate_expires(fields: &[ExpiresField]) -> Result<(), ParseError> { 56 | if fields.is_empty() { 57 | return Err(ParseError::ExpiresFieldMissing); 58 | } 59 | if fields.len() > 1 { 60 | return Err(ParseError::ExpiresFieldMultiple); 61 | } 62 | 63 | Ok(()) 64 | } 65 | 66 | fn validate_preferred_languages(fields: &[PreferredLanguagesField]) -> Result<(), ParseError> { 67 | if fields.len() > 1 { 68 | return Err(ParseError::PreferredLanguagesFieldMultiple); 69 | } 70 | 71 | Ok(()) 72 | } 73 | 74 | pub(crate) fn new(fields: Vec, options: &SecurityTxtOptions) -> Result { 75 | let mut acknowledgments: Vec = vec![]; 76 | let mut canonical: Vec = vec![]; 77 | let mut contact: Vec = vec![]; 78 | let mut encryption: Vec = vec![]; 79 | let mut expires: Vec = vec![]; 80 | let mut extension: Vec = vec![]; 81 | let mut hiring: Vec = vec![]; 82 | let mut policy: Vec = vec![]; 83 | let mut preferred_languages: Vec = vec![]; 84 | 85 | for field in fields { 86 | let name = field.name.to_lowercase(); 87 | 88 | match &name[..] { 89 | "acknowledgments" => acknowledgments.push(AcknowledgmentsField::new(field.value)?), 90 | "canonical" => canonical.push(CanonicalField::new(field.value)?), 91 | "contact" => contact.push(ContactField::new(field.value)?), 92 | "encryption" => encryption.push(EncryptionField::new(field.value)?), 93 | "expires" => expires.push(ExpiresField::new(field.value, options.now)?), 94 | "hiring" => hiring.push(HiringField::new(field.value)?), 95 | "policy" => policy.push(PolicyField::new(field.value)?), 96 | "preferred-languages" => preferred_languages.push(PreferredLanguagesField::new(field.value)?), 97 | _ => extension.push(ExtensionField::new(name, field.value.to_owned())?), 98 | } 99 | } 100 | 101 | Self::validate_contact_fields(&contact)?; 102 | Self::validate_expires(&expires)?; 103 | Self::validate_preferred_languages(&preferred_languages)?; 104 | 105 | Ok(SecurityTxt { 106 | acknowledgments, 107 | canonical, 108 | contact, 109 | encryption, 110 | expires: expires.pop().unwrap(), // checked in Self::validate_expires() 111 | extension, 112 | hiring, 113 | policy, 114 | preferred_languages: preferred_languages.pop(), 115 | }) 116 | } 117 | 118 | /// Parses a security.txt file as a string according to [RFC 9116](https://www.rfc-editor.org/rfc/rfc9116). 119 | pub fn parse(text: &str) -> Result { 120 | let options = Default::default(); 121 | Self::parse_with(text, &options) 122 | } 123 | 124 | /// Parses a security.txt file as a string according to [RFC 9116](https://www.rfc-editor.org/rfc/rfc9116). 125 | pub fn parse_with(text: &str, options: &SecurityTxtOptions) -> Result { 126 | let unsigned_parser = SecurityTxtParser::new(options); 127 | 128 | match unsigned_parser.parse(text) { 129 | Ok(fields) => { 130 | let fields: Vec = fields.into_iter().flatten().collect(); 131 | Self::new(fields, options) 132 | } 133 | _ => { 134 | let signed_parser = PGPCleartextMessageParser::new(options); 135 | let msg = signed_parser.parse(text)?; 136 | let fields = unsigned_parser.parse(&msg.cleartext)?; 137 | let fields: Vec = fields.into_iter().flatten().collect(); 138 | Self::new(fields, options) 139 | } 140 | } 141 | } 142 | } 143 | 144 | impl PartialOrd for SecurityTxt { 145 | #[inline] 146 | fn partial_cmp(&self, other: &Self) -> Option { 147 | self.expires.partial_cmp(&other.expires) 148 | } 149 | } 150 | 151 | impl FromStr for SecurityTxt { 152 | type Err = ParseError; 153 | 154 | #[inline] 155 | fn from_str(text: &str) -> Result { 156 | Self::parse(text) 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /sectxtlib/src/securitytxt_options.rs: -------------------------------------------------------------------------------- 1 | use chrono::{DateTime, Utc}; 2 | 3 | /// Options for parsing a security.txt file 4 | #[derive(Clone, Debug)] 5 | pub struct SecurityTxtOptions { 6 | /// The current date and time to validate the "Expires" field against 7 | pub now: DateTime, 8 | 9 | /// Whether to be strict with line endings or more relaxed 10 | pub strict: bool, 11 | } 12 | 13 | impl SecurityTxtOptions { 14 | pub fn new(strict: bool) -> Self { 15 | Self { 16 | now: Utc::now(), 17 | strict, 18 | } 19 | } 20 | } 21 | 22 | impl Default for SecurityTxtOptions { 23 | fn default() -> Self { 24 | Self { 25 | now: Utc::now(), 26 | strict: true, 27 | } 28 | } 29 | } 30 | --------------------------------------------------------------------------------