New upstream version 2.3.0
Sophie Brun
2 years ago
0 | [target.armv7-unknown-linux-gnueabihf] | |
1 | linker = "arm-linux-gnueabihf-gcc" | |
2 | ||
3 | [target.aarch64-unknown-linux-gnu] | |
4 | linker = "aarch64-linux-gnu-gcc" |
10 | 10 | |
11 | 11 | ## Static analysis checks |
12 | 12 | - [ ] All rust files are formatted using `cargo fmt` |
13 | - [ ] All `clippy` checks pass when running `cargo clippy --all-targets --all-features -- -D warnings -A clippy::deref_addrof -A clippy::mutex-atomic` | |
13 | - [ ] All `clippy` checks pass when running `cargo clippy --all-targets --all-features -- -D warnings -A clippy::mutex-atomic` | |
14 | 14 | - [ ] All existing tests pass |
15 | 15 | |
16 | 16 | ## Documentation |
3 | 3 | |
4 | 4 | jobs: |
5 | 5 | build-nix: |
6 | env: | |
7 | IN_PIPELINE: true | |
6 | 8 | runs-on: ${{ matrix.os }} |
7 | 9 | if: github.ref == 'refs/heads/main' |
8 | 10 | strategy: |
9 | 11 | matrix: |
10 | type: [ubuntu-x64, ubuntu-x86] | |
12 | type: [ubuntu-x64, ubuntu-x86, armv7, aarch64] | |
11 | 13 | include: |
12 | 14 | - type: ubuntu-x64 |
13 | 15 | os: ubuntu-latest |
21 | 23 | name: x86-linux-feroxbuster |
22 | 24 | path: target/i686-unknown-linux-musl/release/feroxbuster |
23 | 25 | pkg_config_path: /usr/lib/i686-linux-gnu/pkgconfig |
26 | - type: armv7 | |
27 | os: ubuntu-latest | |
28 | target: armv7-unknown-linux-gnueabihf | |
29 | name: armv7-feroxbuster | |
30 | path: target/armv7-unknown-linux-gnueabihf/release/feroxbuster | |
31 | pkg_config_path: /usr/lib/x86_64-linux-gnu/pkgconfig | |
32 | - type: aarch64 | |
33 | os: ubuntu-latest | |
34 | target: aarch64-unknown-linux-gnu | |
35 | name: aarch64-feroxbuster | |
36 | path: target/aarch64-unknown-linux-gnu/release/feroxbuster | |
37 | pkg_config_path: /usr/lib/x86_64-linux-gnu/pkgconfig | |
24 | 38 | steps: |
25 | 39 | - uses: actions/checkout@v2 |
26 | 40 | - name: Install System Dependencies |
27 | 41 | run: | |
42 | env | |
28 | 43 | sudo apt-get update |
29 | sudo apt-get install -y --no-install-recommends libssl-dev pkg-config | |
44 | sudo apt-get install -y --no-install-recommends libssl-dev pkg-config gcc-arm-linux-gnueabihf gcc-aarch64-linux-gnu | |
30 | 45 | - uses: actions-rs/toolchain@v1 |
31 | 46 | with: |
32 | 47 | toolchain: stable |
42 | 57 | args: --release --target=${{ matrix.target }} |
43 | 58 | - name: Strip symbols from binary |
44 | 59 | run: | |
45 | strip -s ${{ matrix.path }} | |
60 | strip -s ${{ matrix.path }} || arm-linux-gnueabihf-strip -s ${{ matrix.path }} || aarch64-linux-gnu-strip -s ${{ matrix.path }} | |
46 | 61 | - name: Build tar.gz for homebrew installs |
47 | 62 | if: matrix.type == 'ubuntu-x64' |
48 | 63 | run: | |
71 | 86 | path: ./target/x86_64-unknown-linux-musl/debian/* |
72 | 87 | |
73 | 88 | build-macos: |
89 | env: | |
90 | IN_PIPELINE: true | |
74 | 91 | runs-on: macos-latest |
75 | 92 | if: github.ref == 'refs/heads/main' |
76 | 93 | steps: |
101 | 118 | path: x86_64-macos-feroxbuster.tar.gz |
102 | 119 | |
103 | 120 | build-windows: |
121 | env: | |
122 | IN_PIPELINE: true | |
104 | 123 | runs-on: ${{ matrix.os }} |
105 | 124 | if: github.ref == 'refs/heads/main' |
106 | 125 | strategy: |
133 | 152 | with: |
134 | 153 | name: ${{ matrix.name }} |
135 | 154 | path: ${{ matrix.path }} |
136 |
1 | 1 | # It is not intended for manual editing. |
2 | 2 | [[package]] |
3 | 3 | name = "aho-corasick" |
4 | version = "0.7.15" | |
5 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
6 | checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" | |
4 | version = "0.7.18" | |
5 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
6 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" | |
7 | 7 | dependencies = [ |
8 | 8 | "memchr", |
9 | 9 | ] |
19 | 19 | |
20 | 20 | [[package]] |
21 | 21 | name = "anyhow" |
22 | version = "1.0.38" | |
23 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
24 | checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" | |
25 | ||
26 | [[package]] | |
27 | name = "arrayref" | |
28 | version = "0.3.6" | |
29 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
30 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" | |
31 | ||
32 | [[package]] | |
33 | name = "arrayvec" | |
34 | version = "0.5.2" | |
35 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
36 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" | |
22 | version = "1.0.41" | |
23 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
24 | checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61" | |
37 | 25 | |
38 | 26 | [[package]] |
39 | 27 | name = "ascii-canvas" |
40 | version = "2.0.0" | |
41 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
42 | checksum = "ff8eb72df928aafb99fe5d37b383f2fe25bd2a765e3e5f7c365916b6f2463a29" | |
28 | version = "3.0.0" | |
29 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
30 | checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" | |
43 | 31 | dependencies = [ |
44 | 32 | "term", |
45 | 33 | ] |
46 | 34 | |
47 | 35 | [[package]] |
48 | 36 | name = "assert-json-diff" |
49 | version = "1.1.0" | |
50 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
51 | checksum = "4259cbe96513d2f1073027a259fc2ca917feb3026a5a8d984e3628e490255cc0" | |
52 | dependencies = [ | |
53 | "extend", | |
37 | version = "2.0.1" | |
38 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
39 | checksum = "50f1c3703dd33532d7f0ca049168930e9099ecac238e23cf932f3a69c42f06da" | |
40 | dependencies = [ | |
54 | 41 | "serde", |
55 | 42 | "serde_json", |
56 | 43 | ] |
57 | 44 | |
58 | 45 | [[package]] |
59 | 46 | name = "assert_cmd" |
60 | version = "1.0.3" | |
61 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
62 | checksum = "f2475b58cd94eb4f70159f4fd8844ba3b807532fe3131b3373fae060bbe30396" | |
47 | version = "1.0.5" | |
48 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
49 | checksum = "a88b6bd5df287567ffdf4ddf4d33060048e1068308e5f62d81c6f9824a045a48" | |
63 | 50 | dependencies = [ |
64 | 51 | "bstr", |
65 | 52 | "doc-comment", |
71 | 58 | |
72 | 59 | [[package]] |
73 | 60 | name = "async-channel" |
74 | version = "1.5.1" | |
75 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
76 | checksum = "59740d83946db6a5af71ae25ddf9562c2b176b2ca42cf99a455f09f4a220d6b9" | |
61 | version = "1.6.1" | |
62 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
63 | checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" | |
77 | 64 | dependencies = [ |
78 | 65 | "concurrent-queue", |
79 | 66 | "event-listener", |
82 | 69 | |
83 | 70 | [[package]] |
84 | 71 | name = "async-executor" |
85 | version = "1.4.0" | |
86 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
87 | checksum = "eb877970c7b440ead138f6321a3b5395d6061183af779340b65e20c0fede9146" | |
72 | version = "1.4.1" | |
73 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
74 | checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" | |
88 | 75 | dependencies = [ |
89 | 76 | "async-task", |
90 | 77 | "concurrent-queue", |
91 | 78 | "fastrand", |
92 | 79 | "futures-lite", |
93 | 80 | "once_cell", |
94 | "vec-arena", | |
81 | "slab", | |
95 | 82 | ] |
96 | 83 | |
97 | 84 | [[package]] |
112 | 99 | |
113 | 100 | [[package]] |
114 | 101 | name = "async-io" |
115 | version = "1.3.1" | |
116 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
117 | checksum = "9315f8f07556761c3e48fec2e6b276004acf426e6dc068b2c2251854d65ee0fd" | |
102 | version = "1.4.1" | |
103 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
104 | checksum = "4bbfd5cf2794b1e908ea8457e6c45f8f8f1f6ec5f74617bf4662623f47503c3b" | |
118 | 105 | dependencies = [ |
119 | 106 | "concurrent-queue", |
120 | 107 | "fastrand", |
121 | 108 | "futures-lite", |
122 | 109 | "libc", |
123 | 110 | "log", |
124 | "nb-connect", | |
125 | 111 | "once_cell", |
126 | 112 | "parking", |
127 | 113 | "polling", |
128 | "vec-arena", | |
114 | "slab", | |
115 | "socket2", | |
129 | 116 | "waker-fn", |
130 | 117 | "winapi", |
131 | 118 | ] |
132 | 119 | |
133 | 120 | [[package]] |
134 | 121 | name = "async-lock" |
135 | version = "2.3.0" | |
136 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
137 | checksum = "1996609732bde4a9988bc42125f55f2af5f3c36370e27c778d5191a4a1b63bfb" | |
122 | version = "2.4.0" | |
123 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
124 | checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b" | |
138 | 125 | dependencies = [ |
139 | 126 | "event-listener", |
140 | 127 | ] |
159 | 146 | |
160 | 147 | [[package]] |
161 | 148 | name = "async-process" |
162 | version = "1.0.2" | |
163 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
164 | checksum = "ef37b86e2fa961bae5a4d212708ea0154f904ce31d1a4a7f47e1bbc33a0c040b" | |
149 | version = "1.1.0" | |
150 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
151 | checksum = "a8f38756dd9ac84671c428afbf7c9f7495feff9ec5b0710f17100098e5b354ac" | |
165 | 152 | dependencies = [ |
166 | 153 | "async-io", |
167 | 154 | "blocking", |
168 | "cfg-if 1.0.0", | |
155 | "cfg-if", | |
169 | 156 | "event-listener", |
170 | 157 | "futures-lite", |
158 | "libc", | |
171 | 159 | "once_cell", |
172 | "signal-hook 0.3.4", | |
160 | "signal-hook", | |
173 | 161 | "winapi", |
174 | 162 | ] |
175 | 163 | |
209 | 197 | |
210 | 198 | [[package]] |
211 | 199 | name = "async-trait" |
212 | version = "0.1.42" | |
213 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
214 | checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" | |
200 | version = "0.1.50" | |
201 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
202 | checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722" | |
215 | 203 | dependencies = [ |
216 | 204 | "proc-macro2", |
217 | 205 | "quote", |
278 | 266 | version = "1.2.1" |
279 | 267 | source = "registry+https://github.com/rust-lang/crates.io-index" |
280 | 268 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" |
281 | ||
282 | [[package]] | |
283 | name = "blake2b_simd" | |
284 | version = "0.5.11" | |
285 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
286 | checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" | |
287 | dependencies = [ | |
288 | "arrayref", | |
289 | "arrayvec", | |
290 | "constant_time_eq", | |
291 | ] | |
292 | 269 | |
293 | 270 | [[package]] |
294 | 271 | name = "blocking" |
306 | 283 | |
307 | 284 | [[package]] |
308 | 285 | name = "bstr" |
309 | version = "0.2.15" | |
310 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
311 | checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" | |
286 | version = "0.2.16" | |
287 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
288 | checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" | |
312 | 289 | dependencies = [ |
313 | 290 | "lazy_static", |
314 | 291 | "memchr", |
317 | 294 | |
318 | 295 | [[package]] |
319 | 296 | name = "bumpalo" |
320 | version = "3.6.0" | |
321 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
322 | checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" | |
323 | ||
324 | [[package]] | |
325 | name = "byteorder" | |
326 | version = "1.4.2" | |
327 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
328 | checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" | |
297 | version = "3.7.0" | |
298 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
299 | checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" | |
329 | 300 | |
330 | 301 | [[package]] |
331 | 302 | name = "bytes" |
341 | 312 | |
342 | 313 | [[package]] |
343 | 314 | name = "cc" |
344 | version = "1.0.66" | |
345 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
346 | checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" | |
347 | ||
348 | [[package]] | |
349 | name = "cfg-if" | |
350 | version = "0.1.10" | |
351 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
352 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" | |
315 | version = "1.0.68" | |
316 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
317 | checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" | |
353 | 318 | |
354 | 319 | [[package]] |
355 | 320 | name = "cfg-if" |
383 | 348 | |
384 | 349 | [[package]] |
385 | 350 | name = "console" |
386 | version = "0.14.0" | |
387 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
388 | checksum = "7cc80946b3480f421c2f17ed1cb841753a371c7c5104f51d507e13f532c856aa" | |
351 | version = "0.14.1" | |
352 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
353 | checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45" | |
389 | 354 | dependencies = [ |
390 | 355 | "encode_unicode", |
391 | 356 | "lazy_static", |
397 | 362 | ] |
398 | 363 | |
399 | 364 | [[package]] |
400 | name = "constant_time_eq" | |
401 | version = "0.1.5" | |
402 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
403 | checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" | |
404 | ||
405 | [[package]] | |
406 | 365 | name = "core-foundation" |
407 | 366 | version = "0.9.1" |
408 | 367 | source = "registry+https://github.com/rust-lang/crates.io-index" |
420 | 379 | |
421 | 380 | [[package]] |
422 | 381 | name = "crossbeam-utils" |
423 | version = "0.8.1" | |
424 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
425 | checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" | |
426 | dependencies = [ | |
427 | "autocfg", | |
428 | "cfg-if 1.0.0", | |
382 | version = "0.8.5" | |
383 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
384 | checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" | |
385 | dependencies = [ | |
386 | "cfg-if", | |
429 | 387 | "lazy_static", |
430 | 388 | ] |
431 | 389 | |
432 | 390 | [[package]] |
433 | 391 | name = "crossterm" |
434 | version = "0.19.0" | |
435 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
436 | checksum = "7c36c10130df424b2f3552fcc2ddcd9b28a27b1e54b358b45874f88d1ca6888c" | |
392 | version = "0.20.0" | |
393 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
394 | checksum = "c0ebde6a9dd5e331cd6c6f48253254d117642c31653baa475e394657c59c1f7d" | |
437 | 395 | dependencies = [ |
438 | 396 | "bitflags", |
439 | 397 | "crossterm_winapi", |
440 | "lazy_static", | |
441 | 398 | "libc", |
442 | 399 | "mio", |
443 | 400 | "parking_lot", |
444 | "signal-hook 0.1.17", | |
401 | "signal-hook", | |
402 | "signal-hook-mio", | |
445 | 403 | "winapi", |
446 | 404 | ] |
447 | 405 | |
448 | 406 | [[package]] |
449 | 407 | name = "crossterm_winapi" |
450 | version = "0.7.0" | |
451 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
452 | checksum = "0da8964ace4d3e4a044fd027919b2237000b24315a37c916f61809f1ff2140b9" | |
408 | version = "0.8.0" | |
409 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
410 | checksum = "3a6966607622438301997d3dac0d2f6e9a90c68bb6bc1785ea98456ab93c0507" | |
453 | 411 | dependencies = [ |
454 | 412 | "winapi", |
455 | 413 | ] |
462 | 420 | |
463 | 421 | [[package]] |
464 | 422 | name = "ctor" |
465 | version = "0.1.19" | |
466 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
467 | checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19" | |
423 | version = "0.1.20" | |
424 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
425 | checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" | |
468 | 426 | dependencies = [ |
469 | 427 | "quote", |
470 | 428 | "syn", |
472 | 430 | |
473 | 431 | [[package]] |
474 | 432 | name = "ctrlc" |
475 | version = "3.1.7" | |
476 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
477 | checksum = "b57a92e9749e10f25a171adcebfafe72991d45e7ec2dcb853e8f83d9dafaeb08" | |
433 | version = "3.1.9" | |
434 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
435 | checksum = "232295399409a8b7ae41276757b5a1cc21032848d42bff2352261f958b3ca29a" | |
478 | 436 | dependencies = [ |
479 | 437 | "nix", |
480 | 438 | "winapi", |
482 | 440 | |
483 | 441 | [[package]] |
484 | 442 | name = "curl" |
485 | version = "0.4.34" | |
486 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
487 | checksum = "e268162af1a5fe89917ae25ba3b0a77c8da752bdc58e7dbb4f15b91fbd33756e" | |
443 | version = "0.4.38" | |
444 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
445 | checksum = "003cb79c1c6d1c93344c7e1201bb51c2148f24ec2bd9c253709d6b2efb796515" | |
488 | 446 | dependencies = [ |
489 | 447 | "curl-sys", |
490 | 448 | "libc", |
497 | 455 | |
498 | 456 | [[package]] |
499 | 457 | name = "curl-sys" |
500 | version = "0.4.40+curl-7.75.0" | |
501 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
502 | checksum = "2ffafc1c35958318bd7fdd0582995ce4c72f4f461a8e70499ccee83a619fd562" | |
458 | version = "0.4.44+curl-7.77.0" | |
459 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
460 | checksum = "4b6d85e9322b193f117c966e79c2d6929ec08c02f339f950044aba12e20bbaf1" | |
503 | 461 | dependencies = [ |
504 | 462 | "cc", |
505 | 463 | "libc", |
525 | 483 | |
526 | 484 | [[package]] |
527 | 485 | name = "dirs" |
528 | version = "1.0.5" | |
529 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
530 | checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" | |
486 | version = "3.0.2" | |
487 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
488 | checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" | |
489 | dependencies = [ | |
490 | "dirs-sys", | |
491 | ] | |
492 | ||
493 | [[package]] | |
494 | name = "dirs-next" | |
495 | version = "2.0.0" | |
496 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
497 | checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" | |
498 | dependencies = [ | |
499 | "cfg-if", | |
500 | "dirs-sys-next", | |
501 | ] | |
502 | ||
503 | [[package]] | |
504 | name = "dirs-sys" | |
505 | version = "0.3.6" | |
506 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
507 | checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" | |
531 | 508 | dependencies = [ |
532 | 509 | "libc", |
533 | 510 | "redox_users", |
535 | 512 | ] |
536 | 513 | |
537 | 514 | [[package]] |
538 | name = "dirs" | |
539 | version = "3.0.1" | |
540 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
541 | checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff" | |
542 | dependencies = [ | |
543 | "dirs-sys", | |
544 | ] | |
545 | ||
546 | [[package]] | |
547 | name = "dirs-sys" | |
548 | version = "0.3.5" | |
549 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
550 | checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" | |
515 | name = "dirs-sys-next" | |
516 | version = "0.1.2" | |
517 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
518 | checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" | |
551 | 519 | dependencies = [ |
552 | 520 | "libc", |
553 | 521 | "redox_users", |
587 | 555 | source = "registry+https://github.com/rust-lang/crates.io-index" |
588 | 556 | checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" |
589 | 557 | dependencies = [ |
590 | "cfg-if 1.0.0", | |
558 | "cfg-if", | |
591 | 559 | ] |
592 | 560 | |
593 | 561 | [[package]] |
594 | 562 | name = "env_logger" |
595 | version = "0.8.3" | |
596 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
597 | checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" | |
563 | version = "0.8.4" | |
564 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
565 | checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" | |
598 | 566 | dependencies = [ |
599 | 567 | "atty", |
600 | 568 | "humantime", |
610 | 578 | checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" |
611 | 579 | |
612 | 580 | [[package]] |
613 | name = "extend" | |
614 | version = "0.1.2" | |
615 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
616 | checksum = "f47da3a72ec598d9c8937a7ebca8962a5c7a1f28444e38c2b33c771ba3f55f05" | |
617 | dependencies = [ | |
618 | "proc-macro-error", | |
619 | "proc-macro2", | |
620 | "quote", | |
621 | "syn", | |
622 | ] | |
623 | ||
624 | [[package]] | |
625 | 581 | name = "fastrand" |
626 | version = "1.4.0" | |
627 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
628 | checksum = "ca5faf057445ce5c9d4329e382b2ce7ca38550ef3b73a5348362d5f24e0c7fe3" | |
582 | version = "1.4.1" | |
583 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
584 | checksum = "77b705829d1e87f762c2df6da140b26af5839e1033aa84aa5f56bb688e4e1bdb" | |
629 | 585 | dependencies = [ |
630 | 586 | "instant", |
631 | 587 | ] |
632 | 588 | |
633 | 589 | [[package]] |
634 | 590 | name = "feroxbuster" |
635 | version = "2.2.1" | |
591 | version = "2.3.0" | |
636 | 592 | dependencies = [ |
637 | 593 | "anyhow", |
638 | 594 | "assert_cmd", |
640 | 596 | "console", |
641 | 597 | "crossterm", |
642 | 598 | "ctrlc", |
643 | "dirs 3.0.1", | |
599 | "dirs", | |
644 | 600 | "env_logger", |
645 | 601 | "futures", |
646 | 602 | "fuzzyhash", |
679 | 635 | ] |
680 | 636 | |
681 | 637 | [[package]] |
682 | name = "flume" | |
683 | version = "0.10.2" | |
684 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
685 | checksum = "531a685ab99b8f60a271b44d5dd1a76e55124a8c9fa0407b7a8e9cd172d5b588" | |
686 | dependencies = [ | |
687 | "futures-core", | |
688 | "futures-sink", | |
689 | "pin-project", | |
690 | "spinning_top", | |
691 | ] | |
692 | ||
693 | [[package]] | |
694 | 638 | name = "fnv" |
695 | 639 | version = "1.0.7" |
696 | 640 | source = "registry+https://github.com/rust-lang/crates.io-index" |
713 | 657 | |
714 | 658 | [[package]] |
715 | 659 | name = "form_urlencoded" |
716 | version = "1.0.0" | |
717 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
718 | checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" | |
660 | version = "1.0.1" | |
661 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
662 | checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" | |
719 | 663 | dependencies = [ |
720 | 664 | "matches", |
721 | 665 | "percent-encoding", |
723 | 667 | |
724 | 668 | [[package]] |
725 | 669 | name = "futures" |
726 | version = "0.3.12" | |
727 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
728 | checksum = "da9052a1a50244d8d5aa9bf55cbc2fb6f357c86cc52e46c62ed390a7180cf150" | |
670 | version = "0.3.15" | |
671 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
672 | checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27" | |
729 | 673 | dependencies = [ |
730 | 674 | "futures-channel", |
731 | 675 | "futures-core", |
738 | 682 | |
739 | 683 | [[package]] |
740 | 684 | name = "futures-channel" |
741 | version = "0.3.12" | |
742 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
743 | checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846" | |
685 | version = "0.3.15" | |
686 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
687 | checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2" | |
744 | 688 | dependencies = [ |
745 | 689 | "futures-core", |
746 | 690 | "futures-sink", |
748 | 692 | |
749 | 693 | [[package]] |
750 | 694 | name = "futures-core" |
751 | version = "0.3.12" | |
752 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
753 | checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" | |
695 | version = "0.3.15" | |
696 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
697 | checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1" | |
754 | 698 | |
755 | 699 | [[package]] |
756 | 700 | name = "futures-executor" |
757 | version = "0.3.12" | |
758 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
759 | checksum = "e9e59fdc009a4b3096bf94f740a0f2424c082521f20a9b08c5c07c48d90fd9b9" | |
701 | version = "0.3.15" | |
702 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
703 | checksum = "badaa6a909fac9e7236d0620a2f57f7664640c56575b71a7552fbd68deafab79" | |
760 | 704 | dependencies = [ |
761 | 705 | "futures-core", |
762 | 706 | "futures-task", |
765 | 709 | |
766 | 710 | [[package]] |
767 | 711 | name = "futures-io" |
768 | version = "0.3.12" | |
769 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
770 | checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500" | |
712 | version = "0.3.15" | |
713 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
714 | checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1" | |
771 | 715 | |
772 | 716 | [[package]] |
773 | 717 | name = "futures-lite" |
774 | version = "1.11.3" | |
775 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
776 | checksum = "b4481d0cd0de1d204a4fa55e7d45f07b1d958abcb06714b3446438e2eff695fb" | |
718 | version = "1.12.0" | |
719 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
720 | checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" | |
777 | 721 | dependencies = [ |
778 | 722 | "fastrand", |
779 | 723 | "futures-core", |
786 | 730 | |
787 | 731 | [[package]] |
788 | 732 | name = "futures-macro" |
789 | version = "0.3.12" | |
790 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
791 | checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd" | |
792 | dependencies = [ | |
733 | version = "0.3.15" | |
734 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
735 | checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121" | |
736 | dependencies = [ | |
737 | "autocfg", | |
793 | 738 | "proc-macro-hack", |
794 | 739 | "proc-macro2", |
795 | 740 | "quote", |
798 | 743 | |
799 | 744 | [[package]] |
800 | 745 | name = "futures-sink" |
801 | version = "0.3.12" | |
802 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
803 | checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6" | |
746 | version = "0.3.15" | |
747 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
748 | checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282" | |
804 | 749 | |
805 | 750 | [[package]] |
806 | 751 | name = "futures-task" |
807 | version = "0.3.12" | |
808 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
809 | checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86" | |
810 | dependencies = [ | |
811 | "once_cell", | |
812 | ] | |
752 | version = "0.3.15" | |
753 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
754 | checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae" | |
813 | 755 | |
814 | 756 | [[package]] |
815 | 757 | name = "futures-util" |
816 | version = "0.3.12" | |
817 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
818 | checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b" | |
819 | dependencies = [ | |
758 | version = "0.3.15" | |
759 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
760 | checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967" | |
761 | dependencies = [ | |
762 | "autocfg", | |
820 | 763 | "futures-channel", |
821 | 764 | "futures-core", |
822 | 765 | "futures-io", |
839 | 782 | |
840 | 783 | [[package]] |
841 | 784 | name = "getrandom" |
842 | version = "0.1.16" | |
843 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
844 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" | |
845 | dependencies = [ | |
846 | "cfg-if 1.0.0", | |
847 | "libc", | |
848 | "wasi 0.9.0+wasi-snapshot-preview1", | |
849 | ] | |
850 | ||
851 | [[package]] | |
852 | name = "getrandom" | |
853 | version = "0.2.2" | |
854 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
855 | checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" | |
856 | dependencies = [ | |
857 | "cfg-if 1.0.0", | |
858 | "libc", | |
859 | "wasi 0.10.2+wasi-snapshot-preview1", | |
785 | version = "0.2.3" | |
786 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
787 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" | |
788 | dependencies = [ | |
789 | "cfg-if", | |
790 | "libc", | |
791 | "wasi", | |
860 | 792 | ] |
861 | 793 | |
862 | 794 | [[package]] |
874 | 806 | |
875 | 807 | [[package]] |
876 | 808 | name = "h2" |
877 | version = "0.3.0" | |
878 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
879 | checksum = "6b67e66362108efccd8ac053abafc8b7a8d86a37e6e48fc4f6f7485eb5e9e6a5" | |
809 | version = "0.3.3" | |
810 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
811 | checksum = "825343c4eef0b63f541f8903f395dc5beb362a979b5799a84062527ef1e37726" | |
880 | 812 | dependencies = [ |
881 | 813 | "bytes", |
882 | 814 | "fnv", |
889 | 821 | "tokio", |
890 | 822 | "tokio-util", |
891 | 823 | "tracing", |
892 | "tracing-futures", | |
893 | 824 | ] |
894 | 825 | |
895 | 826 | [[package]] |
909 | 840 | |
910 | 841 | [[package]] |
911 | 842 | name = "http" |
912 | version = "0.2.3" | |
913 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
914 | checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" | |
843 | version = "0.2.4" | |
844 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
845 | checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" | |
915 | 846 | dependencies = [ |
916 | 847 | "bytes", |
917 | 848 | "fnv", |
920 | 851 | |
921 | 852 | [[package]] |
922 | 853 | name = "http-body" |
923 | version = "0.4.0" | |
924 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
925 | checksum = "2861bd27ee074e5ee891e8b539837a9430012e249d7f0ca2d795650f579c1994" | |
854 | version = "0.4.2" | |
855 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
856 | checksum = "60daa14be0e0786db0f03a9e57cb404c9d756eed2b6c62b9ea98ec5743ec75a9" | |
926 | 857 | dependencies = [ |
927 | 858 | "bytes", |
928 | 859 | "http", |
860 | "pin-project-lite", | |
929 | 861 | ] |
930 | 862 | |
931 | 863 | [[package]] |
932 | 864 | name = "httparse" |
933 | version = "1.3.5" | |
934 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
935 | checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691" | |
865 | version = "1.4.1" | |
866 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
867 | checksum = "f3a87b616e37e93c22fb19bcd386f02f3af5ea98a25670ad0fce773de23c5e68" | |
936 | 868 | |
937 | 869 | [[package]] |
938 | 870 | name = "httpdate" |
939 | version = "0.3.2" | |
940 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
941 | checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" | |
871 | version = "1.0.1" | |
872 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
873 | checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" | |
942 | 874 | |
943 | 875 | [[package]] |
944 | 876 | name = "httpmock" |
945 | version = "0.5.5" | |
946 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
947 | checksum = "93a02d342ce934f890fa39865cf7d2f3485d19a805b7c570ea33819106df1c21" | |
877 | version = "0.5.8" | |
878 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
879 | checksum = "b217899bcbe8ad3bdee7a46727bd3754b908831462755567852fb20eac585d46" | |
948 | 880 | dependencies = [ |
949 | 881 | "assert-json-diff", |
950 | 882 | "async-object-pool", |
975 | 907 | |
976 | 908 | [[package]] |
977 | 909 | name = "hyper" |
978 | version = "0.14.4" | |
979 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
980 | checksum = "e8e946c2b1349055e0b72ae281b238baf1a3ea7307c7e9f9d64673bdd9c26ac7" | |
910 | version = "0.14.9" | |
911 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
912 | checksum = "07d6baa1b441335f3ce5098ac421fb6547c46dda735ca1bc6d0153c838f9dd83" | |
981 | 913 | dependencies = [ |
982 | 914 | "bytes", |
983 | 915 | "futures-channel", |
989 | 921 | "httparse", |
990 | 922 | "httpdate", |
991 | 923 | "itoa", |
992 | "pin-project", | |
924 | "pin-project-lite", | |
993 | 925 | "socket2", |
994 | 926 | "tokio", |
995 | 927 | "tower-service", |
1012 | 944 | |
1013 | 945 | [[package]] |
1014 | 946 | name = "idna" |
1015 | version = "0.2.1" | |
1016 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1017 | checksum = "de910d521f7cc3135c4de8db1cb910e0b5ed1dc6f57c381cd07e8e661ce10094" | |
947 | version = "0.2.3" | |
948 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
949 | checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" | |
1018 | 950 | dependencies = [ |
1019 | 951 | "matches", |
1020 | 952 | "unicode-bidi", |
1023 | 955 | |
1024 | 956 | [[package]] |
1025 | 957 | name = "indexmap" |
1026 | version = "1.6.1" | |
1027 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1028 | checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" | |
958 | version = "1.6.2" | |
959 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
960 | checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" | |
1029 | 961 | dependencies = [ |
1030 | 962 | "autocfg", |
1031 | 963 | "hashbrown", |
1049 | 981 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1050 | 982 | checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" |
1051 | 983 | dependencies = [ |
1052 | "cfg-if 1.0.0", | |
984 | "cfg-if", | |
1053 | 985 | ] |
1054 | 986 | |
1055 | 987 | [[package]] |
1060 | 992 | |
1061 | 993 | [[package]] |
1062 | 994 | name = "isahc" |
1063 | version = "1.1.0" | |
1064 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1065 | checksum = "af3d0a62435883f745c825ec06a03a38d24bf5fa65c43e2c083b6a60ce0058ae" | |
1066 | dependencies = [ | |
995 | version = "1.4.0" | |
996 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
997 | checksum = "81c01404730bb4574bbacb59ca0855f969f8eabd688ca22866f2cc333f1a4f69" | |
998 | dependencies = [ | |
999 | "async-channel", | |
1067 | 1000 | "crossbeam-utils", |
1068 | 1001 | "curl", |
1069 | 1002 | "curl-sys", |
1070 | 1003 | "encoding_rs", |
1071 | "flume", | |
1004 | "event-listener", | |
1072 | 1005 | "futures-lite", |
1073 | 1006 | "http", |
1074 | 1007 | "log", |
1075 | 1008 | "mime", |
1076 | 1009 | "once_cell", |
1010 | "polling", | |
1077 | 1011 | "slab", |
1078 | 1012 | "sluice", |
1079 | 1013 | "tracing", |
1084 | 1018 | |
1085 | 1019 | [[package]] |
1086 | 1020 | name = "itertools" |
1087 | version = "0.9.0" | |
1088 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1089 | checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" | |
1021 | version = "0.10.1" | |
1022 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1023 | checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" | |
1090 | 1024 | dependencies = [ |
1091 | 1025 | "either", |
1092 | 1026 | ] |
1099 | 1033 | |
1100 | 1034 | [[package]] |
1101 | 1035 | name = "js-sys" |
1102 | version = "0.3.47" | |
1103 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1104 | checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65" | |
1036 | version = "0.3.51" | |
1037 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1038 | checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" | |
1105 | 1039 | dependencies = [ |
1106 | 1040 | "wasm-bindgen", |
1107 | 1041 | ] |
1117 | 1051 | |
1118 | 1052 | [[package]] |
1119 | 1053 | name = "lalrpop" |
1120 | version = "0.19.4" | |
1121 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1122 | checksum = "4a71d75b267b3299da9ccff4dd80d73325b5d8adcd76fe97cf92725eb7c6f122" | |
1054 | version = "0.19.6" | |
1055 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1056 | checksum = "b15174f1c529af5bf1283c3bc0058266b483a67156f79589fab2a25e23cf8988" | |
1123 | 1057 | dependencies = [ |
1124 | 1058 | "ascii-canvas", |
1125 | 1059 | "atty", |
1140 | 1074 | |
1141 | 1075 | [[package]] |
1142 | 1076 | name = "lalrpop-util" |
1143 | version = "0.19.4" | |
1144 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1145 | checksum = "3ebbd90154472db6267a7d28ca08fea7788e5619fef10f2398155cb74c08f77a" | |
1077 | version = "0.19.6" | |
1078 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1079 | checksum = "d3e58cce361efcc90ba8a0a5f982c741ff86b603495bb15a998412e957dcd278" | |
1146 | 1080 | dependencies = [ |
1147 | 1081 | "regex", |
1148 | 1082 | ] |
1169 | 1103 | |
1170 | 1104 | [[package]] |
1171 | 1105 | name = "levenshtein" |
1172 | version = "1.0.4" | |
1173 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1174 | checksum = "66189c12161c65c0023ceb53e2fccc0013311bcb36a7cbd0f9c5e938b408ac96" | |
1106 | version = "1.0.5" | |
1107 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1108 | checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" | |
1175 | 1109 | |
1176 | 1110 | [[package]] |
1177 | 1111 | name = "libc" |
1178 | version = "0.2.86" | |
1179 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1180 | checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" | |
1112 | version = "0.2.97" | |
1113 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1114 | checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" | |
1181 | 1115 | |
1182 | 1116 | [[package]] |
1183 | 1117 | name = "libnghttp2-sys" |
1191 | 1125 | |
1192 | 1126 | [[package]] |
1193 | 1127 | name = "libz-sys" |
1194 | version = "1.1.2" | |
1195 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1196 | checksum = "602113192b08db8f38796c4e85c39e960c145965140e918018bcde1952429655" | |
1128 | version = "1.1.3" | |
1129 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1130 | checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66" | |
1197 | 1131 | dependencies = [ |
1198 | 1132 | "cc", |
1199 | 1133 | "libc", |
1203 | 1137 | |
1204 | 1138 | [[package]] |
1205 | 1139 | name = "lock_api" |
1206 | version = "0.4.2" | |
1207 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1208 | checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" | |
1140 | version = "0.4.4" | |
1141 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1142 | checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" | |
1209 | 1143 | dependencies = [ |
1210 | 1144 | "scopeguard", |
1211 | 1145 | ] |
1216 | 1150 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1217 | 1151 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" |
1218 | 1152 | dependencies = [ |
1219 | "cfg-if 1.0.0", | |
1153 | "cfg-if", | |
1220 | 1154 | "value-bag", |
1221 | 1155 | ] |
1222 | 1156 | |
1228 | 1162 | |
1229 | 1163 | [[package]] |
1230 | 1164 | name = "memchr" |
1231 | version = "2.3.4" | |
1232 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1233 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" | |
1165 | version = "2.4.0" | |
1166 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1167 | checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" | |
1234 | 1168 | |
1235 | 1169 | [[package]] |
1236 | 1170 | name = "mime" |
1240 | 1174 | |
1241 | 1175 | [[package]] |
1242 | 1176 | name = "mio" |
1243 | version = "0.7.7" | |
1244 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1245 | checksum = "e50ae3f04d169fcc9bde0b547d1c205219b7157e07ded9c5aff03e0637cb3ed7" | |
1177 | version = "0.7.13" | |
1178 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1179 | checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" | |
1246 | 1180 | dependencies = [ |
1247 | 1181 | "libc", |
1248 | 1182 | "log", |
1253 | 1187 | |
1254 | 1188 | [[package]] |
1255 | 1189 | name = "miow" |
1256 | version = "0.3.6" | |
1257 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1258 | checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" | |
1259 | dependencies = [ | |
1260 | "socket2", | |
1190 | version = "0.3.7" | |
1191 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1192 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" | |
1193 | dependencies = [ | |
1261 | 1194 | "winapi", |
1262 | 1195 | ] |
1263 | 1196 | |
1280 | 1213 | ] |
1281 | 1214 | |
1282 | 1215 | [[package]] |
1283 | name = "nb-connect" | |
1284 | version = "1.0.3" | |
1285 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1286 | checksum = "670361df1bc2399ee1ff50406a0d422587dd3bb0da596e1978fe8e05dabddf4f" | |
1287 | dependencies = [ | |
1288 | "libc", | |
1289 | "socket2", | |
1290 | ] | |
1291 | ||
1292 | [[package]] | |
1293 | 1216 | name = "new_debug_unreachable" |
1294 | 1217 | version = "1.0.4" |
1295 | 1218 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1297 | 1220 | |
1298 | 1221 | [[package]] |
1299 | 1222 | name = "nix" |
1300 | version = "0.18.0" | |
1301 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1302 | checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" | |
1223 | version = "0.20.0" | |
1224 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1225 | checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" | |
1303 | 1226 | dependencies = [ |
1304 | 1227 | "bitflags", |
1305 | 1228 | "cc", |
1306 | "cfg-if 0.1.10", | |
1229 | "cfg-if", | |
1307 | 1230 | "libc", |
1308 | 1231 | ] |
1309 | 1232 | |
1349 | 1272 | |
1350 | 1273 | [[package]] |
1351 | 1274 | name = "once_cell" |
1352 | version = "1.5.2" | |
1353 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1354 | checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" | |
1275 | version = "1.8.0" | |
1276 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1277 | checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" | |
1355 | 1278 | |
1356 | 1279 | [[package]] |
1357 | 1280 | name = "openssl" |
1358 | version = "0.10.32" | |
1359 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1360 | checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70" | |
1281 | version = "0.10.34" | |
1282 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1283 | checksum = "6d7830286ad6a3973c0f1d9b73738f69c76b739301d0229c4b96501695cbe4c8" | |
1361 | 1284 | dependencies = [ |
1362 | 1285 | "bitflags", |
1363 | "cfg-if 1.0.0", | |
1286 | "cfg-if", | |
1364 | 1287 | "foreign-types", |
1365 | "lazy_static", | |
1366 | "libc", | |
1288 | "libc", | |
1289 | "once_cell", | |
1367 | 1290 | "openssl-sys", |
1368 | 1291 | ] |
1369 | 1292 | |
1370 | 1293 | [[package]] |
1371 | 1294 | name = "openssl-probe" |
1372 | version = "0.1.2" | |
1373 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1374 | checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" | |
1295 | version = "0.1.4" | |
1296 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1297 | checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" | |
1375 | 1298 | |
1376 | 1299 | [[package]] |
1377 | 1300 | name = "openssl-src" |
1378 | version = "111.13.0+1.1.1i" | |
1379 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1380 | checksum = "045e4dc48af57aad93d665885789b43222ae26f4886494da12d1ed58d309dcb6" | |
1301 | version = "111.15.0+1.1.1k" | |
1302 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1303 | checksum = "b1a5f6ae2ac04393b217ea9f700cd04fa9bf3d93fae2872069f3d15d908af70a" | |
1381 | 1304 | dependencies = [ |
1382 | 1305 | "cc", |
1383 | 1306 | ] |
1384 | 1307 | |
1385 | 1308 | [[package]] |
1386 | 1309 | name = "openssl-sys" |
1387 | version = "0.9.60" | |
1388 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1389 | checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6" | |
1310 | version = "0.9.63" | |
1311 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1312 | checksum = "b6b0d6fb7d80f877617dfcb014e605e2b5ab2fb0afdf27935219bb6bd984cb98" | |
1390 | 1313 | dependencies = [ |
1391 | 1314 | "autocfg", |
1392 | 1315 | "cc", |
1419 | 1342 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1420 | 1343 | checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" |
1421 | 1344 | dependencies = [ |
1422 | "cfg-if 1.0.0", | |
1345 | "cfg-if", | |
1423 | 1346 | "instant", |
1424 | 1347 | "libc", |
1425 | "redox_syscall 0.2.5", | |
1348 | "redox_syscall", | |
1426 | 1349 | "smallvec", |
1427 | 1350 | "winapi", |
1428 | 1351 | ] |
1454 | 1377 | |
1455 | 1378 | [[package]] |
1456 | 1379 | name = "pico-args" |
1457 | version = "0.3.4" | |
1458 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1459 | checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1" | |
1380 | version = "0.4.2" | |
1381 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1382 | checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" | |
1460 | 1383 | |
1461 | 1384 | [[package]] |
1462 | 1385 | name = "pin-project" |
1463 | version = "1.0.5" | |
1464 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1465 | checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63" | |
1386 | version = "1.0.7" | |
1387 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1388 | checksum = "c7509cc106041c40a4518d2af7a61530e1eed0e6285296a3d8c5472806ccc4a4" | |
1466 | 1389 | dependencies = [ |
1467 | 1390 | "pin-project-internal", |
1468 | 1391 | ] |
1469 | 1392 | |
1470 | 1393 | [[package]] |
1471 | 1394 | name = "pin-project-internal" |
1472 | version = "1.0.5" | |
1473 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1474 | checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" | |
1395 | version = "1.0.7" | |
1396 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1397 | checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f" | |
1475 | 1398 | dependencies = [ |
1476 | 1399 | "proc-macro2", |
1477 | 1400 | "quote", |
1480 | 1403 | |
1481 | 1404 | [[package]] |
1482 | 1405 | name = "pin-project-lite" |
1483 | version = "0.2.4" | |
1484 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1485 | checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" | |
1406 | version = "0.2.6" | |
1407 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1408 | checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" | |
1486 | 1409 | |
1487 | 1410 | [[package]] |
1488 | 1411 | name = "pin-utils" |
1498 | 1421 | |
1499 | 1422 | [[package]] |
1500 | 1423 | name = "polling" |
1501 | version = "2.0.2" | |
1502 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1503 | checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4" | |
1504 | dependencies = [ | |
1505 | "cfg-if 0.1.10", | |
1424 | version = "2.1.0" | |
1425 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1426 | checksum = "92341d779fa34ea8437ef4d82d440d5e1ce3f3ff7f824aa64424cd481f9a1f25" | |
1427 | dependencies = [ | |
1428 | "cfg-if", | |
1506 | 1429 | "libc", |
1507 | 1430 | "log", |
1508 | "wepoll-sys", | |
1431 | "wepoll-ffi", | |
1509 | 1432 | "winapi", |
1510 | 1433 | ] |
1511 | 1434 | |
1523 | 1446 | |
1524 | 1447 | [[package]] |
1525 | 1448 | name = "predicates" |
1526 | version = "1.0.7" | |
1527 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1528 | checksum = "eeb433456c1a57cc93554dea3ce40b4c19c4057e41c55d4a0f3d84ea71c325aa" | |
1449 | version = "1.0.8" | |
1450 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1451 | checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df" | |
1529 | 1452 | dependencies = [ |
1530 | 1453 | "difference", |
1531 | 1454 | "float-cmp", |
1551 | 1474 | ] |
1552 | 1475 | |
1553 | 1476 | [[package]] |
1554 | name = "proc-macro-error" | |
1555 | version = "1.0.4" | |
1556 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1557 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" | |
1558 | dependencies = [ | |
1559 | "proc-macro-error-attr", | |
1560 | "proc-macro2", | |
1561 | "quote", | |
1562 | "syn", | |
1563 | "version_check", | |
1564 | ] | |
1565 | ||
1566 | [[package]] | |
1567 | name = "proc-macro-error-attr" | |
1568 | version = "1.0.4" | |
1569 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1570 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" | |
1571 | dependencies = [ | |
1572 | "proc-macro2", | |
1573 | "quote", | |
1574 | "version_check", | |
1575 | ] | |
1576 | ||
1577 | [[package]] | |
1578 | 1477 | name = "proc-macro-hack" |
1579 | 1478 | version = "0.5.19" |
1580 | 1479 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1588 | 1487 | |
1589 | 1488 | [[package]] |
1590 | 1489 | name = "proc-macro2" |
1591 | version = "1.0.24" | |
1592 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1593 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" | |
1490 | version = "1.0.27" | |
1491 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1492 | checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" | |
1594 | 1493 | dependencies = [ |
1595 | 1494 | "unicode-xid", |
1596 | 1495 | ] |
1627 | 1526 | |
1628 | 1527 | [[package]] |
1629 | 1528 | name = "rand_chacha" |
1630 | version = "0.3.0" | |
1631 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1632 | checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" | |
1529 | version = "0.3.1" | |
1530 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1531 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" | |
1633 | 1532 | dependencies = [ |
1634 | 1533 | "ppv-lite86", |
1635 | 1534 | "rand_core", |
1641 | 1540 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1642 | 1541 | checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" |
1643 | 1542 | dependencies = [ |
1644 | "getrandom 0.2.2", | |
1543 | "getrandom", | |
1645 | 1544 | ] |
1646 | 1545 | |
1647 | 1546 | [[package]] |
1655 | 1554 | |
1656 | 1555 | [[package]] |
1657 | 1556 | name = "redox_syscall" |
1658 | version = "0.1.57" | |
1659 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1660 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" | |
1661 | ||
1662 | [[package]] | |
1663 | name = "redox_syscall" | |
1664 | version = "0.2.5" | |
1665 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1666 | checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" | |
1557 | version = "0.2.8" | |
1558 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1559 | checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" | |
1667 | 1560 | dependencies = [ |
1668 | 1561 | "bitflags", |
1669 | 1562 | ] |
1670 | 1563 | |
1671 | 1564 | [[package]] |
1672 | 1565 | name = "redox_users" |
1673 | version = "0.3.5" | |
1674 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1675 | checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" | |
1676 | dependencies = [ | |
1677 | "getrandom 0.1.16", | |
1678 | "redox_syscall 0.1.57", | |
1679 | "rust-argon2", | |
1566 | version = "0.4.0" | |
1567 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1568 | checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" | |
1569 | dependencies = [ | |
1570 | "getrandom", | |
1571 | "redox_syscall", | |
1680 | 1572 | ] |
1681 | 1573 | |
1682 | 1574 | [[package]] |
1683 | 1575 | name = "regex" |
1684 | version = "1.4.3" | |
1685 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1686 | checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" | |
1576 | version = "1.5.4" | |
1577 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1578 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" | |
1687 | 1579 | dependencies = [ |
1688 | 1580 | "aho-corasick", |
1689 | 1581 | "memchr", |
1690 | 1582 | "regex-syntax", |
1691 | "thread_local", | |
1692 | 1583 | ] |
1693 | 1584 | |
1694 | 1585 | [[package]] |
1695 | 1586 | name = "regex-automata" |
1696 | version = "0.1.9" | |
1697 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1698 | checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" | |
1699 | dependencies = [ | |
1700 | "byteorder", | |
1701 | ] | |
1587 | version = "0.1.10" | |
1588 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1589 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" | |
1702 | 1590 | |
1703 | 1591 | [[package]] |
1704 | 1592 | name = "regex-syntax" |
1705 | version = "0.6.22" | |
1706 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1707 | checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" | |
1593 | version = "0.6.25" | |
1594 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1595 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" | |
1708 | 1596 | |
1709 | 1597 | [[package]] |
1710 | 1598 | name = "remove_dir_all" |
1717 | 1605 | |
1718 | 1606 | [[package]] |
1719 | 1607 | name = "reqwest" |
1720 | version = "0.11.1" | |
1721 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1722 | checksum = "0460542b551950620a3648c6aa23318ac6b3cd779114bd873209e6e8b5eb1c34" | |
1608 | version = "0.11.3" | |
1609 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1610 | checksum = "2296f2fac53979e8ccbc4a1136b25dcefd37be9ed7e4a1f6b05a6029c84ff124" | |
1723 | 1611 | dependencies = [ |
1724 | 1612 | "base64", |
1725 | 1613 | "bytes", |
1752 | 1640 | |
1753 | 1641 | [[package]] |
1754 | 1642 | name = "rlimit" |
1755 | version = "0.5.3" | |
1756 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1757 | checksum = "6e7148757b4951f04391d2b301b2e3597d504c4d2434212d542b73c1a6b3f847" | |
1758 | dependencies = [ | |
1759 | "libc", | |
1760 | ] | |
1761 | ||
1762 | [[package]] | |
1763 | name = "rust-argon2" | |
1764 | version = "0.8.3" | |
1765 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1766 | checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" | |
1767 | dependencies = [ | |
1768 | "base64", | |
1769 | "blake2b_simd", | |
1770 | "constant_time_eq", | |
1771 | "crossbeam-utils", | |
1772 | ] | |
1643 | version = "0.5.4" | |
1644 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1645 | checksum = "81a9ed03edbed449d6897c2092c71ab5f7b5fb80f6f0b1a3ed6d40a6f9fc0720" | |
1646 | dependencies = [ | |
1647 | "libc", | |
1648 | ] | |
1649 | ||
1650 | [[package]] | |
1651 | name = "rustversion" | |
1652 | version = "1.0.5" | |
1653 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1654 | checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" | |
1773 | 1655 | |
1774 | 1656 | [[package]] |
1775 | 1657 | name = "ryu" |
1795 | 1677 | |
1796 | 1678 | [[package]] |
1797 | 1679 | name = "security-framework" |
1798 | version = "2.0.0" | |
1799 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1800 | checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" | |
1680 | version = "2.3.1" | |
1681 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1682 | checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" | |
1801 | 1683 | dependencies = [ |
1802 | 1684 | "bitflags", |
1803 | 1685 | "core-foundation", |
1808 | 1690 | |
1809 | 1691 | [[package]] |
1810 | 1692 | name = "security-framework-sys" |
1811 | version = "2.0.0" | |
1812 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1813 | checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b" | |
1693 | version = "2.3.0" | |
1694 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1695 | checksum = "7e4effb91b4b8b6fb7732e670b6cee160278ff8e6bf485c7805d9e319d76e284" | |
1814 | 1696 | dependencies = [ |
1815 | 1697 | "core-foundation-sys", |
1816 | 1698 | "libc", |
1818 | 1700 | |
1819 | 1701 | [[package]] |
1820 | 1702 | name = "serde" |
1821 | version = "1.0.123" | |
1822 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1823 | checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" | |
1703 | version = "1.0.126" | |
1704 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1705 | checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" | |
1824 | 1706 | dependencies = [ |
1825 | 1707 | "serde_derive", |
1826 | 1708 | ] |
1827 | 1709 | |
1828 | 1710 | [[package]] |
1829 | 1711 | name = "serde_derive" |
1830 | version = "1.0.123" | |
1831 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1832 | checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" | |
1712 | version = "1.0.126" | |
1713 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1714 | checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" | |
1833 | 1715 | dependencies = [ |
1834 | 1716 | "proc-macro2", |
1835 | 1717 | "quote", |
1838 | 1720 | |
1839 | 1721 | [[package]] |
1840 | 1722 | name = "serde_json" |
1841 | version = "1.0.62" | |
1842 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1843 | checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486" | |
1723 | version = "1.0.64" | |
1724 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1725 | checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" | |
1844 | 1726 | dependencies = [ |
1845 | 1727 | "itoa", |
1846 | 1728 | "ryu", |
1871 | 1753 | |
1872 | 1754 | [[package]] |
1873 | 1755 | name = "signal-hook" |
1874 | version = "0.1.17" | |
1875 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1876 | checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" | |
1756 | version = "0.3.9" | |
1757 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1758 | checksum = "470c5a6397076fae0094aaf06a08e6ba6f37acb77d3b1b91ea92b4d6c8650c39" | |
1759 | dependencies = [ | |
1760 | "libc", | |
1761 | "signal-hook-registry", | |
1762 | ] | |
1763 | ||
1764 | [[package]] | |
1765 | name = "signal-hook-mio" | |
1766 | version = "0.2.1" | |
1767 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1768 | checksum = "29fd5867f1c4f2c5be079aee7a2adf1152ebb04a4bc4d341f504b7dece607ed4" | |
1877 | 1769 | dependencies = [ |
1878 | 1770 | "libc", |
1879 | 1771 | "mio", |
1880 | "signal-hook-registry", | |
1881 | ] | |
1882 | ||
1883 | [[package]] | |
1884 | name = "signal-hook" | |
1885 | version = "0.3.4" | |
1886 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1887 | checksum = "780f5e3fe0c66f67197236097d89de1e86216f1f6fdeaf47c442f854ab46c240" | |
1888 | dependencies = [ | |
1889 | "libc", | |
1890 | "signal-hook-registry", | |
1772 | "signal-hook", | |
1891 | 1773 | ] |
1892 | 1774 | |
1893 | 1775 | [[package]] |
1894 | 1776 | name = "signal-hook-registry" |
1895 | version = "1.3.0" | |
1896 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1897 | checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" | |
1777 | version = "1.4.0" | |
1778 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1779 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" | |
1898 | 1780 | dependencies = [ |
1899 | 1781 | "libc", |
1900 | 1782 | ] |
1901 | 1783 | |
1902 | 1784 | [[package]] |
1903 | 1785 | name = "siphasher" |
1904 | version = "0.3.3" | |
1905 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1906 | checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7" | |
1786 | version = "0.3.5" | |
1787 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1788 | checksum = "cbce6d4507c7e4a3962091436e56e95290cb71fa302d0d270e32130b75fbff27" | |
1907 | 1789 | |
1908 | 1790 | [[package]] |
1909 | 1791 | name = "slab" |
1910 | version = "0.4.2" | |
1911 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1912 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" | |
1792 | version = "0.4.3" | |
1793 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1794 | checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" | |
1913 | 1795 | |
1914 | 1796 | [[package]] |
1915 | 1797 | name = "sluice" |
1930 | 1812 | |
1931 | 1813 | [[package]] |
1932 | 1814 | name = "socket2" |
1933 | version = "0.3.19" | |
1934 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1935 | checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" | |
1936 | dependencies = [ | |
1937 | "cfg-if 1.0.0", | |
1938 | "libc", | |
1939 | "winapi", | |
1940 | ] | |
1941 | ||
1942 | [[package]] | |
1943 | name = "spinning_top" | |
1944 | version = "0.2.2" | |
1945 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1946 | checksum = "7e529d73e80d64b5f2631f9035113347c578a1c9c7774b83a2b880788459ab36" | |
1947 | dependencies = [ | |
1948 | "lock_api", | |
1815 | version = "0.4.0" | |
1816 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1817 | checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" | |
1818 | dependencies = [ | |
1819 | "libc", | |
1820 | "winapi", | |
1949 | 1821 | ] |
1950 | 1822 | |
1951 | 1823 | [[package]] |
1968 | 1840 | |
1969 | 1841 | [[package]] |
1970 | 1842 | name = "syn" |
1971 | version = "1.0.60" | |
1972 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1973 | checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" | |
1843 | version = "1.0.73" | |
1844 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1845 | checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" | |
1974 | 1846 | dependencies = [ |
1975 | 1847 | "proc-macro2", |
1976 | 1848 | "quote", |
1983 | 1855 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1984 | 1856 | checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" |
1985 | 1857 | dependencies = [ |
1986 | "cfg-if 1.0.0", | |
1858 | "cfg-if", | |
1987 | 1859 | "libc", |
1988 | 1860 | "rand", |
1989 | "redox_syscall 0.2.5", | |
1861 | "redox_syscall", | |
1990 | 1862 | "remove_dir_all", |
1991 | 1863 | "winapi", |
1992 | 1864 | ] |
1993 | 1865 | |
1994 | 1866 | [[package]] |
1995 | 1867 | name = "term" |
1996 | version = "0.5.2" | |
1997 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1998 | checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" | |
1999 | dependencies = [ | |
2000 | "byteorder", | |
2001 | "dirs 1.0.5", | |
1868 | version = "0.7.0" | |
1869 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1870 | checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" | |
1871 | dependencies = [ | |
1872 | "dirs-next", | |
1873 | "rustversion", | |
2002 | 1874 | "winapi", |
2003 | 1875 | ] |
2004 | 1876 | |
2013 | 1885 | |
2014 | 1886 | [[package]] |
2015 | 1887 | name = "terminal_size" |
2016 | version = "0.1.16" | |
2017 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2018 | checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406" | |
1888 | version = "0.1.17" | |
1889 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1890 | checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" | |
2019 | 1891 | dependencies = [ |
2020 | 1892 | "libc", |
2021 | 1893 | "winapi", |
2032 | 1904 | |
2033 | 1905 | [[package]] |
2034 | 1906 | name = "thiserror" |
2035 | version = "1.0.23" | |
2036 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2037 | checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" | |
1907 | version = "1.0.25" | |
1908 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1909 | checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" | |
2038 | 1910 | dependencies = [ |
2039 | 1911 | "thiserror-impl", |
2040 | 1912 | ] |
2041 | 1913 | |
2042 | 1914 | [[package]] |
2043 | 1915 | name = "thiserror-impl" |
2044 | version = "1.0.23" | |
2045 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2046 | checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" | |
1916 | version = "1.0.25" | |
1917 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1918 | checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" | |
2047 | 1919 | dependencies = [ |
2048 | 1920 | "proc-macro2", |
2049 | 1921 | "quote", |
2051 | 1923 | ] |
2052 | 1924 | |
2053 | 1925 | [[package]] |
2054 | name = "thread_local" | |
2055 | version = "1.1.3" | |
2056 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2057 | checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" | |
2058 | dependencies = [ | |
2059 | "once_cell", | |
2060 | ] | |
2061 | ||
2062 | [[package]] | |
2063 | 1926 | name = "tiny-keccak" |
2064 | 1927 | version = "2.0.2" |
2065 | 1928 | source = "registry+https://github.com/rust-lang/crates.io-index" |
2070 | 1933 | |
2071 | 1934 | [[package]] |
2072 | 1935 | name = "tinyvec" |
2073 | version = "1.1.1" | |
2074 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2075 | checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" | |
1936 | version = "1.2.0" | |
1937 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1938 | checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" | |
2076 | 1939 | dependencies = [ |
2077 | 1940 | "tinyvec_macros", |
2078 | 1941 | ] |
2085 | 1948 | |
2086 | 1949 | [[package]] |
2087 | 1950 | name = "tokio" |
2088 | version = "1.2.0" | |
2089 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2090 | checksum = "e8190d04c665ea9e6b6a0dc45523ade572c088d2e6566244c1122671dbf4ae3a" | |
1951 | version = "1.6.2" | |
1952 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1953 | checksum = "aea337f72e96efe29acc234d803a5981cd9a2b6ed21655cd7fc21cfe021e8ec7" | |
2091 | 1954 | dependencies = [ |
2092 | 1955 | "autocfg", |
2093 | 1956 | "bytes", |
2105 | 1968 | |
2106 | 1969 | [[package]] |
2107 | 1970 | name = "tokio-macros" |
2108 | version = "1.1.0" | |
2109 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2110 | checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" | |
1971 | version = "1.2.0" | |
1972 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1973 | checksum = "c49e3df43841dafb86046472506755d8501c5615673955f6aa17181125d13c37" | |
2111 | 1974 | dependencies = [ |
2112 | 1975 | "proc-macro2", |
2113 | 1976 | "quote", |
2138 | 2001 | |
2139 | 2002 | [[package]] |
2140 | 2003 | name = "tokio-stream" |
2141 | version = "0.1.3" | |
2142 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2143 | checksum = "1981ad97df782ab506a1f43bf82c967326960d278acf3bf8279809648c3ff3ea" | |
2004 | version = "0.1.6" | |
2005 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2006 | checksum = "f8864d706fdb3cc0843a49647ac892720dac98a6eeb818b77190592cf4994066" | |
2144 | 2007 | dependencies = [ |
2145 | 2008 | "futures-core", |
2146 | 2009 | "pin-project-lite", |
2149 | 2012 | |
2150 | 2013 | [[package]] |
2151 | 2014 | name = "tokio-util" |
2152 | version = "0.6.3" | |
2153 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2154 | checksum = "ebb7cb2f00c5ae8df755b252306272cd1790d39728363936e01827e11f0b017b" | |
2015 | version = "0.6.7" | |
2016 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2017 | checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592" | |
2155 | 2018 | dependencies = [ |
2156 | 2019 | "bytes", |
2157 | 2020 | "futures-core", |
2178 | 2041 | |
2179 | 2042 | [[package]] |
2180 | 2043 | name = "tracing" |
2181 | version = "0.1.23" | |
2182 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2183 | checksum = "f7d40a22fd029e33300d8d89a5cc8ffce18bb7c587662f54629e94c9de5487f3" | |
2184 | dependencies = [ | |
2185 | "cfg-if 1.0.0", | |
2044 | version = "0.1.26" | |
2045 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2046 | checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" | |
2047 | dependencies = [ | |
2048 | "cfg-if", | |
2186 | 2049 | "log", |
2187 | 2050 | "pin-project-lite", |
2188 | 2051 | "tracing-attributes", |
2191 | 2054 | |
2192 | 2055 | [[package]] |
2193 | 2056 | name = "tracing-attributes" |
2194 | version = "0.1.12" | |
2195 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2196 | checksum = "43f080ea7e4107844ef4766459426fa2d5c1ada2e47edba05dc7fa99d9629f47" | |
2057 | version = "0.1.15" | |
2058 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2059 | checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" | |
2197 | 2060 | dependencies = [ |
2198 | 2061 | "proc-macro2", |
2199 | 2062 | "quote", |
2202 | 2065 | |
2203 | 2066 | [[package]] |
2204 | 2067 | name = "tracing-core" |
2205 | version = "0.1.17" | |
2206 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2207 | checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" | |
2068 | version = "0.1.18" | |
2069 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2070 | checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" | |
2208 | 2071 | dependencies = [ |
2209 | 2072 | "lazy_static", |
2210 | 2073 | ] |
2233 | 2096 | |
2234 | 2097 | [[package]] |
2235 | 2098 | name = "unicode-bidi" |
2236 | version = "0.3.4" | |
2237 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2238 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" | |
2099 | version = "0.3.5" | |
2100 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2101 | checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" | |
2239 | 2102 | dependencies = [ |
2240 | 2103 | "matches", |
2241 | 2104 | ] |
2242 | 2105 | |
2243 | 2106 | [[package]] |
2244 | 2107 | name = "unicode-normalization" |
2245 | version = "0.1.17" | |
2246 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2247 | checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" | |
2108 | version = "0.1.19" | |
2109 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2110 | checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" | |
2248 | 2111 | dependencies = [ |
2249 | 2112 | "tinyvec", |
2250 | 2113 | ] |
2257 | 2120 | |
2258 | 2121 | [[package]] |
2259 | 2122 | name = "unicode-xid" |
2260 | version = "0.2.1" | |
2261 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2262 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" | |
2123 | version = "0.2.2" | |
2124 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2125 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" | |
2263 | 2126 | |
2264 | 2127 | [[package]] |
2265 | 2128 | name = "url" |
2266 | version = "2.2.0" | |
2267 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2268 | checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" | |
2129 | version = "2.2.2" | |
2130 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2131 | checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" | |
2269 | 2132 | dependencies = [ |
2270 | 2133 | "form_urlencoded", |
2271 | 2134 | "idna", |
2279 | 2142 | source = "registry+https://github.com/rust-lang/crates.io-index" |
2280 | 2143 | checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" |
2281 | 2144 | dependencies = [ |
2282 | "getrandom 0.2.2", | |
2145 | "getrandom", | |
2283 | 2146 | ] |
2284 | 2147 | |
2285 | 2148 | [[package]] |
2286 | 2149 | name = "value-bag" |
2287 | version = "1.0.0-alpha.6" | |
2288 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2289 | checksum = "6b676010e055c99033117c2343b33a40a30b91fecd6c49055ac9cd2d6c305ab1" | |
2150 | version = "1.0.0-alpha.7" | |
2151 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2152 | checksum = "dd320e1520f94261153e96f7534476ad869c14022aee1e59af7c778075d840ae" | |
2290 | 2153 | dependencies = [ |
2291 | 2154 | "ctor", |
2155 | "version_check", | |
2292 | 2156 | ] |
2293 | 2157 | |
2294 | 2158 | [[package]] |
2295 | 2159 | name = "vcpkg" |
2296 | version = "0.2.11" | |
2297 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2298 | checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" | |
2299 | ||
2300 | [[package]] | |
2301 | name = "vec-arena" | |
2302 | version = "1.0.0" | |
2303 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2304 | checksum = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d" | |
2160 | version = "0.2.13" | |
2161 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2162 | checksum = "025ce40a007e1907e58d5bc1a594def78e5573bb0b1160bc389634e8f12e4faa" | |
2305 | 2163 | |
2306 | 2164 | [[package]] |
2307 | 2165 | name = "vec_map" |
2311 | 2169 | |
2312 | 2170 | [[package]] |
2313 | 2171 | name = "version_check" |
2314 | version = "0.9.2" | |
2315 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2316 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" | |
2172 | version = "0.9.3" | |
2173 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2174 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" | |
2317 | 2175 | |
2318 | 2176 | [[package]] |
2319 | 2177 | name = "wait-timeout" |
2342 | 2200 | |
2343 | 2201 | [[package]] |
2344 | 2202 | name = "wasi" |
2345 | version = "0.9.0+wasi-snapshot-preview1" | |
2346 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2347 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" | |
2348 | ||
2349 | [[package]] | |
2350 | name = "wasi" | |
2351 | 2203 | version = "0.10.2+wasi-snapshot-preview1" |
2352 | 2204 | source = "registry+https://github.com/rust-lang/crates.io-index" |
2353 | 2205 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" |
2354 | 2206 | |
2355 | 2207 | [[package]] |
2356 | 2208 | name = "wasm-bindgen" |
2357 | version = "0.2.70" | |
2358 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2359 | checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" | |
2360 | dependencies = [ | |
2361 | "cfg-if 1.0.0", | |
2209 | version = "0.2.74" | |
2210 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2211 | checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" | |
2212 | dependencies = [ | |
2213 | "cfg-if", | |
2362 | 2214 | "serde", |
2363 | 2215 | "serde_json", |
2364 | 2216 | "wasm-bindgen-macro", |
2366 | 2218 | |
2367 | 2219 | [[package]] |
2368 | 2220 | name = "wasm-bindgen-backend" |
2369 | version = "0.2.70" | |
2370 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2371 | checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7" | |
2221 | version = "0.2.74" | |
2222 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2223 | checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" | |
2372 | 2224 | dependencies = [ |
2373 | 2225 | "bumpalo", |
2374 | 2226 | "lazy_static", |
2381 | 2233 | |
2382 | 2234 | [[package]] |
2383 | 2235 | name = "wasm-bindgen-futures" |
2384 | version = "0.4.20" | |
2385 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2386 | checksum = "3de431a2910c86679c34283a33f66f4e4abd7e0aec27b6669060148872aadf94" | |
2387 | dependencies = [ | |
2388 | "cfg-if 1.0.0", | |
2236 | version = "0.4.24" | |
2237 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2238 | checksum = "5fba7978c679d53ce2d0ac80c8c175840feb849a161664365d1287b41f2e67f1" | |
2239 | dependencies = [ | |
2240 | "cfg-if", | |
2389 | 2241 | "js-sys", |
2390 | 2242 | "wasm-bindgen", |
2391 | 2243 | "web-sys", |
2393 | 2245 | |
2394 | 2246 | [[package]] |
2395 | 2247 | name = "wasm-bindgen-macro" |
2396 | version = "0.2.70" | |
2397 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2398 | checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c" | |
2248 | version = "0.2.74" | |
2249 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2250 | checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" | |
2399 | 2251 | dependencies = [ |
2400 | 2252 | "quote", |
2401 | 2253 | "wasm-bindgen-macro-support", |
2403 | 2255 | |
2404 | 2256 | [[package]] |
2405 | 2257 | name = "wasm-bindgen-macro-support" |
2406 | version = "0.2.70" | |
2407 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2408 | checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" | |
2258 | version = "0.2.74" | |
2259 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2260 | checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" | |
2409 | 2261 | dependencies = [ |
2410 | 2262 | "proc-macro2", |
2411 | 2263 | "quote", |
2416 | 2268 | |
2417 | 2269 | [[package]] |
2418 | 2270 | name = "wasm-bindgen-shared" |
2419 | version = "0.2.70" | |
2420 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2421 | checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" | |
2271 | version = "0.2.74" | |
2272 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2273 | checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" | |
2422 | 2274 | |
2423 | 2275 | [[package]] |
2424 | 2276 | name = "web-sys" |
2425 | version = "0.3.47" | |
2426 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2427 | checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3" | |
2277 | version = "0.3.51" | |
2278 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2279 | checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" | |
2428 | 2280 | dependencies = [ |
2429 | 2281 | "js-sys", |
2430 | 2282 | "wasm-bindgen", |
2431 | 2283 | ] |
2432 | 2284 | |
2433 | 2285 | [[package]] |
2434 | name = "wepoll-sys" | |
2435 | version = "3.0.1" | |
2436 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2437 | checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff" | |
2286 | name = "wepoll-ffi" | |
2287 | version = "0.1.2" | |
2288 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
2289 | checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" | |
2438 | 2290 | dependencies = [ |
2439 | 2291 | "cc", |
2440 | 2292 | ] |
0 | 0 | [package] |
1 | 1 | name = "feroxbuster" |
2 | version = "2.2.1" | |
2 | version = "2.3.0" | |
3 | 3 | authors = ["Ben 'epi' Risher <[email protected]>"] |
4 | 4 | license = "MIT" |
5 | 5 | edition = "2018" |
18 | 18 | clap = "2.33" |
19 | 19 | regex = "1" |
20 | 20 | lazy_static = "1.4" |
21 | dirs = "3.0" | |
21 | 22 | |
22 | 23 | [dependencies] |
23 | futures = { version = "0.3"} | |
24 | tokio = { version = "1.2.0", features = ["full"] } | |
25 | tokio-util = {version = "0.6.3", features = ["codec"]} | |
24 | futures = { version = "0.3.14"} | |
25 | tokio = { version = "1.6", features = ["full"] } | |
26 | tokio-util = {version = "0.6.6", features = ["codec"]} | |
26 | 27 | log = "0.4" |
27 | env_logger = "0.8.3" | |
28 | reqwest = { version = "0.11.1", features = ["socks"] } | |
28 | env_logger = "0.8" | |
29 | reqwest = { version = "0.11", features = ["socks"] } | |
29 | 30 | clap = "2.33" |
30 | 31 | lazy_static = "1.4" |
31 | 32 | toml = "0.5" |
32 | 33 | serde = { version = "1.0", features = ["derive", "rc"] } |
33 | serde_json = "1.0.62" | |
34 | serde_json = "1.0.64" | |
34 | 35 | uuid = { version = "0.8", features = ["v4"] } |
35 | 36 | indicatif = "0.15" |
36 | 37 | console = "0.14" |
37 | 38 | openssl = { version = "0.10", features = ["vendored"] } |
38 | 39 | dirs = "3.0" |
39 | 40 | regex = "1" |
40 | crossterm = "0.19" | |
41 | rlimit = "0.5" | |
42 | ctrlc = "3.1" | |
41 | crossterm = "0.20" | |
42 | rlimit = "0.5.4" | |
43 | ctrlc = "3.1.9" | |
43 | 44 | fuzzyhash = "0.2.1" |
44 | 45 | anyhow = "1.0" |
45 | 46 | leaky-bucket = "0.10.0" |
46 | 47 | |
47 | 48 | [dev-dependencies] |
48 | 49 | tempfile = "3.1" |
49 | httpmock = "0.5.2" | |
50 | assert_cmd = "1.0.3" | |
51 | predicates = "1.0.7" | |
50 | httpmock = "0.5.8" | |
51 | assert_cmd = "1.0" | |
52 | predicates = "1.0.8" | |
52 | 53 | |
53 | 54 | [profile.release] |
54 | 55 | lto = true |
62 | 63 | assets = [ |
63 | 64 | ["target/release/feroxbuster", "/usr/bin/", "755"], |
64 | 65 | ["ferox-config.toml.example", "/etc/feroxbuster/ferox-config.toml", "644"], |
66 | ["shell_completions/feroxbuster.bash", "/usr/share/bash-completion/completions/feroxbuster.bash", "644"], | |
67 | ["shell_completions/feroxbuster.fish", "/usr/share/fish/completions/feroxbuster.fish", "644"], | |
68 | ["shell_completions/_feroxbuster", "/usr/share/zsh/vendor-completions/_feroxbuster", "644"], | |
65 | 69 | ] |
5 | 5 | datadir = $(datarootdir) |
6 | 6 | example_config = ferox-config.toml.example |
7 | 7 | config_file = ferox-config.toml |
8 | completion_dir = shell_completions | |
9 | completion_prefix = $(completion_dir)/$(BIN) | |
8 | 10 | |
11 | BIN=feroxbuster | |
9 | 12 | SHR_SOURCES = $(shell find src -type f -wholename '*src/*.rs') Cargo.toml Cargo.lock |
10 | 13 | |
11 | 14 | RELEASE = debug |
12 | 15 | DEBUG ?= 0 |
13 | ifeq (0,$(DEBUG)) | |
16 | ||
17 | ifeq (0, $(DEBUG)) | |
14 | 18 | ARGS = --release |
15 | 19 | RELEASE = release |
16 | 20 | endif |
22 | 26 | |
23 | 27 | TARGET = target/$(RELEASE) |
24 | 28 | |
25 | .PHONY: all clean distclean install uninstall update | |
26 | ||
27 | BIN=feroxbuster | |
28 | DESKTOP=$(APPID).desktop | |
29 | .PHONY: all clean install uninstall test update | |
29 | 30 | |
30 | 31 | all: cli |
32 | cli: $(TARGET)/$(BIN) $(TARGET)/$(BIN).1.gz $(SHR_SOURCES) | |
33 | install: all install-cli | |
31 | 34 | |
32 | cli: $(TARGET)/$(BIN) $(TARGET)/$(BIN).1.gz $(SHR_SOURCES) | |
35 | verify: | |
36 | cargo fmt | |
37 | cargo clippy --all-targets --all-features -- -D warnings -A clippy::mutex-atomic | |
38 | cargo test | |
33 | 39 | |
34 | 40 | clean: |
35 | 41 | cargo clean |
36 | 42 | |
37 | distclean: clean | |
38 | rm -rf .cargo vendor Cargo.lock vendor.tar | |
39 | ||
40 | 43 | vendor: vendor.tar |
41 | 44 | |
42 | 45 | vendor.tar: |
43 | mkdir -p .cargo | |
44 | cargo vendor | head -n -1 > .cargo/config | |
45 | echo 'directory = "vendor"' >> .cargo/config | |
46 | cargo vendor | |
46 | 47 | tar pcf vendor.tar vendor |
47 | 48 | rm -rf vendor |
48 | 49 | |
49 | 50 | install-cli: cli |
50 | install -Dm 0755 "$(TARGET)/$(BIN)" "$(DESTDIR)$(bindir)/$(BIN)" | |
51 | install -Dm 0644 "$(completion_prefix).bash" "$(DESTDIR)/usr/share/bash-completion/completions/$(BIN).bash" | |
52 | install -Dm 0644 "$(completion_prefix).fish" "$(DESTDIR)/usr/share/fish/completions/$(BIN).fish" | |
53 | install -Dm 0644 "$(completion_dir)/_$(BIN)" "$(DESTDIR)/usr/share/zsh/vendor-completions/_$(BIN)" | |
54 | install -sDm 0755 "$(TARGET)/$(BIN)" "$(DESTDIR)$(bindir)/$(BIN)" | |
51 | 55 | install -Dm 0644 "$(TARGET)/$(BIN).1.gz" "$(DESTDIR)$(datadir)/man/man1/$(BIN).1.gz" |
52 | install -Dm 0644 "$(example_config)" "/etc/$(BIN)/$(config_File)" | |
56 | install -Dm 0644 "$(example_config)" "$(DESTDIR)/etc/$(BIN)/$(config_file)" | |
53 | 57 | |
54 | install: all install-cli | |
55 | ||
56 | uninstall-cli: | |
58 | uninstall: | |
57 | 59 | rm -f "$(DESTDIR)$(bindir)/$(BIN)" |
58 | 60 | rm -f "$(DESTDIR)$(datadir)/man/man1/$(BIN).1.gz" |
59 | rm -rf "/etc/$(BIN)/" | |
60 | ||
61 | uninstall: uninstall-cli | |
62 | ||
63 | update: | |
64 | cargo update | |
61 | rm -rf "$(DESTDIR)/etc/$(BIN)/" | |
62 | rm -f "$(DESTDIR)/usr/share/bash-completion/completions/$(BIN).bash" | |
63 | rm -f "$(DESTDIR)/usr/share/zsh/vendor-completions/_$(BIN)" | |
64 | rm -f "$(DESTDIR)/usr/share/fish/completions/$(BIN).fish" | |
65 | 65 | |
66 | 66 | extract: |
67 | ifeq ($(VENDORED),1) | |
67 | ifeq (1, $(VENDORED)) | |
68 | 68 | tar pxf vendor.tar |
69 | 69 | endif |
70 | 70 | |
71 | 71 | $(TARGET)/$(BIN): extract |
72 | cargo build --manifest-path Cargo.toml $(ARGS) | |
72 | mkdir -p .cargo | |
73 | cp debian/cargo.config .cargo/config.toml | |
74 | cargo build $(ARGS) | |
73 | 75 | |
74 | 76 | $(TARGET)/$(BIN).1.gz: $(TARGET)/$(BIN) |
75 | 77 | help2man --no-info $< | gzip -c > [email protected] |
69 | 69 | - [Snap Install](#snap-install) |
70 | 70 | - [Homebrew on MacOS and Linux](#homebrew-on-macos-and-linux) |
71 | 71 | - [Cargo Install](#cargo-install) |
72 | - [Kali Install](#kali-install) | |
72 | 73 | - [apt Install](#apt-install) |
73 | 74 | - [AUR Install](#aur-install) |
74 | 75 | - [Docker Install](#docker-install) |
105 | 106 | - [Silence all Output or Be Kinda Quiet (new in `v2.0.0`)](#silence-all-output-or-be-kinda-quiet-new-in-v200) |
106 | 107 | - [Auto-tune or Auto-bail from Scans (new in `v2.1.0`)](#auto-tune-or-auto-bail-from-scans-new-in-v210) |
107 | 108 | - [Run Scans in Parallel (new in `v2.2.0`)](#run-scans-in-parallel-new-in-v220) |
109 | - [Prevent Specific Domain/Directory Scans aka a Deny List (new in `v2.3.0`)](#prevent-specific-domaindirectory-scans-aka-a-deny-list-new-in-v230) | |
108 | 110 | - [Comparison w/ Similar Tools](#-comparison-w-similar-tools) |
109 | 111 | - [Common Problems/Issues (FAQ)](#-common-problemsissues-faq) |
110 | 112 | - [No file descriptors available](#no-file-descriptors-available) |
117 | 119 | |
118 | 120 | ### Download a Release |
119 | 121 | |
120 | Releases for multiple architectures can be found in the [Releases](https://github.com/epi052/feroxbuster/releases) | |
121 | section. The latest release for each of the following systems can be downloaded and executed as shown below. | |
122 | Releases for `armv7`, `aarch64`, and an `x86_64 .deb` can be found in the [Releases](https://github.com/epi052/feroxbuster/releases) section. | |
123 | ||
124 | All other OS/architecture combinations can be installed dynamically using one of the methods shown below. | |
122 | 125 | |
123 | 126 | #### Linux (32 and 64-bit) & MacOS |
124 | 127 | |
193 | 196 | cargo install feroxbuster |
194 | 197 | ``` |
195 | 198 | |
199 | ### Kali Install | |
200 | ||
201 | 🥳 `feroxbuster` was recently added to the official Kali Linux repos 🥳 | |
202 | ||
203 | If you're using kali, this is the preferred install method. Installing from the repos adds a [**ferox-config.toml**](#ferox-config.toml) in `/etc/feroxbuster/`, adds command completion for bash, fish, and zsh, includes a man page entry, and installs `feroxbuster` itself. | |
204 | ||
205 | ``` | |
206 | sudo apt update && sudo apt install -y feroxbuster | |
207 | ``` | |
208 | ||
196 | 209 | ### apt Install |
197 | 210 | |
198 | 211 | Download `feroxbuster_amd64.deb` from the [Releases](https://github.com/epi052/feroxbuster/releases) section. After |
210 | 223 | |
211 | 224 | ``` |
212 | 225 | yay -S feroxbuster-git |
226 | ``` | |
227 | ||
228 | ### BlackArch install | |
229 | ||
230 | Install `feroxbuster` on BlackArch Linux: | |
231 | ||
232 | ``` | |
233 | pacman -S feroxbuster | |
213 | 234 | ``` |
214 | 235 | |
215 | 236 | ### Docker Install |
395 | 416 | # dont_filter = true |
396 | 417 | # extract_links = true |
397 | 418 | # depth = 1 |
419 | # url_denylist = ["https://dont-scan-me.com/"] | |
398 | 420 | # filter_size = [5174] |
399 | 421 | # filter_regex = ["^ignore me$"] |
400 | 422 | # filter_similar = ["https://somesite.com/soft404"] |
428 | 450 | feroxbuster [FLAGS] [OPTIONS] --url <URL>... |
429 | 451 | |
430 | 452 | FLAGS: |
431 | -f, --add-slash Append / to each request | |
432 | --auto-bail Automatically stop scanning when an excessive amount of errors are encountered | |
433 | --auto-tune Automatically lower scan rate when an excessive amount of errors are encountered | |
434 | -D, --dont-filter Don't auto-filter wildcard responses | |
435 | -e, --extract-links Extract links from response body (html, javascript, etc...); make new requests based on | |
436 | findings (default: false) | |
437 | -h, --help Prints help information | |
438 | -k, --insecure Disables TLS certificate validation | |
439 | --json Emit JSON logs to --output and --debug-log instead of normal text | |
440 | -n, --no-recursion Do not scan recursively | |
441 | -q, --quiet Hide progress bars and banner (good for tmux windows w/ notifications) | |
442 | -r, --redirects Follow redirects | |
443 | --silent Only print URLs + turn off logging (good for piping a list of urls to other commands) | |
444 | --stdin Read url(s) from STDIN | |
445 | -V, --version Prints version information | |
446 | -v, --verbosity Increase verbosity level (use -vv or more for greater effect. [CAUTION] 4 -v's is probably | |
447 | too much) | |
453 | -f, --add-slash | |
454 | Append / to each request | |
455 | ||
456 | --auto-bail | |
457 | Automatically stop scanning when an excessive amount of errors are encountered | |
458 | ||
459 | --auto-tune | |
460 | Automatically lower scan rate when an excessive amount of errors are encountered | |
461 | ||
462 | -D, --dont-filter | |
463 | Don't auto-filter wildcard responses | |
464 | ||
465 | -e, --extract-links | |
466 | Extract links from response body (html, javascript, etc...); make new requests based on findings (default: | |
467 | false) | |
468 | -h, --help | |
469 | Prints help information | |
470 | ||
471 | -k, --insecure | |
472 | Disables TLS certificate validation | |
473 | ||
474 | --json | |
475 | Emit JSON logs to --output and --debug-log instead of normal text | |
476 | ||
477 | -n, --no-recursion | |
478 | Do not scan recursively | |
479 | ||
480 | -q, --quiet | |
481 | Hide progress bars and banner (good for tmux windows w/ notifications) | |
482 | ||
483 | -r, --redirects | |
484 | Follow redirects | |
485 | ||
486 | --silent | |
487 | Only print URLs + turn off logging (good for piping a list of urls to other commands) | |
488 | ||
489 | --stdin | |
490 | Read url(s) from STDIN | |
491 | ||
492 | -V, --version | |
493 | Prints version information | |
494 | ||
495 | -v, --verbosity | |
496 | Increase verbosity level (use -vv or more for greater effect. [CAUTION] 4 -v's is probably too much) | |
497 | ||
448 | 498 | |
449 | 499 | OPTIONS: |
450 | --debug-log <FILE> Output file to write log entries (use w/ --json for JSON entries) | |
500 | --debug-log <FILE> | |
501 | Output file to write log entries (use w/ --json for JSON entries) | |
502 | ||
451 | 503 | -d, --depth <RECURSION_DEPTH> |
452 | 504 | Maximum recursion depth, a depth of 0 is infinite recursion (default: 4) |
453 | 505 | |
454 | -x, --extensions <FILE_EXTENSION>... File extension(s) to search for (ex: -x php -x pdf js) | |
455 | -N, --filter-lines <LINES>... Filter out messages of a particular line count (ex: -N 20 -N 31,30) | |
506 | -x, --extensions <FILE_EXTENSION>... | |
507 | File extension(s) to search for (ex: -x php -x pdf js) | |
508 | ||
509 | -N, --filter-lines <LINES>... | |
510 | Filter out messages of a particular line count (ex: -N 20 -N 31,30) | |
511 | ||
456 | 512 | -X, --filter-regex <REGEX>... |
457 | 513 | Filter out messages via regular expression matching on the response's body (ex: -X '^ignore me$') |
458 | 514 | |
459 | 515 | --filter-similar-to <UNWANTED_PAGE>... |
460 | 516 | Filter out pages that are similar to the given page (ex. --filter-similar-to http://site.xyz/soft404) |
461 | 517 | |
462 | -S, --filter-size <SIZE>... Filter out messages of a particular size (ex: -S 5120 -S 4927,1970) | |
463 | -C, --filter-status <STATUS_CODE>... Filter out status codes (deny list) (ex: -C 200 -C 401) | |
464 | -W, --filter-words <WORDS>... Filter out messages of a particular word count (ex: -W 312 -W 91,82) | |
465 | -H, --headers <HEADER>... Specify HTTP headers (ex: -H Header:val 'stuff: things') | |
466 | -o, --output <FILE> Output file to write results to (use w/ --json for JSON entries) | |
518 | -S, --filter-size <SIZE>... | |
519 | Filter out messages of a particular size (ex: -S 5120 -S 4927,1970) | |
520 | ||
521 | -C, --filter-status <STATUS_CODE>... | |
522 | Filter out status codes (deny list) (ex: -C 200 -C 401) | |
523 | ||
524 | -W, --filter-words <WORDS>... | |
525 | Filter out messages of a particular word count (ex: -W 312 -W 91,82) | |
526 | ||
527 | -H, --headers <HEADER>... | |
528 | Specify HTTP headers (ex: -H Header:val 'stuff: things') | |
529 | ||
530 | -o, --output <FILE> | |
531 | Output file to write results to (use w/ --json for JSON entries) | |
532 | ||
467 | 533 | --parallel <PARALLEL_SCANS> |
468 | 534 | Run parallel feroxbuster instances (one child process per url passed via stdin) |
469 | 535 | |
470 | 536 | -p, --proxy <PROXY> |
471 | 537 | Proxy to use for requests (ex: http(s)://host:port, socks5(h)://host:port) |
472 | 538 | |
473 | -Q, --query <QUERY>... Specify URL query parameters (ex: -Q token=stuff -Q secret=key) | |
539 | -Q, --query <QUERY>... | |
540 | Specify URL query parameters (ex: -Q token=stuff -Q secret=key) | |
541 | ||
474 | 542 | --rate-limit <RATE_LIMIT> |
475 | 543 | Limit number of requests per second (per directory) (default: 0, i.e. no limit) |
476 | 544 | |
483 | 551 | --resume-from <STATE_FILE> |
484 | 552 | State file from which to resume a partially complete scan (ex. --resume-from ferox-1606586780.state) |
485 | 553 | |
486 | -L, --scan-limit <SCAN_LIMIT> Limit total number of concurrent scans (default: 0, i.e. no limit) | |
554 | -L, --scan-limit <SCAN_LIMIT> | |
555 | Limit total number of concurrent scans (default: 0, i.e. no limit) | |
556 | ||
487 | 557 | -s, --status-codes <STATUS_CODE>... |
488 | 558 | Status Codes to include (allow list) (default: 200 204 301 302 307 308 401 403 405) |
489 | 559 | |
490 | -t, --threads <THREADS> Number of concurrent threads (default: 50) | |
491 | --time-limit <TIME_SPEC> Limit total run time of all scans (ex: --time-limit 10m) | |
492 | -T, --timeout <SECONDS> Number of seconds before a request times out (default: 7) | |
493 | -u, --url <URL>... The target URL(s) (required, unless --stdin used) | |
494 | -a, --user-agent <USER_AGENT> Sets the User-Agent (default: feroxbuster/VERSION) | |
495 | -w, --wordlist <FILE> Path to the wordlist | |
560 | -t, --threads <THREADS> | |
561 | Number of concurrent threads (default: 50) | |
562 | ||
563 | --time-limit <TIME_SPEC> | |
564 | Limit total run time of all scans (ex: --time-limit 10m) | |
565 | ||
566 | -T, --timeout <SECONDS> | |
567 | Number of seconds before a request times out (default: 7) | |
568 | ||
569 | -u, --url <URL>... | |
570 | The target URL(s) (required, unless --stdin used) | |
571 | ||
572 | --dont-scan <URL>... | |
573 | URL(s) to exclude from recursion/scans | |
574 | ||
575 | -a, --user-agent <USER_AGENT> | |
576 | Sets the User-Agent (default: feroxbuster/VERSION) | |
577 | ||
578 | -w, --wordlist <FILE> | |
579 | Path to the wordlist | |
496 | 580 | ``` |
497 | 581 | |
498 | 582 | ## 📊 Scan's Display Explained |
815 | 899 | Using the menu is pretty simple: |
816 | 900 | - Press `ENTER` to view the menu |
817 | 901 | - Choose a scan to cancel by entering its scan index (`1`) |
818 | - more than one scan can be selected by using a comma-separated list (`1,2,3` ... etc) | |
902 | - more than one scan can be selected by using a comma-separated list of indexes and/or ranges (`1-4,8,9-13` ... etc) | |
819 | 903 | - Confirm selections, after which all non-cancelled scans will resume |
820 | ||
821 | Here is a short demonstration of cancelling two in-progress scans found via recursion. | |
904 | - To skip confirmation, simply add a `-f` somewhere in your input (`3-5 -f`) | |
905 | ||
906 | Here is a short demonstration of force cancelling a range of scans followed by a single scan with interactive prompt. | |
822 | 907 | |
823 | 908 | ![cancel-scan](img/cancel-scan.gif) |
824 | 909 | |
925 | 1010 | \_ feroxbuster --silent --extract-links --auto-bail -u https://target-ten |
926 | 1011 | ``` |
927 | 1012 | |
1013 | ### Prevent Specific Domain/Directory Scans aka a Deny List (new in `v2.3.0`) | |
1014 | ||
1015 | > This action is taken BEFORE a request is sent to the target, which differs from the filter-* options that are applied to responses | |
1016 | ||
1017 | Version 2.3.0 introduces the `--dont-scan` option. The values passed to `--dont-scan` act as a deny-list. The values | |
1018 | can be an entire domain (`http://some.domain`), a specific folder (`http://some.domain/js`), or a specific file | |
1019 | (`http://some.domain/some-application/stupid-page.php`) If a folder/domain is used any sub-folder/sub-file of the | |
1020 | url passed to `--dont-scan` will be blocked before it can be requested. | |
1021 | ||
1022 | For example, given the command | |
1023 | ||
1024 | ``` | |
1025 | ./feroxbuster -u http://some.domain --dont-scan http://some.domain/js | |
1026 | ``` | |
1027 | ||
1028 | `http://some.domain` will be scanned recursively, but any url path that begins with `/js/` will not be requested at all. | |
1029 | ||
1030 | A caveat to the sub-folder/sub-file rule is when the value passed to `--dont-scan` is a parent of the scan you want to | |
1031 | perform. When denying at a hierarchical level higher than your scan, only sub-files/sub-folders of your `-u|--stdin` | |
1032 | value(s) will be processed. | |
1033 | ||
1034 | ``` | |
1035 | ./feroxbuster -u http://some.domain/some-application --dont-scan http://some.domain/ | |
1036 | ``` | |
1037 | ||
1038 | In the command above, only `http://some.domain/some-application` and children of that directory found via recursion will | |
1039 | be scanned. Anything 'outside' of `/some-application` will not be scanned. | |
1040 | ||
928 | 1041 | ## 🧐 Comparison w/ Similar Tools |
929 | 1042 | |
930 | 1043 | There are quite a few similar tools for forced browsing/content discovery. Burp Suite Pro, Dirb, Dirbuster, etc... |
948 | 1061 | | fast | ✔ | ✔ | ✔ | |
949 | 1062 | | allows recursion | ✔ | | ✔ | |
950 | 1063 | | can specify query parameters | ✔ | | ✔ | |
951 | | SOCKS proxy support | ✔ | | | | |
1064 | | SOCKS proxy support | ✔ | | ✔ | | |
952 | 1065 | | multiple target scan (via stdin or multiple -u) | ✔ | | ✔ | |
953 | 1066 | | configuration file for default value override | ✔ | | ✔ | |
954 | 1067 | | can accept urls via STDIN as part of a pipeline | ✔ | | ✔ | |
975 | 1088 | | automatically tune scans based on errors/403s/429s (`v2.1.0`) | ✔ | | | |
976 | 1089 | | automatically stop scans based on errors/403s/429s (`v2.1.0`) | ✔ | | ✔ | |
977 | 1090 | | run scans in parallel (1 process per target) (`v2.2.0`) | ✔ | | | |
1091 | | prevent requests to given domain/folder/file (`v2.3.0`) | ✔ | | | | |
978 | 1092 | | **huge** number of other options | | | ✔ | |
979 | 1093 | |
980 | 1094 | Of note, there's another written-in-rust content discovery tool, [rustbuster](https://github.com/phra/rustbuster). I |
0 | use std::fs::{copy, create_dir_all, OpenOptions}; | |
1 | use std::io::{Read, Seek, SeekFrom, Write}; | |
0 | 2 | extern crate clap; |
3 | extern crate dirs; | |
1 | 4 | |
2 | 5 | use clap::Shell; |
3 | 6 | |
19 | 22 | for shell in &shells { |
20 | 23 | app.gen_completions("feroxbuster", *shell, outdir); |
21 | 24 | } |
25 | ||
26 | // 0xdf pointed out an oddity when tab-completing options that expect file paths, the fix we | |
27 | // landed on was to add -o plusdirs to the bash completion script. The following code aims to | |
28 | // automate that fix and have it present in all future builds | |
29 | let mut contents = String::new(); | |
30 | ||
31 | let mut bash_file = OpenOptions::new() | |
32 | .read(true) | |
33 | .write(true) | |
34 | .open(format!("{}/feroxbuster.bash", outdir)) | |
35 | .expect("Couldn't open bash completion script"); | |
36 | ||
37 | bash_file | |
38 | .read_to_string(&mut contents) | |
39 | .expect("Couldn't read bash completion script"); | |
40 | ||
41 | contents = contents.replace("default feroxbuster", "default -o plusdirs feroxbuster"); | |
42 | ||
43 | bash_file | |
44 | .seek(SeekFrom::Start(0)) | |
45 | .expect("Couldn't seek to position 0 in bash completion script"); | |
46 | ||
47 | bash_file | |
48 | .write_all(contents.as_bytes()) | |
49 | .expect("Couldn't write updated bash completion script to disk"); | |
50 | ||
51 | // hunter0x8 let me know that when installing via cargo, it would be nice if we dropped a | |
52 | // config file during the build process. The following code will place an example config in | |
53 | // the user's configuration directory | |
54 | // - linux: $XDG_CONFIG_HOME or $HOME/.config | |
55 | // - macOS: $HOME/Library/Application Support | |
56 | // - windows: {FOLDERID_RoamingAppData} | |
57 | let mut config_dir = dirs::config_dir().expect("Couldn't resolve user's config directory"); | |
58 | config_dir = config_dir.join("feroxbuster"); // $HOME/.config/feroxbuster | |
59 | ||
60 | if !config_dir.exists() { | |
61 | // recursively create the feroxbuster directory and all of its parent components if | |
62 | // they are missing | |
63 | if !config_dir.exists() { | |
64 | // recursively create the feroxbuster directory and all of its parent components if | |
65 | // they are missing | |
66 | if create_dir_all(&config_dir).is_err() { | |
67 | // only copy the config file when we're not running in the CI/CD pipeline | |
68 | // which fails with permission denied | |
69 | eprintln!("Couldn't create one or more directories needed to copy the config file"); | |
70 | return; | |
71 | } | |
72 | } | |
73 | } | |
74 | ||
75 | // hard-coding config name here to not rely on the crate we're building, if DEFAULT_CONFIG_NAME | |
76 | // ever changes, this will need to be updated | |
77 | let config_file = config_dir.join("ferox-config.toml"); | |
78 | ||
79 | if !config_file.exists() { | |
80 | // config file doesn't exist, add it to the config directory | |
81 | if copy("ferox-config.toml.example", config_file).is_err() { | |
82 | eprintln!("Couldn't copy example config into config directory"); | |
83 | } | |
84 | } | |
22 | 85 | } |
29 | 29 | # redirects = true |
30 | 30 | # insecure = true |
31 | 31 | # extensions = ["php", "html"] |
32 | # url_denylist = ["http://dont-scan.me", "https://also-not.me"] | |
32 | 33 | # no_recursion = true |
33 | 34 | # add_slash = true |
34 | 35 | # stdin = true |
Binary diff not shown
Binary diff not shown
2 | 2 | BASE_URL=https://github.com/epi052/feroxbuster/releases/latest/download |
3 | 3 | |
4 | 4 | MAC_ZIP=x86_64-macos-feroxbuster.zip |
5 | MAC_URL="${BASE_URL}/${MAC_ZIP}" | |
5 | MAC_URL="$BASE_URL/$MAC_ZIP" | |
6 | 6 | |
7 | 7 | LIN32_ZIP=x86-linux-feroxbuster.zip |
8 | LIN32_URL="${BASE_URL}/${LIN32_ZIP}" | |
8 | LIN32_URL="$BASE_URL/$LIN32_ZIP" | |
9 | 9 | |
10 | 10 | LIN64_ZIP=x86_64-linux-feroxbuster.zip |
11 | LIN64_URL="${BASE_URL}/${LIN64_ZIP}" | |
11 | LIN64_URL="$BASE_URL/$LIN64_ZIP" | |
12 | 12 | |
13 | 13 | EMOJI_URL=https://gist.github.com/epi052/8196b550ea51d0907ad4b93751b1b57d/raw/6112c9f32ae07922983fdc549c54fd3fb9a38e4c/NotoColorEmoji.ttf |
14 | 14 | |
15 | 15 | echo "[+] Installing feroxbuster!" |
16 | 16 | |
17 | if [[ "$(uname)" == "Darwin" ]]; then | |
18 | echo "[=] Found MacOS, downloading from ${MAC_URL}" | |
19 | ||
20 | curl -sLO "${MAC_URL}" | |
21 | unzip -o "${MAC_ZIP}" > /dev/null | |
22 | rm "${MAC_ZIP}" | |
23 | elif [[ "$(expr substr $(uname -s) 1 5)" == "Linux" ]]; then | |
24 | if [[ $(getconf LONG_BIT) == 32 ]]; then | |
25 | echo "[=] Found 32-bit Linux, downloading from ${LIN32_URL}" | |
26 | ||
27 | curl -sLO "${LIN32_URL}" | |
28 | unzip -o "${LIN32_ZIP}" > /dev/null | |
29 | rm "${LIN32_ZIP}" | |
30 | else | |
31 | echo "[=] Found 64-bit Linux, downloading from ${LIN64_URL}" | |
32 | ||
33 | curl -sLO "${LIN64_URL}" | |
34 | unzip -o "${LIN64_ZIP}" > /dev/null | |
35 | rm "${LIN64_ZIP}" | |
36 | fi | |
37 | ||
38 | if [[ -e ~/.fonts/NotoColorEmoji.ttf ]]; then | |
39 | echo "[=] Found Noto Emoji Font, skipping install" | |
40 | else | |
41 | echo "[=] Installing Noto Emoji Font" | |
42 | mkdir -p ~/.fonts | |
43 | pushd ~/.fonts 2>&1 >/dev/null | |
44 | ||
45 | curl -sLO "${EMOJI_URL}" | |
46 | ||
47 | fc-cache -f -v >/dev/null | |
48 | ||
49 | popd 2>&1 >/dev/null | |
50 | echo "[+] Noto Emoji Font installed" | |
51 | fi | |
17 | which unzip &>/dev/null | |
18 | if [ "$?" = "0" ]; then | |
19 | echo "[+] unzip found" | |
20 | else | |
21 | echo "[ ] unzip not found, exiting. " | |
22 | exit -1 | |
52 | 23 | fi |
53 | 24 | |
25 | if [[ "$(uname)" == "Darwin" ]]; then | |
26 | echo "[=] Found MacOS, downloading from $MAC_URL" | |
27 | ||
28 | curl -sLO "$MAC_URL" | |
29 | unzip -o "$MAC_ZIP" >/dev/null | |
30 | rm "$MAC_ZIP" | |
31 | elif [[ "$(expr substr $(uname -s) 1 5)" == "Linux" ]]; then | |
32 | if [[ $(getconf LONG_BIT) == 32 ]]; then | |
33 | echo "[=] Found 32-bit Linux, downloading from $LIN32_URL" | |
34 | ||
35 | curl -sLO "$LIN32_URL" | |
36 | unzip -o "$LIN32_ZIP" >/dev/null | |
37 | rm "$LIN32_ZIP" | |
38 | else | |
39 | echo "[=] Found 64-bit Linux, downloading from $LIN64_URL" | |
40 | ||
41 | curl -sLO "$LIN64_URL" | |
42 | unzip -o "$LIN64_ZIP" >/dev/null | |
43 | rm "$LIN64_ZIP" | |
44 | fi | |
45 | ||
46 | if [[ -e ~/.fonts/NotoColorEmoji.ttf ]]; then | |
47 | echo "[=] Found Noto Emoji Font, skipping install" | |
48 | else | |
49 | echo "[=] Installing Noto Emoji Font" | |
50 | mkdir -p ~/.fonts | |
51 | pushd ~/.fonts 2>&1 >/dev/null | |
52 | ||
53 | curl -sLO "$EMOJI_URL" | |
54 | ||
55 | fc-cache -f -v >/dev/null | |
56 | ||
57 | popd 2>&1 >/dev/null | |
58 | echo "[+] Noto Emoji Font installed" | |
59 | fi | |
60 | fi | |
54 | 61 | |
55 | 62 | chmod +x ./feroxbuster |
56 | 63 | |
57 | 64 | echo "[+] Installed feroxbuster version $(./feroxbuster -V)" |
58 | ||
59 | ||
60 |
40 | 40 | '--user-agent=[Sets the User-Agent (default: feroxbuster/VERSION)]' \ |
41 | 41 | '*-x+[File extension(s) to search for (ex: -x php -x pdf js)]' \ |
42 | 42 | '*--extensions=[File extension(s) to search for (ex: -x php -x pdf js)]' \ |
43 | '*--dont-scan=[URL(s) to exclude from recursion/scans]' \ | |
43 | 44 | '*-H+[Specify HTTP headers (ex: -H Header:val '\''stuff: things'\'')]' \ |
44 | 45 | '*--headers=[Specify HTTP headers (ex: -H Header:val '\''stuff: things'\'')]' \ |
45 | 46 | '*-Q+[Specify URL query parameters (ex: -Q token=stuff -Q secret=key)]' \ |
45 | 45 | [CompletionResult]::new('--user-agent', 'user-agent', [CompletionResultType]::ParameterName, 'Sets the User-Agent (default: feroxbuster/VERSION)') |
46 | 46 | [CompletionResult]::new('-x', 'x', [CompletionResultType]::ParameterName, 'File extension(s) to search for (ex: -x php -x pdf js)') |
47 | 47 | [CompletionResult]::new('--extensions', 'extensions', [CompletionResultType]::ParameterName, 'File extension(s) to search for (ex: -x php -x pdf js)') |
48 | [CompletionResult]::new('--dont-scan', 'dont-scan', [CompletionResultType]::ParameterName, 'URL(s) to exclude from recursion/scans') | |
48 | 49 | [CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Specify HTTP headers (ex: -H Header:val ''stuff: things'')') |
49 | 50 | [CompletionResult]::new('--headers', 'headers', [CompletionResultType]::ParameterName, 'Specify HTTP headers (ex: -H Header:val ''stuff: things'')') |
50 | 51 | [CompletionResult]::new('-Q', 'Q', [CompletionResultType]::ParameterName, 'Specify URL query parameters (ex: -Q token=stuff -Q secret=key)') |
19 | 19 | |
20 | 20 | case "${cmd}" in |
21 | 21 | feroxbuster) |
22 | opts=" -v -q -D -r -k -n -f -e -h -V -w -u -t -d -T -p -P -R -s -o -a -x -H -Q -S -X -W -N -C -L --verbosity --silent --quiet --auto-tune --auto-bail --json --dont-filter --redirects --insecure --no-recursion --add-slash --stdin --extract-links --help --version --wordlist --url --threads --depth --timeout --proxy --replay-proxy --replay-codes --status-codes --output --resume-from --debug-log --user-agent --extensions --headers --query --filter-size --filter-regex --filter-words --filter-lines --filter-status --filter-similar-to --scan-limit --parallel --rate-limit --time-limit " | |
22 | opts=" -v -q -D -r -k -n -f -e -h -V -w -u -t -d -T -p -P -R -s -o -a -x -H -Q -S -X -W -N -C -L --verbosity --silent --quiet --auto-tune --auto-bail --json --dont-filter --redirects --insecure --no-recursion --add-slash --stdin --extract-links --help --version --wordlist --url --threads --depth --timeout --proxy --replay-proxy --replay-codes --status-codes --output --resume-from --debug-log --user-agent --extensions --dont-scan --headers --query --filter-size --filter-regex --filter-words --filter-lines --filter-status --filter-similar-to --scan-limit --parallel --rate-limit --time-limit " | |
23 | 23 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then |
24 | 24 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) |
25 | 25 | return 0 |
127 | 127 | return 0 |
128 | 128 | ;; |
129 | 129 | -x) |
130 | COMPREPLY=($(compgen -f "${cur}")) | |
131 | return 0 | |
132 | ;; | |
133 | --dont-scan) | |
130 | 134 | COMPREPLY=($(compgen -f "${cur}")) |
131 | 135 | return 0 |
132 | 136 | ;; |
221 | 225 | esac |
222 | 226 | } |
223 | 227 | |
224 | complete -F _feroxbuster -o bashdefault -o default feroxbuster | |
228 | complete -F _feroxbuster -o bashdefault -o default -o plusdirs feroxbuster |
11 | 11 | complete -c feroxbuster -n "__fish_use_subcommand" -l debug-log -d 'Output file to write log entries (use w/ --json for JSON entries)' |
12 | 12 | complete -c feroxbuster -n "__fish_use_subcommand" -s a -l user-agent -d 'Sets the User-Agent (default: feroxbuster/VERSION)' |
13 | 13 | complete -c feroxbuster -n "__fish_use_subcommand" -s x -l extensions -d 'File extension(s) to search for (ex: -x php -x pdf js)' |
14 | complete -c feroxbuster -n "__fish_use_subcommand" -l dont-scan -d 'URL(s) to exclude from recursion/scans' | |
14 | 15 | complete -c feroxbuster -n "__fish_use_subcommand" -s H -l headers -d 'Specify HTTP headers (ex: -H Header:val \'stuff: things\')' |
15 | 16 | complete -c feroxbuster -n "__fish_use_subcommand" -s Q -l query -d 'Specify URL query parameters (ex: -Q token=stuff -Q secret=key)' |
16 | 17 | complete -c feroxbuster -n "__fish_use_subcommand" -s S -l filter-size -d 'Filter out messages of a particular size (ex: -S 5120 -S 4927,1970)' |
133 | 133 | /// represents Configuration.auto_bail |
134 | 134 | auto_bail: BannerEntry, |
135 | 135 | |
136 | /// represents Configuration.url_denylist | |
137 | url_denylist: Vec<BannerEntry>, | |
138 | ||
136 | 139 | /// current version of feroxbuster |
137 | 140 | pub(super) version: String, |
138 | 141 | |
145 | 148 | /// Create a new Banner from a Configuration and live targets |
146 | 149 | pub fn new(tgts: &[String], config: &Configuration) -> Self { |
147 | 150 | let mut targets = Vec::new(); |
151 | let mut url_denylist = Vec::new(); | |
148 | 152 | let mut code_filters = Vec::new(); |
149 | 153 | let mut replay_codes = Vec::new(); |
150 | 154 | let mut headers = Vec::new(); |
157 | 161 | |
158 | 162 | for target in tgts { |
159 | 163 | targets.push(BannerEntry::new("🎯", "Target Url", target)); |
164 | } | |
165 | ||
166 | for denied_url in &config.url_denylist { | |
167 | url_denylist.push(BannerEntry::new("🚫", "Don't Scan", &denied_url)); | |
160 | 168 | } |
161 | 169 | |
162 | 170 | let mut codes = vec![]; |
322 | 330 | rate_limit, |
323 | 331 | scan_limit, |
324 | 332 | time_limit, |
333 | url_denylist, | |
325 | 334 | config: cfg, |
326 | 335 | version: VERSION.to_string(), |
327 | 336 | update_status: UpdateStatus::Unknown, |
412 | 421 | writeln!(&mut writer, "{}", target)?; |
413 | 422 | } |
414 | 423 | |
424 | for denied_url in &self.url_denylist { | |
425 | writeln!(&mut writer, "{}", denied_url)?; | |
426 | } | |
427 | ||
415 | 428 | writeln!(&mut writer, "{}", self.threads)?; |
416 | 429 | writeln!(&mut writer, "{}", self.wordlist)?; |
417 | 430 | writeln!(&mut writer, "{}", self.status_codes)?; |
247 | 247 | /// Filter out response bodies that meet a certain threshold of similarity |
248 | 248 | #[serde(default)] |
249 | 249 | pub filter_similar: Vec<String>, |
250 | ||
251 | /// URLs that should never be scanned/recursed into | |
252 | #[serde(default)] | |
253 | pub url_denylist: Vec<String>, | |
250 | 254 | } |
251 | 255 | |
252 | 256 | impl Default for Configuration { |
303 | 307 | extensions: Vec::new(), |
304 | 308 | filter_size: Vec::new(), |
305 | 309 | filter_regex: Vec::new(), |
310 | url_denylist: Vec::new(), | |
306 | 311 | filter_line_count: Vec::new(), |
307 | 312 | filter_word_count: Vec::new(), |
308 | 313 | filter_status: Vec::new(), |
340 | 345 | /// - **user_agent**: `feroxbuster/VERSION` |
341 | 346 | /// - **insecure**: `false` (don't be insecure, i.e. don't allow invalid certs) |
342 | 347 | /// - **extensions**: `None` |
348 | /// - **url_denylist**: `None` | |
343 | 349 | /// - **filter_size**: `None` |
344 | 350 | /// - **filter_similar**: `None` |
345 | 351 | /// - **filter_regex**: `None` |
537 | 543 | config.extensions = arg.map(|val| val.to_string()).collect(); |
538 | 544 | } |
539 | 545 | |
546 | if let Some(arg) = args.values_of("url_denylist") { | |
547 | config.url_denylist = arg.map(|val| val.to_string()).collect(); | |
548 | } | |
549 | ||
540 | 550 | if let Some(arg) = args.values_of("filter_regex") { |
541 | 551 | config.filter_regex = arg.map(|val| val.to_string()).collect(); |
542 | 552 | } |
766 | 776 | update_if_not_default!(&mut conf.insecure, new.insecure, false); |
767 | 777 | update_if_not_default!(&mut conf.extract_links, new.extract_links, false); |
768 | 778 | update_if_not_default!(&mut conf.extensions, new.extensions, Vec::<String>::new()); |
779 | update_if_not_default!( | |
780 | &mut conf.url_denylist, | |
781 | new.url_denylist, | |
782 | Vec::<String>::new() | |
783 | ); | |
769 | 784 | update_if_not_default!(&mut conf.headers, new.headers, HashMap::new()); |
770 | 785 | update_if_not_default!(&mut conf.queries, new.queries, Vec::new()); |
771 | 786 | update_if_not_default!(&mut conf.no_recursion, new.no_recursion, false); |
28 | 28 | redirects = true |
29 | 29 | insecure = true |
30 | 30 | extensions = ["html", "php", "js"] |
31 | url_denylist = ["http://dont-scan.me", "https://also-not.me"] | |
31 | 32 | headers = {stuff = "things", mostuff = "mothings"} |
32 | 33 | queries = [["name","value"], ["rick", "astley"]] |
33 | 34 | no_recursion = true |
71 | 72 | assert_eq!(config.timeout, timeout()); |
72 | 73 | assert_eq!(config.verbosity, 0); |
73 | 74 | assert_eq!(config.scan_limit, 0); |
74 | assert_eq!(config.silent, false); | |
75 | assert_eq!(config.quiet, false); | |
75 | assert!(!config.silent); | |
76 | assert!(!config.quiet); | |
76 | 77 | assert_eq!(config.output_level, OutputLevel::Default); |
77 | assert_eq!(config.dont_filter, false); | |
78 | assert_eq!(config.auto_tune, false); | |
79 | assert_eq!(config.auto_bail, false); | |
78 | assert!(!config.dont_filter); | |
79 | assert!(!config.auto_tune); | |
80 | assert!(!config.auto_bail); | |
80 | 81 | assert_eq!(config.requester_policy, RequesterPolicy::Default); |
81 | assert_eq!(config.no_recursion, false); | |
82 | assert_eq!(config.json, false); | |
83 | assert_eq!(config.save_state, true); | |
84 | assert_eq!(config.stdin, false); | |
85 | assert_eq!(config.add_slash, false); | |
86 | assert_eq!(config.redirects, false); | |
87 | assert_eq!(config.extract_links, false); | |
88 | assert_eq!(config.insecure, false); | |
82 | assert!(!config.no_recursion); | |
83 | assert!(!config.json); | |
84 | assert!(config.save_state); | |
85 | assert!(!config.stdin); | |
86 | assert!(!config.add_slash); | |
87 | assert!(!config.redirects); | |
88 | assert!(!config.extract_links); | |
89 | assert!(!config.insecure); | |
89 | 90 | assert_eq!(config.queries, Vec::new()); |
91 | assert_eq!(config.filter_size, Vec::<u64>::new()); | |
90 | 92 | assert_eq!(config.extensions, Vec::<String>::new()); |
91 | assert_eq!(config.filter_size, Vec::<u64>::new()); | |
93 | assert_eq!(config.url_denylist, Vec::<String>::new()); | |
92 | 94 | assert_eq!(config.filter_regex, Vec::<String>::new()); |
93 | 95 | assert_eq!(config.filter_similar, Vec::<String>::new()); |
94 | 96 | assert_eq!(config.filter_word_count, Vec::<usize>::new()); |
185 | 187 | /// parse the test config and see that the value parsed is correct |
186 | 188 | fn config_reads_silent() { |
187 | 189 | let config = setup_config_test(); |
188 | assert_eq!(config.silent, true); | |
190 | assert!(config.silent); | |
189 | 191 | } |
190 | 192 | |
191 | 193 | #[test] |
192 | 194 | /// parse the test config and see that the value parsed is correct |
193 | 195 | fn config_reads_quiet() { |
194 | 196 | let config = setup_config_test(); |
195 | assert_eq!(config.quiet, true); | |
197 | assert!(config.quiet); | |
196 | 198 | } |
197 | 199 | |
198 | 200 | #[test] |
199 | 201 | /// parse the test config and see that the value parsed is correct |
200 | 202 | fn config_reads_json() { |
201 | 203 | let config = setup_config_test(); |
202 | assert_eq!(config.json, true); | |
204 | assert!(config.json); | |
203 | 205 | } |
204 | 206 | |
205 | 207 | #[test] |
206 | 208 | /// parse the test config and see that the value parsed is correct |
207 | 209 | fn config_reads_auto_bail() { |
208 | 210 | let config = setup_config_test(); |
209 | assert_eq!(config.auto_bail, true); | |
211 | assert!(config.auto_bail); | |
210 | 212 | } |
211 | 213 | |
212 | 214 | #[test] |
213 | 215 | /// parse the test config and see that the value parsed is correct |
214 | 216 | fn config_reads_auto_tune() { |
215 | 217 | let config = setup_config_test(); |
216 | assert_eq!(config.auto_tune, true); | |
218 | assert!(config.auto_tune); | |
217 | 219 | } |
218 | 220 | |
219 | 221 | #[test] |
234 | 236 | /// parse the test config and see that the value parsed is correct |
235 | 237 | fn config_reads_redirects() { |
236 | 238 | let config = setup_config_test(); |
237 | assert_eq!(config.redirects, true); | |
239 | assert!(config.redirects); | |
238 | 240 | } |
239 | 241 | |
240 | 242 | #[test] |
241 | 243 | /// parse the test config and see that the value parsed is correct |
242 | 244 | fn config_reads_insecure() { |
243 | 245 | let config = setup_config_test(); |
244 | assert_eq!(config.insecure, true); | |
246 | assert!(config.insecure); | |
245 | 247 | } |
246 | 248 | |
247 | 249 | #[test] |
248 | 250 | /// parse the test config and see that the value parsed is correct |
249 | 251 | fn config_reads_no_recursion() { |
250 | 252 | let config = setup_config_test(); |
251 | assert_eq!(config.no_recursion, true); | |
253 | assert!(config.no_recursion); | |
252 | 254 | } |
253 | 255 | |
254 | 256 | #[test] |
255 | 257 | /// parse the test config and see that the value parsed is correct |
256 | 258 | fn config_reads_stdin() { |
257 | 259 | let config = setup_config_test(); |
258 | assert_eq!(config.stdin, true); | |
260 | assert!(config.stdin); | |
259 | 261 | } |
260 | 262 | |
261 | 263 | #[test] |
262 | 264 | /// parse the test config and see that the value parsed is correct |
263 | 265 | fn config_reads_dont_filter() { |
264 | 266 | let config = setup_config_test(); |
265 | assert_eq!(config.dont_filter, true); | |
267 | assert!(config.dont_filter); | |
266 | 268 | } |
267 | 269 | |
268 | 270 | #[test] |
269 | 271 | /// parse the test config and see that the value parsed is correct |
270 | 272 | fn config_reads_add_slash() { |
271 | 273 | let config = setup_config_test(); |
272 | assert_eq!(config.add_slash, true); | |
274 | assert!(config.add_slash); | |
273 | 275 | } |
274 | 276 | |
275 | 277 | #[test] |
276 | 278 | /// parse the test config and see that the value parsed is correct |
277 | 279 | fn config_reads_extract_links() { |
278 | 280 | let config = setup_config_test(); |
279 | assert_eq!(config.extract_links, true); | |
281 | assert!(config.extract_links); | |
280 | 282 | } |
281 | 283 | |
282 | 284 | #[test] |
288 | 290 | |
289 | 291 | #[test] |
290 | 292 | /// parse the test config and see that the value parsed is correct |
293 | fn config_reads_url_denylist() { | |
294 | let config = setup_config_test(); | |
295 | assert_eq!( | |
296 | config.url_denylist, | |
297 | vec!["http://dont-scan.me", "https://also-not.me"] | |
298 | ); | |
299 | } | |
300 | ||
301 | #[test] | |
302 | /// parse the test config and see that the value parsed is correct | |
291 | 303 | fn config_reads_filter_regex() { |
292 | 304 | let config = setup_config_test(); |
293 | 305 | assert_eq!(config.filter_regex, vec!["^ignore me$"]); |
332 | 344 | /// parse the test config and see that the value parsed is correct |
333 | 345 | fn config_reads_save_state() { |
334 | 346 | let config = setup_config_test(); |
335 | assert_eq!(config.save_state, false); | |
347 | assert!(!config.save_state); | |
336 | 348 | } |
337 | 349 | |
338 | 350 | #[test] |
363 | 375 | /// parse the test config and see that the values parsed are correct |
364 | 376 | fn config_reads_queries() { |
365 | 377 | let config = setup_config_test(); |
366 | let mut queries = vec![]; | |
367 | queries.push(("name".to_string(), "value".to_string())); | |
368 | queries.push(("rick".to_string(), "astley".to_string())); | |
378 | let queries = vec![ | |
379 | ("name".to_string(), "value".to_string()), | |
380 | ("rick".to_string(), "astley".to_string()), | |
381 | ]; | |
369 | 382 | assert_eq!(config.queries, queries); |
370 | 383 | } |
371 | 384 |
0 | use std::collections::HashSet; | |
1 | 0 | use std::sync::Arc; |
2 | 1 | |
3 | 2 | use reqwest::StatusCode; |
52 | 51 | TryRecursion(Box<FeroxResponse>), |
53 | 52 | |
54 | 53 | /// Send a pointer to the wordlist to the recursion handler |
55 | UpdateWordlist(Arc<HashSet<String>>), | |
54 | UpdateWordlist(Arc<Vec<String>>), | |
56 | 55 | |
57 | 56 | /// Instruct the ScanHandler to join on all known scans, use sender to notify main when done |
58 | 57 | JoinTasks(Sender<bool>), |
138 | 138 | Self { |
139 | 139 | receiver, |
140 | 140 | tx_file, |
141 | config, | |
142 | 141 | file_task, |
142 | config, | |
143 | 143 | } |
144 | 144 | } |
145 | 145 |
0 | use std::collections::HashSet; | |
1 | 0 | use std::sync::Arc; |
2 | 1 | |
3 | 2 | use anyhow::{bail, Result}; |
4 | 3 | use tokio::sync::{mpsc, Semaphore}; |
5 | 4 | |
6 | use crate::response::FeroxResponse; | |
7 | use crate::url::FeroxUrl; | |
8 | 5 | use crate::{ |
6 | response::FeroxResponse, | |
9 | 7 | scan_manager::{FeroxScan, FeroxScans, ScanOrder}, |
10 | 8 | scanner::FeroxScanner, |
11 | 9 | statistics::StatField::TotalScans, |
10 | url::FeroxUrl, | |
11 | utils::should_deny_url, | |
12 | 12 | CommandReceiver, CommandSender, FeroxChannel, Joiner, SLEEP_DURATION, |
13 | 13 | }; |
14 | 14 | |
15 | 15 | use super::command::Command::AddToUsizeField; |
16 | 16 | use super::*; |
17 | use reqwest::Url; | |
17 | 18 | use tokio::time::Duration; |
18 | 19 | |
19 | 20 | #[derive(Debug)] |
53 | 54 | receiver: CommandReceiver, |
54 | 55 | |
55 | 56 | /// wordlist (re)used for each scan |
56 | wordlist: std::sync::Mutex<Option<Arc<HashSet<String>>>>, | |
57 | wordlist: std::sync::Mutex<Option<Arc<Vec<String>>>>, | |
57 | 58 | |
58 | 59 | /// group of scans that need to be joined |
59 | 60 | tasks: Vec<Arc<FeroxScan>>, |
104 | 105 | } |
105 | 106 | |
106 | 107 | /// Set the wordlist |
107 | fn wordlist(&self, wordlist: Arc<HashSet<String>>) { | |
108 | fn wordlist(&self, wordlist: Arc<Vec<String>>) { | |
108 | 109 | if let Ok(mut guard) = self.wordlist.lock() { |
109 | 110 | if guard.is_none() { |
110 | 111 | let _ = std::mem::replace(&mut *guard, Some(wordlist)); |
174 | 175 | } |
175 | 176 | |
176 | 177 | /// Helper to easily get the (locked) underlying wordlist |
177 | pub fn get_wordlist(&self) -> Result<Arc<HashSet<String>>> { | |
178 | pub fn get_wordlist(&self) -> Result<Arc<Vec<String>>> { | |
178 | 179 | if let Ok(guard) = self.wordlist.lock().as_ref() { |
179 | 180 | if let Some(list) = guard.as_ref() { |
180 | 181 | return Ok(list.clone()); |
187 | 188 | /// wrapper around scanning a url to stay DRY |
188 | 189 | async fn ordered_scan_url(&mut self, targets: Vec<String>, order: ScanOrder) -> Result<()> { |
189 | 190 | log::trace!("enter: ordered_scan_url({:?}, {:?})", targets, order); |
191 | let should_test_deny = !self.handles.config.url_denylist.is_empty(); | |
190 | 192 | |
191 | 193 | for target in targets { |
192 | 194 | if self.data.contains(&target) && matches!(order, ScanOrder::Latest) { |
203 | 205 | self.data.add_directory_scan(&target, order).1 // add the new target; return FeroxScan |
204 | 206 | }; |
205 | 207 | |
208 | if should_test_deny && should_deny_url(&Url::parse(&target)?, self.handles.clone())? { | |
209 | // response was caught by a user-provided deny list | |
210 | // checking this last, since it's most susceptible to longer runtimes due to what | |
211 | // input is received | |
212 | continue; | |
213 | } | |
214 | ||
206 | 215 | let list = self.get_wordlist()?; |
207 | 216 | |
208 | 217 | log::info!("scan handler received {} - beginning scan", target); |
243 | 252 | async fn try_recursion(&mut self, response: Box<FeroxResponse>) -> Result<()> { |
244 | 253 | log::trace!("enter: try_recursion({:?})", response,); |
245 | 254 | |
255 | if !response.is_directory() { | |
256 | // not a directory, quick exit | |
257 | return Ok(()); | |
258 | } | |
259 | ||
246 | 260 | let mut base_depth = 1_usize; |
247 | 261 | |
248 | 262 | for (base_url, base_url_depth) in &self.depths { |
256 | 270 | return Ok(()); |
257 | 271 | } |
258 | 272 | |
259 | if !response.is_directory() { | |
260 | // not a directory | |
261 | return Ok(()); | |
262 | } | |
263 | ||
264 | 273 | let targets = vec![response.url().to_string()]; |
265 | 274 | self.ordered_scan_url(targets, ScanOrder::Latest).await?; |
266 | 275 |
0 | 0 | use super::*; |
1 | use crate::utils::should_deny_url; | |
1 | 2 | use crate::{ |
2 | 3 | client, |
3 | 4 | event_handlers::{ |
52 | 53 | |
53 | 54 | /// Extractor implementation |
54 | 55 | impl<'a> Extractor<'a> { |
55 | /// business logic that handles getting links from a normal http body response | |
56 | pub async fn extract(&self) -> Result<()> { | |
57 | let links = match self.target { | |
58 | ExtractionTarget::ResponseBody => self.extract_from_body().await?, | |
59 | ExtractionTarget::RobotsTxt => self.extract_from_robots().await?, | |
60 | }; | |
61 | ||
56 | /// perform extraction from the given target and return any links found | |
57 | pub async fn extract(&self) -> Result<HashSet<String>> { | |
58 | log::trace!("enter: extract (this fn has associated trace exit msg)"); | |
59 | match self.target { | |
60 | ExtractionTarget::ResponseBody => Ok(self.extract_from_body().await?), | |
61 | ExtractionTarget::RobotsTxt => Ok(self.extract_from_robots().await?), | |
62 | } | |
63 | } | |
64 | ||
65 | /// given a set of links from a normal http body response, task the request handler to make | |
66 | /// the requests | |
67 | pub async fn request_links(&self, links: HashSet<String>) -> Result<()> { | |
68 | log::trace!("enter: request_links({:?})", links); | |
62 | 69 | let recursive = if self.handles.config.no_recursion { |
63 | 70 | RecursionStatus::NotRecursive |
64 | 71 | } else { |
120 | 127 | rx.await?; |
121 | 128 | } |
122 | 129 | } |
130 | log::trace!("exit: request_links"); | |
123 | 131 | Ok(()) |
124 | 132 | } |
125 | 133 | |
301 | 309 | bail!("previously seen url"); |
302 | 310 | } |
303 | 311 | |
312 | if !self.handles.config.url_denylist.is_empty() | |
313 | && should_deny_url(&new_url, self.handles.clone())? | |
314 | { | |
315 | // can't allow a denied url to be requested | |
316 | bail!( | |
317 | "prevented request to {} due to {:?}", | |
318 | url, | |
319 | self.handles.config.url_denylist | |
320 | ); | |
321 | } | |
322 | ||
304 | 323 | // make the request and store the response |
305 | 324 | let new_response = logged_request(&new_url, self.handles.clone()).await?; |
306 | 325 | |
390 | 409 | FeroxResponse::from(response, true, self.handles.config.output_level).await; |
391 | 410 | |
392 | 411 | log::trace!("exit: get_robots_file -> {}", ferox_response); |
393 | return Ok(ferox_response); | |
412 | Ok(ferox_response) | |
394 | 413 | } |
395 | 414 | |
396 | 415 | /// update total number of links extracted and expected responses |
66 | 66 | assert_eq!(r_paths.len(), expected.len()); |
67 | 67 | assert_eq!(b_paths.len(), expected.len()); |
68 | 68 | for expected_path in expected { |
69 | assert_eq!(r_paths.contains(&expected_path.to_string()), true); | |
70 | assert_eq!(b_paths.contains(&expected_path.to_string()), true); | |
69 | assert!(r_paths.contains(&expected_path.to_string())); | |
70 | assert!(b_paths.contains(&expected_path.to_string())); | |
71 | 71 | } |
72 | 72 | } |
73 | 73 | |
84 | 84 | assert_eq!(r_paths.len(), expected.len()); |
85 | 85 | assert_eq!(b_paths.len(), expected.len()); |
86 | 86 | for expected_path in expected { |
87 | assert_eq!(r_paths.contains(&expected_path.to_string()), true); | |
88 | assert_eq!(b_paths.contains(&expected_path.to_string()), true); | |
87 | assert!(r_paths.contains(&expected_path.to_string())); | |
88 | assert!(b_paths.contains(&expected_path.to_string())); | |
89 | 89 | } |
90 | 90 | } |
91 | 91 | |
101 | 101 | assert_eq!(r_paths.len(), expected.len()); |
102 | 102 | assert_eq!(b_paths.len(), expected.len()); |
103 | 103 | for expected_path in expected { |
104 | assert_eq!(r_paths.contains(&expected_path.to_string()), true); | |
105 | assert_eq!(b_paths.contains(&expected_path.to_string()), true); | |
104 | assert!(r_paths.contains(&expected_path.to_string())); | |
105 | assert!(b_paths.contains(&expected_path.to_string())); | |
106 | 106 | } |
107 | 107 | } |
108 | 108 | |
117 | 117 | assert_eq!(r_paths.len(), expected.len()); |
118 | 118 | assert_eq!(b_paths.len(), expected.len()); |
119 | 119 | for expected_path in expected { |
120 | assert_eq!(r_paths.contains(&expected_path.to_string()), true); | |
121 | assert_eq!(b_paths.contains(&expected_path.to_string()), true); | |
120 | assert!(r_paths.contains(&expected_path.to_string())); | |
121 | assert!(b_paths.contains(&expected_path.to_string())); | |
122 | 122 | } |
123 | 123 | } |
124 | 124 |
0 | 0 | use std::{ |
1 | collections::HashSet, | |
2 | 1 | env::args, |
3 | 2 | fs::File, |
4 | 3 | io::{stderr, BufRead, BufReader}, |
40 | 39 | } |
41 | 40 | |
42 | 41 | /// Create a HashSet of Strings from the given wordlist then stores it inside an Arc |
43 | fn get_unique_words_from_wordlist(path: &str) -> Result<Arc<HashSet<String>>> { | |
42 | fn get_unique_words_from_wordlist(path: &str) -> Result<Arc<Vec<String>>> { | |
44 | 43 | log::trace!("enter: get_unique_words_from_wordlist({})", path); |
45 | 44 | |
46 | 45 | let file = File::open(&path).with_context(|| format!("Could not open {}", path))?; |
47 | 46 | |
48 | 47 | let reader = BufReader::new(file); |
49 | 48 | |
50 | let mut words = HashSet::new(); | |
49 | let mut words = Vec::new(); | |
51 | 50 | |
52 | 51 | for line in reader.lines() { |
53 | 52 | let result = match line { |
59 | 58 | continue; |
60 | 59 | } |
61 | 60 | |
62 | words.insert(result); | |
61 | words.push(result); | |
63 | 62 | } |
64 | 63 | |
65 | 64 | log::trace!( |
77 | 76 | // so that will allow for cheap/safe sharing of a single wordlist across multi-target scans |
78 | 77 | // as well as additional directories found as part of recursion |
79 | 78 | |
80 | let words = { | |
81 | let words_handles = handles.clone(); | |
82 | tokio::spawn(async move { get_unique_words_from_wordlist(&words_handles.config.wordlist) }) | |
83 | .await?? | |
84 | }; | |
79 | let words = get_unique_words_from_wordlist(&handles.config.wordlist)?; | |
85 | 80 | |
86 | 81 | if words.len() == 0 { |
87 | 82 | bail!("Did not find any words in {}", handles.config.wordlist); |
0 | 0 | use clap::{App, Arg, ArgGroup}; |
1 | 1 | use lazy_static::lazy_static; |
2 | 2 | use regex::Regex; |
3 | use std::env; | |
4 | use std::process; | |
3 | 5 | |
4 | 6 | lazy_static! { |
5 | 7 | /// Regex used to validate values passed to --time-limit |
15 | 17 | |
16 | 18 | /// Create and return an instance of [clap::App](https://docs.rs/clap/latest/clap/struct.App.html), i.e. the Command Line Interface's configuration |
17 | 19 | pub fn initialize() -> App<'static, 'static> { |
18 | App::new("feroxbuster") | |
20 | let mut app = App::new("feroxbuster") | |
19 | 21 | .version(env!("CARGO_PKG_VERSION")) |
20 | 22 | .author("Ben 'epi' Risher (@epi052)") |
21 | 23 | .about("A fast, simple, recursive content discovery tool written in Rust") |
216 | 218 | ), |
217 | 219 | ) |
218 | 220 | .arg( |
221 | Arg::with_name("url_denylist") | |
222 | .long("dont-scan") | |
223 | .value_name("URL") | |
224 | .takes_value(true) | |
225 | .multiple(true) | |
226 | .use_delimiter(true) | |
227 | .help( | |
228 | "URL(s) to exclude from recursion/scans", | |
229 | ), | |
230 | ) | |
231 | .arg( | |
219 | 232 | Arg::with_name("headers") |
220 | 233 | .short("H") |
221 | 234 | .long("headers") |
409 | 422 | |
410 | 423 | Ludicrous speed... go! |
411 | 424 | ./feroxbuster -u http://127.1 -t 200 |
412 | "#) | |
425 | "#); | |
426 | ||
427 | for arg in env::args() { | |
428 | // secure-77 noticed that when an incorrect flag/option is used, the short help message is printed | |
429 | // which is fine, but if you add -h|--help, it still errors out on the bad flag/option, | |
430 | // never showing the full help message. This code addresses that behavior | |
431 | if arg == "--help" || arg == "-h" { | |
432 | app.print_long_help().unwrap(); | |
433 | println!(); // just a newline to mirror original --help output | |
434 | process::exit(1); | |
435 | } | |
436 | } | |
437 | ||
438 | app | |
413 | 439 | } |
414 | 440 | |
415 | 441 | /// Validate that a string is formatted as a number followed by s, m, h, or d (10d, 30s, etc...) |
30 | 30 | let separator = "─".to_string(); |
31 | 31 | |
32 | 32 | let instructions = format!( |
33 | "Enter a {} list of indexes to {} (ex: 2,3)", | |
33 | "Enter a {} list of indexes/ranges to {} ({}: 1-4,8,9-13)", | |
34 | 34 | style("comma-separated").yellow(), |
35 | 35 | style("cancel").red(), |
36 | style("ex").cyan(), | |
36 | 37 | ); |
37 | 38 | |
38 | 39 | let name = format!( |
42 | 43 | "💀" |
43 | 44 | ); |
44 | 45 | |
46 | let force_msg = format!( | |
47 | "Add {} to {} confirmation ({}: 3-5 -f)", | |
48 | style("-f").yellow(), | |
49 | style("skip").yellow(), | |
50 | style("ex").cyan(), | |
51 | ); | |
52 | ||
45 | 53 | let longest = measure_text_width(&instructions).max(measure_text_width(&name)); |
46 | 54 | |
47 | 55 | let border = separator.repeat(longest); |
48 | 56 | |
49 | 57 | let padded_name = pad_str(&name, longest, Alignment::Center, None); |
58 | let padded_force = pad_str(&force_msg, longest, Alignment::Center, None); | |
50 | 59 | |
51 | 60 | let header = format!("{}\n{}\n{}", border, padded_name, border); |
52 | let footer = format!("{}\n{}\n{}", border, instructions, border); | |
61 | let footer = format!("{}\n{}\n{}\n{}", border, instructions, padded_force, border); | |
53 | 62 | |
54 | 63 | Self { |
55 | 64 | separator, |
92 | 101 | self.term.write_line(msg).unwrap_or_default(); |
93 | 102 | } |
94 | 103 | |
95 | /// split a string into vec of usizes | |
104 | /// Helper for parsing a usize from a str | |
105 | fn str_to_usize(&self, value: &str) -> usize { | |
106 | if value.is_empty() { | |
107 | return 0; | |
108 | } | |
109 | ||
110 | value | |
111 | .trim() | |
112 | .to_string() | |
113 | .parse::<usize>() | |
114 | .unwrap_or_else(|e| { | |
115 | self.println(&format!("Found non-numeric input: {}: {:?}", e, value)); | |
116 | 0 | |
117 | }) | |
118 | } | |
119 | ||
120 | /// split a comma delimited string into vec of usizes | |
96 | 121 | pub(super) fn split_to_nums(&self, line: &str) -> Vec<usize> { |
97 | line.split(',') | |
98 | .map(|s| { | |
99 | s.trim().to_string().parse::<usize>().unwrap_or_else(|e| { | |
100 | self.println(&format!("Found non-numeric input: {}", e)); | |
101 | 0 | |
102 | }) | |
103 | }) | |
104 | .filter(|m| *m != 0) | |
105 | .collect() | |
122 | let mut nums = Vec::new(); | |
123 | let values = line.split(','); | |
124 | ||
125 | for mut value in values { | |
126 | value = value.trim(); | |
127 | ||
128 | if value.contains('-') { | |
129 | // range of two values, needs further processing | |
130 | ||
131 | let range: Vec<usize> = value | |
132 | .split('-') | |
133 | .map(|s| self.str_to_usize(s)) | |
134 | .filter(|m| *m != 0) | |
135 | .collect(); | |
136 | ||
137 | if range.len() != 2 { | |
138 | // expecting [1, 4] or similar, if a 0 was used, we'd be left with a vec of size 1 | |
139 | self.println(&format!("Found invalid range of scans: {}", value)); | |
140 | continue; | |
141 | } | |
142 | ||
143 | (range[0]..=range[1]).for_each(|n| { | |
144 | // iterate from lower to upper bound and add all interim values, skipping | |
145 | // any already known | |
146 | if !nums.contains(&n) { | |
147 | nums.push(n) | |
148 | } | |
149 | }); | |
150 | } else { | |
151 | let value = self.str_to_usize(value); | |
152 | ||
153 | if value != 0 && !nums.contains(&value) { | |
154 | // the zeroth scan is always skipped, skip already known values | |
155 | nums.push(value); | |
156 | } | |
157 | } | |
158 | } | |
159 | ||
160 | nums | |
106 | 161 | } |
107 | 162 | |
108 | 163 | /// get comma-separated list of scan indexes from the user |
109 | pub(super) fn get_scans_from_user(&self) -> Option<Vec<usize>> { | |
164 | pub(super) fn get_scans_from_user(&self) -> Option<(Vec<usize>, bool)> { | |
110 | 165 | if let Ok(line) = self.term.read_line() { |
111 | Some(self.split_to_nums(&line)) | |
166 | let force = line.contains("-f"); | |
167 | let line = line.replace("-f", ""); | |
168 | Some((self.split_to_nums(&line), force)) | |
112 | 169 | } else { |
113 | 170 | None |
114 | 171 | } |
35 | 35 | pub(super) scan_type: ScanType, |
36 | 36 | |
37 | 37 | /// The order in which the scan was received |
38 | pub(super) scan_order: ScanOrder, | |
38 | pub(crate) scan_order: ScanOrder, | |
39 | 39 | |
40 | 40 | /// Number of requests to populate the progress bar with |
41 | 41 | pub(super) num_requests: u64, |
251 | 251 | } |
252 | 252 | |
253 | 253 | /// Given a list of indexes, cancel their associated FeroxScans |
254 | async fn cancel_scans(&self, indexes: Vec<usize>) -> usize { | |
254 | async fn cancel_scans(&self, indexes: Vec<usize>, force: bool) -> usize { | |
255 | 255 | let menu_pause_duration = Duration::from_millis(SLEEP_DURATION); |
256 | 256 | |
257 | 257 | let mut num_cancelled = 0_usize; |
272 | 272 | Err(..) => continue, |
273 | 273 | }; |
274 | 274 | |
275 | let input = self.menu.confirm_cancellation(&selected.url); | |
275 | let input = if force { | |
276 | 'y' | |
277 | } else { | |
278 | self.menu.confirm_cancellation(&selected.url) | |
279 | }; | |
276 | 280 | |
277 | 281 | if input == 'y' || input == '\n' { |
278 | 282 | self.menu.println(&format!("Stopping {}...", selected.url)); |
304 | 308 | |
305 | 309 | let mut num_cancelled = 0_usize; |
306 | 310 | |
307 | if let Some(input) = self.menu.get_scans_from_user() { | |
308 | num_cancelled += self.cancel_scans(input).await; | |
311 | if let Some((input, force)) = self.menu.get_scans_from_user() { | |
312 | num_cancelled += self.cancel_scans(input, force).await; | |
309 | 313 | }; |
310 | 314 | |
311 | 315 | self.menu.clear_screen(); |
46 | 46 | |
47 | 47 | /// Simple call to produce a JSON string using the given FeroxState |
48 | 48 | fn as_json(&self) -> Result<String> { |
49 | Ok(serde_json::to_string(&self) | |
50 | .with_context(|| fmt_err("Could not convert scan's running state to JSON"))?) | |
49 | serde_json::to_string(&self) | |
50 | .with_context(|| fmt_err("Could not convert scan's running state to JSON")) | |
51 | 51 | } |
52 | 52 | } |
51 | 51 | let urls = FeroxScans::default(); |
52 | 52 | let url = "http://unknown_url"; |
53 | 53 | let (result, _scan) = urls.add_scan(url, ScanType::Directory, ScanOrder::Latest); |
54 | assert_eq!(result, true); | |
54 | assert!(result); | |
55 | 55 | } |
56 | 56 | |
57 | 57 | #[test] |
70 | 70 | Some(pb), |
71 | 71 | ); |
72 | 72 | |
73 | assert_eq!(urls.insert(scan), true); | |
73 | assert!(urls.insert(scan)); | |
74 | 74 | |
75 | 75 | let (result, _scan) = urls.add_scan(url, ScanType::Directory, ScanOrder::Latest); |
76 | 76 | |
77 | assert_eq!(result, false); | |
77 | assert!(!result); | |
78 | 78 | } |
79 | 79 | |
80 | 80 | #[test] |
92 | 92 | Some(pb), |
93 | 93 | ); |
94 | 94 | |
95 | assert_eq!( | |
96 | scan.progress_bar | |
97 | .lock() | |
98 | .unwrap() | |
99 | .as_ref() | |
100 | .unwrap() | |
101 | .is_finished(), | |
102 | false | |
103 | ); | |
95 | assert!(!scan | |
96 | .progress_bar | |
97 | .lock() | |
98 | .unwrap() | |
99 | .as_ref() | |
100 | .unwrap() | |
101 | .is_finished()); | |
104 | 102 | |
105 | 103 | scan.stop_progress_bar(); |
106 | 104 | |
107 | assert_eq!( | |
108 | scan.progress_bar | |
109 | .lock() | |
110 | .unwrap() | |
111 | .as_ref() | |
112 | .unwrap() | |
113 | .is_finished(), | |
114 | true | |
115 | ); | |
105 | assert!(scan | |
106 | .progress_bar | |
107 | .lock() | |
108 | .unwrap() | |
109 | .as_ref() | |
110 | .unwrap() | |
111 | .is_finished()); | |
116 | 112 | } |
117 | 113 | |
118 | 114 | #[test] |
130 | 126 | None, |
131 | 127 | ); |
132 | 128 | |
133 | assert_eq!(urls.insert(scan), true); | |
129 | assert!(urls.insert(scan)); | |
134 | 130 | |
135 | 131 | let (result, _scan) = urls.add_scan(url, ScanType::File, ScanOrder::Latest); |
136 | 132 | |
137 | assert_eq!(result, false); | |
133 | assert!(!result); | |
138 | 134 | } |
139 | 135 | |
140 | 136 | #[tokio::test(flavor = "multi_thread", worker_threads = 1)] |
170 | 166 | .await |
171 | 167 | .unwrap(); |
172 | 168 | |
173 | assert_eq!(urls.insert(scan), true); | |
174 | assert_eq!(urls.insert(scan_two), true); | |
169 | assert!(urls.insert(scan)); | |
170 | assert!(urls.insert(scan_two)); | |
175 | 171 | |
176 | 172 | urls.display_scans().await; |
177 | 173 | } |
329 | 325 | |
330 | 326 | assert_eq!(response.url().as_str(), "https://nerdcore.com/css"); |
331 | 327 | assert_eq!(response.url().path(), "/css"); |
332 | assert_eq!(response.wildcard(), true); | |
328 | assert!(response.wildcard()); | |
333 | 329 | assert_eq!(response.status().as_u16(), 301); |
334 | 330 | assert_eq!(response.content_length(), 173); |
335 | 331 | assert_eq!(response.line_count(), 10); |
382 | 378 | |
383 | 379 | let json_state = ferox_state.as_json().unwrap(); |
384 | 380 | let expected = format!( |
385 | r#"{{"scans":[{{"id":"{}","url":"https://spiritanimal.com","scan_type":"Directory","status":"NotStarted","num_requests":0}}],"config":{{"type":"configuration","wordlist":"/usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt","config":"","proxy":"","replay_proxy":"","target_url":"","status_codes":[200,204,301,302,307,308,401,403,405],"replay_codes":[200,204,301,302,307,308,401,403,405],"filter_status":[],"threads":50,"timeout":7,"verbosity":0,"silent":false,"quiet":false,"auto_bail":false,"auto_tune":false,"json":false,"output":"","debug_log":"","user_agent":"feroxbuster/{}","redirects":false,"insecure":false,"extensions":[],"headers":{{}},"queries":[],"no_recursion":false,"extract_links":false,"add_slash":false,"stdin":false,"depth":4,"scan_limit":0,"parallel":0,"rate_limit":0,"filter_size":[],"filter_line_count":[],"filter_word_count":[],"filter_regex":[],"dont_filter":false,"resumed":false,"resume_from":"","save_state":false,"time_limit":"","filter_similar":[]}},"responses":[{{"type":"response","url":"https://nerdcore.com/css","path":"/css","wildcard":true,"status":301,"content_length":173,"line_count":10,"word_count":16,"headers":{{"server":"nginx/1.16.1"}}}}]"#, | |
381 | r#"{{"scans":[{{"id":"{}","url":"https://spiritanimal.com","scan_type":"Directory","status":"NotStarted","num_requests":0}}],"config":{{"type":"configuration","wordlist":"/usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt","config":"","proxy":"","replay_proxy":"","target_url":"","status_codes":[200,204,301,302,307,308,401,403,405],"replay_codes":[200,204,301,302,307,308,401,403,405],"filter_status":[],"threads":50,"timeout":7,"verbosity":0,"silent":false,"quiet":false,"auto_bail":false,"auto_tune":false,"json":false,"output":"","debug_log":"","user_agent":"feroxbuster/{}","redirects":false,"insecure":false,"extensions":[],"headers":{{}},"queries":[],"no_recursion":false,"extract_links":false,"add_slash":false,"stdin":false,"depth":4,"scan_limit":0,"parallel":0,"rate_limit":0,"filter_size":[],"filter_line_count":[],"filter_word_count":[],"filter_regex":[],"dont_filter":false,"resumed":false,"resume_from":"","save_state":false,"time_limit":"","filter_similar":[],"url_denylist":[]}},"responses":[{{"type":"response","url":"https://nerdcore.com/css","path":"/css","wildcard":true,"status":301,"content_length":173,"line_count":10,"word_count":16,"headers":{{"server":"nginx/1.16.1"}}}}]"#, | |
386 | 382 | saved_id, VERSION |
387 | 383 | ); |
388 | 384 | println!("{}\n{}", expected, json_state); |
520 | 516 | fn split_to_nums_is_correct() { |
521 | 517 | let menu = Menu::new(); |
522 | 518 | |
523 | let nums = menu.split_to_nums("1, 3, 4"); | |
524 | ||
525 | assert_eq!(nums, vec![1, 3, 4]); | |
519 | let nums = menu.split_to_nums("1, 3, 4, 7 - 12, 10-10, 10-11, 9-12, 12-6, -1, 4-"); | |
520 | ||
521 | assert_eq!(nums, vec![1, 3, 4, 7, 8, 9, 10, 11, 12]); | |
522 | assert_eq!(menu.split_to_nums("9-12"), vec![9, 10, 11, 12]); | |
523 | assert!(menu.split_to_nums("-12").is_empty()); | |
524 | assert!(menu.split_to_nums("12-").is_empty()); | |
525 | assert!(menu.split_to_nums("\n").is_empty()); | |
526 | 526 | } |
527 | 527 | |
528 | 528 | #[test] |
40 | 40 | log::trace!("exit: start_max_time_thread"); |
41 | 41 | |
42 | 42 | #[cfg(test)] |
43 | panic!(handles); | |
43 | panic!("{:?}", handles); | |
44 | 44 | #[cfg(not(test))] |
45 | 45 | let _ = TermInputHandler::sigint_handler(handles.clone()); |
46 | 46 | } |
0 | use std::{collections::HashSet, ops::Deref, sync::atomic::Ordering, sync::Arc, time::Instant}; | |
0 | use std::{ops::Deref, sync::atomic::Ordering, sync::Arc, time::Instant}; | |
1 | 1 | |
2 | 2 | use anyhow::{bail, Result}; |
3 | 3 | use futures::{stream, StreamExt}; |
39 | 39 | order: ScanOrder, |
40 | 40 | |
41 | 41 | /// wordlist that's already been read from disk |
42 | wordlist: Arc<HashSet<String>>, | |
42 | wordlist: Arc<Vec<String>>, | |
43 | 43 | |
44 | 44 | /// limiter that restricts the number of active FeroxScanners |
45 | 45 | scan_limiter: Arc<Semaphore>, |
51 | 51 | pub fn new( |
52 | 52 | target_url: &str, |
53 | 53 | order: ScanOrder, |
54 | wordlist: Arc<HashSet<String>>, | |
54 | wordlist: Arc<Vec<String>>, | |
55 | 55 | scan_limiter: Arc<Semaphore>, |
56 | 56 | handles: Arc<Handles>, |
57 | 57 | ) -> Self { |
82 | 82 | .target(RobotsTxt) |
83 | 83 | .build()?; |
84 | 84 | |
85 | let _ = extractor.extract().await; | |
85 | let links = extractor.extract().await?; | |
86 | extractor.request_links(links).await?; | |
86 | 87 | } |
87 | 88 | |
88 | 89 | let scanned_urls = self.handles.ferox_scans()?; |
89 | 89 | atomic_store!(self.remove_limit, true); |
90 | 90 | } |
91 | 91 | } |
92 | self.set_limit(heap.value() as usize); | |
93 | 92 | } else if heap.has_children() { |
94 | 93 | // streak not at 3, just check that we can move down, and do so |
95 | 94 | heap.move_left(); |
96 | self.set_limit(heap.value() as usize); | |
97 | 95 | } else { |
98 | 96 | // tree bottomed out, need to move back up the tree a bit |
99 | 97 | let current = heap.value(); |
103 | 101 | if current > heap.value() { |
104 | 102 | heap.move_up(); |
105 | 103 | } |
106 | ||
107 | self.set_limit(heap.value() as usize); | |
108 | 104 | } |
105 | self.set_limit(heap.value() as usize); | |
109 | 106 | } |
110 | 107 | } |
111 | 108 | |
199 | 196 | pd.adjust_up(&3); |
200 | 197 | assert_eq!(pd.heap.read().unwrap().value(), 300); |
201 | 198 | assert_eq!(pd.limit.load(Ordering::Relaxed), 300); |
202 | assert_eq!(pd.remove_limit.load(Ordering::Relaxed), false); | |
199 | assert!(!pd.remove_limit.load(Ordering::Relaxed)); | |
203 | 200 | } |
204 | 201 | |
205 | 202 | #[test] |
216 | 213 | pd.adjust_up(&3); |
217 | 214 | assert_eq!(pd.heap.read().unwrap().value(), 200); |
218 | 215 | assert_eq!(pd.limit.load(Ordering::Relaxed), 200); |
219 | assert_eq!(pd.remove_limit.load(Ordering::Relaxed), true); | |
216 | assert!(pd.remove_limit.load(Ordering::Relaxed)); | |
220 | 217 | } |
221 | 218 | |
222 | 219 | #[test] |
233 | 230 | pd.adjust_up(&3); |
234 | 231 | assert_eq!(pd.heap.read().unwrap().value(), 350); |
235 | 232 | assert_eq!(pd.limit.load(Ordering::Relaxed), 350); |
236 | assert_eq!(pd.remove_limit.load(Ordering::Relaxed), false); | |
233 | assert!(!pd.remove_limit.load(Ordering::Relaxed)); | |
237 | 234 | } |
238 | 235 | |
239 | 236 | #[test] |
250 | 247 | pd.adjust_up(&3); |
251 | 248 | assert_eq!(pd.heap.read().unwrap().value(), 300); |
252 | 249 | assert_eq!(pd.limit.load(Ordering::Relaxed), 300); |
253 | assert_eq!(pd.remove_limit.load(Ordering::Relaxed), false); | |
250 | assert!(!pd.remove_limit.load(Ordering::Relaxed)); | |
254 | 251 | } |
255 | 252 | |
256 | 253 | #[test] |
268 | 265 | pd.adjust_up(&0); |
269 | 266 | assert_eq!(pd.heap.read().unwrap().value(), 43); |
270 | 267 | assert_eq!(pd.limit.load(Ordering::Relaxed), 43); |
271 | assert_eq!(pd.remove_limit.load(Ordering::Relaxed), false); | |
268 | assert!(!pd.remove_limit.load(Ordering::Relaxed)); | |
272 | 269 | } |
273 | 270 | |
274 | 271 | #[test] |
286 | 283 | pd.adjust_up(&0); |
287 | 284 | assert_eq!(pd.heap.read().unwrap().value(), 37); |
288 | 285 | assert_eq!(pd.limit.load(Ordering::Relaxed), 37); |
289 | assert_eq!(pd.remove_limit.load(Ordering::Relaxed), false); | |
286 | assert!(!pd.remove_limit.load(Ordering::Relaxed)); | |
290 | 287 | } |
291 | 288 | |
292 | 289 | #[test] |
26 | 26 | }; |
27 | 27 | |
28 | 28 | use super::{policy_data::PolicyData, FeroxScanner, PolicyTrigger}; |
29 | use crate::utils::should_deny_url; | |
30 | use std::collections::HashSet; | |
29 | 31 | |
30 | 32 | /// Makes multiple requests based on the presence of extensions |
31 | 33 | pub(super) struct Requester { |
44 | 46 | /// FeroxScan associated with the creation of this Requester |
45 | 47 | ferox_scan: Arc<FeroxScan>, |
46 | 48 | |
49 | /// cache of previously seen links gotten via link extraction. since the requester is passed | |
50 | /// around as an arc, and seen_links needs to be mutable, putting it behind a lock for | |
51 | /// interior mutability, similar to the tuning_lock below | |
52 | seen_links: RwLock<HashSet<String>>, | |
53 | ||
47 | 54 | /// simple lock to control access to tuning to a single thread (per-scan) |
48 | 55 | /// |
49 | 56 | /// need a usize to determine the number of consecutive non-error calls that a requester has |
50 | 57 | /// seen; this will satisfy the non-mut self constraint (due to us being behind an Arc, and |
51 | /// the need for a counter | |
58 | /// the need for a counter) | |
52 | 59 | tuning_lock: Mutex<usize>, |
53 | 60 | } |
54 | 61 | |
72 | 79 | Ok(Self { |
73 | 80 | ferox_scan, |
74 | 81 | policy_data, |
82 | seen_links: RwLock::new(HashSet::<String>::new()), | |
75 | 83 | rate_limiter: RwLock::new(rate_limiter), |
76 | 84 | handles: scanner.handles.clone(), |
77 | 85 | target_url: scanner.target_url.to_owned(), |
297 | 305 | let urls = |
298 | 306 | FeroxUrl::from_string(&self.target_url, self.handles.clone()).formatted_urls(word)?; |
299 | 307 | |
308 | let should_test_deny = !self.handles.config.url_denylist.is_empty(); | |
309 | ||
300 | 310 | for url in urls { |
301 | 311 | // auto_tune is true, or rate_limit was set (mutually exclusive to user) |
302 | 312 | // and a rate_limiter has been created |
310 | 320 | log::warn!("Could not rate limit scan: {}", e); |
311 | 321 | self.handles.stats.send(AddError(Other)).unwrap_or_default(); |
312 | 322 | } |
323 | } | |
324 | ||
325 | if should_test_deny && should_deny_url(&url, self.handles.clone())? { | |
326 | // can't allow a denied url to be requested | |
327 | continue; | |
313 | 328 | } |
314 | 329 | |
315 | 330 | let response = logged_request(&url, self.handles.clone()).await?; |
366 | 381 | .handles(self.handles.clone()) |
367 | 382 | .build()?; |
368 | 383 | |
369 | extractor.extract().await?; | |
384 | let new_links: HashSet<_>; | |
385 | let extracted = extractor.extract().await?; | |
386 | ||
387 | { | |
388 | // gain and quickly drop the read lock on seen_links, using it while unlocked | |
389 | // to determine if there are any new links to process | |
390 | let read_links = self.seen_links.read().await; | |
391 | new_links = extracted.difference(&read_links).cloned().collect(); | |
392 | } | |
393 | ||
394 | if !new_links.is_empty() { | |
395 | // using is_empty instead of direct iteration to acquire the write lock behind | |
396 | // some kind of less expensive gate (and not in a loop, obv) | |
397 | let mut write_links = self.seen_links.write().await; | |
398 | for new_link in &new_links { | |
399 | write_links.insert(new_link.to_owned()); | |
400 | } | |
401 | } | |
402 | ||
403 | extractor.request_links(new_links).await?; | |
370 | 404 | } |
371 | 405 | |
372 | 406 | // everything else should be reported |
535 | 569 | |
536 | 570 | let requester = Requester { |
537 | 571 | handles, |
572 | seen_links: RwLock::new(HashSet::<String>::new()), | |
538 | 573 | tuning_lock: Mutex::new(0), |
539 | 574 | ferox_scan: Arc::new(FeroxScan::default()), |
540 | 575 | target_url: "http://localhost".to_string(), |
562 | 597 | |
563 | 598 | let requester = Requester { |
564 | 599 | handles, |
600 | seen_links: RwLock::new(HashSet::<String>::new()), | |
565 | 601 | tuning_lock: Mutex::new(0), |
566 | 602 | ferox_scan: ferox_scan.clone(), |
567 | 603 | target_url: "http://localhost".to_string(), |
586 | 622 | |
587 | 623 | let requester = Requester { |
588 | 624 | handles, |
625 | seen_links: RwLock::new(HashSet::<String>::new()), | |
589 | 626 | tuning_lock: Mutex::new(0), |
590 | 627 | ferox_scan: ferox_scan.clone(), |
591 | 628 | target_url: "http://localhost".to_string(), |
625 | 662 | |
626 | 663 | let requester = Requester { |
627 | 664 | handles, |
665 | seen_links: RwLock::new(HashSet::<String>::new()), | |
628 | 666 | tuning_lock: Mutex::new(0), |
629 | 667 | ferox_scan: ferox_scan.clone(), |
630 | 668 | target_url: "http://localhost".to_string(), |
679 | 717 | let req_clone = scan_two.clone(); |
680 | 718 | let requester = Requester { |
681 | 719 | handles, |
720 | seen_links: RwLock::new(HashSet::<String>::new()), | |
682 | 721 | tuning_lock: Mutex::new(0), |
683 | 722 | ferox_scan: req_clone, |
684 | 723 | target_url: "http://one/one/stuff.php".to_string(), |
712 | 751 | |
713 | 752 | let requester = Requester { |
714 | 753 | handles, |
754 | seen_links: RwLock::new(HashSet::<String>::new()), | |
715 | 755 | tuning_lock: Mutex::new(0), |
716 | 756 | ferox_scan: Arc::new(FeroxScan::default()), |
717 | 757 | target_url: "http://one/one/stuff.php".to_string(), |
733 | 773 | |
734 | 774 | let requester = Requester { |
735 | 775 | handles, |
776 | seen_links: RwLock::new(HashSet::<String>::new()), | |
736 | 777 | tuning_lock: Mutex::new(0), |
737 | 778 | ferox_scan: Arc::new(FeroxScan::default()), |
738 | 779 | target_url: "http://localhost".to_string(), |
755 | 796 | |
756 | 797 | let requester = Arc::new(Requester { |
757 | 798 | handles, |
799 | seen_links: RwLock::new(HashSet::<String>::new()), | |
758 | 800 | tuning_lock: Mutex::new(0), |
759 | 801 | ferox_scan: Arc::new(FeroxScan::default()), |
760 | 802 | target_url: "http://localhost".to_string(), |
771 | 813 | |
772 | 814 | requester.cool_down().await; |
773 | 815 | |
774 | assert_eq!(resp.await.unwrap(), true); | |
816 | assert!(resp.await.unwrap()); | |
775 | 817 | println!("{}", start.elapsed().as_millis()); |
776 | 818 | assert!(start.elapsed().as_millis() >= 3500); |
777 | 819 | } |
784 | 826 | |
785 | 827 | let requester = Requester { |
786 | 828 | handles, |
829 | seen_links: RwLock::new(HashSet::<String>::new()), | |
787 | 830 | tuning_lock: Mutex::new(0), |
788 | 831 | ferox_scan: Arc::new(FeroxScan::default()), |
789 | 832 | target_url: "http://localhost".to_string(), |
821 | 864 | |
822 | 865 | let requester = Requester { |
823 | 866 | handles, |
867 | seen_links: RwLock::new(HashSet::<String>::new()), | |
824 | 868 | tuning_lock: Mutex::new(0), |
825 | 869 | ferox_scan: Arc::new(scan), |
826 | 870 | target_url: "http://localhost".to_string(), |
856 | 900 | |
857 | 901 | let requester = Requester { |
858 | 902 | handles, |
903 | seen_links: RwLock::new(HashSet::<String>::new()), | |
859 | 904 | tuning_lock: Mutex::new(0), |
860 | 905 | ferox_scan: Arc::new(scan), |
861 | 906 | target_url: "http://localhost".to_string(), |
883 | 928 | |
884 | 929 | let mut requester = Requester { |
885 | 930 | handles, |
931 | seen_links: RwLock::new(HashSet::<String>::new()), | |
886 | 932 | tuning_lock: Mutex::new(0), |
887 | 933 | ferox_scan: Arc::new(FeroxScan::default()), |
888 | 934 | target_url: "http://localhost".to_string(), |
890 | 936 | policy_data: PolicyData::new(RequesterPolicy::AutoBail, 7), |
891 | 937 | }; |
892 | 938 | |
893 | assert_eq!( | |
894 | requester.too_many_status_errors(PolicyTrigger::Errors), | |
895 | false | |
896 | ); | |
897 | ||
898 | assert_eq!( | |
899 | requester.too_many_status_errors(PolicyTrigger::Status429), | |
900 | false | |
901 | ); | |
939 | assert!(!requester.too_many_status_errors(PolicyTrigger::Errors)); | |
940 | ||
941 | assert!(!requester.too_many_status_errors(PolicyTrigger::Status429)); | |
902 | 942 | requester.ferox_scan.progress_bar().set_position(10); |
903 | 943 | requester.ferox_scan.add_429(); |
904 | 944 | requester.ferox_scan.add_429(); |
905 | 945 | requester.ferox_scan.add_429(); |
906 | assert_eq!( | |
907 | requester.too_many_status_errors(PolicyTrigger::Status429), | |
908 | true | |
909 | ); | |
910 | ||
911 | assert_eq!( | |
912 | requester.too_many_status_errors(PolicyTrigger::Status403), | |
913 | false | |
914 | ); | |
946 | assert!(requester.too_many_status_errors(PolicyTrigger::Status429)); | |
947 | ||
948 | assert!(!requester.too_many_status_errors(PolicyTrigger::Status403)); | |
915 | 949 | requester.ferox_scan = Arc::new(FeroxScan::default()); |
916 | 950 | requester.ferox_scan.progress_bar().set_position(10); |
917 | 951 | requester.ferox_scan.add_403(); |
923 | 957 | requester.ferox_scan.add_403(); |
924 | 958 | requester.ferox_scan.add_403(); |
925 | 959 | requester.ferox_scan.add_403(); |
926 | assert_eq!( | |
927 | requester.too_many_status_errors(PolicyTrigger::Status403), | |
928 | true | |
929 | ); | |
960 | assert!(requester.too_many_status_errors(PolicyTrigger::Status403)); | |
930 | 961 | } |
931 | 962 | |
932 | 963 | #[tokio::test(flavor = "multi_thread", worker_threads = 1)] |
940 | 971 | |
941 | 972 | let requester = Requester { |
942 | 973 | handles, |
974 | seen_links: RwLock::new(HashSet::<String>::new()), | |
943 | 975 | tuning_lock: Mutex::new(0), |
944 | 976 | ferox_scan: Arc::new(FeroxScan::default()), |
945 | 977 | target_url: "http://localhost".to_string(), |
982 | 1014 | |
983 | 1015 | let requester = Requester { |
984 | 1016 | handles, |
1017 | seen_links: RwLock::new(HashSet::<String>::new()), | |
985 | 1018 | tuning_lock: Mutex::new(0), |
986 | 1019 | ferox_scan: scan.clone(), |
987 | 1020 | target_url: "http://localhost".to_string(), |
0 | 0 | use std::{ |
1 | collections::HashMap, | |
2 | convert::TryFrom, | |
1 | 3 | fs::File, |
2 | 4 | io::BufReader, |
3 | 5 | sync::{ |
8 | 10 | |
9 | 11 | use anyhow::{Context, Result}; |
10 | 12 | use reqwest::StatusCode; |
11 | use serde::{Deserialize, Serialize}; | |
13 | use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer}; | |
14 | use serde_json::Value; | |
12 | 15 | |
13 | 16 | use crate::{ |
14 | 17 | traits::FeroxSerialize, |
18 | 21 | use super::{error::StatError, field::StatField}; |
19 | 22 | |
20 | 23 | /// Data collection of statistics related to a scan |
21 | #[derive(Default, Deserialize, Debug, Serialize)] | |
24 | #[derive(Default, Debug)] | |
22 | 25 | pub struct Stats { |
23 | #[serde(rename = "type")] | |
24 | 26 | /// Name of this type of struct, used for serialization, i.e. `{"type":"statistics"}` |
25 | 27 | kind: String, |
26 | 28 | |
124 | 126 | total_runtime: Mutex<Vec<f64>>, |
125 | 127 | |
126 | 128 | /// tracker for the number of extensions the user specified |
127 | #[serde(skip)] | |
128 | 129 | num_extensions: usize, |
129 | 130 | |
130 | 131 | /// tracker for whether to use json during serialization or not |
131 | #[serde(skip)] | |
132 | 132 | json: bool, |
133 | 133 | } |
134 | 134 | |
143 | 143 | /// Simple call to produce a JSON string using the given Stats object |
144 | 144 | fn as_json(&self) -> Result<String> { |
145 | 145 | Ok(serde_json::to_string(&self)?) |
146 | } | |
147 | } | |
148 | ||
149 | /// Serialize implementation for Stats | |
150 | impl Serialize for Stats { | |
151 | /// Function that handles serialization of Stats | |
152 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | |
153 | where | |
154 | S: Serializer, | |
155 | { | |
156 | let mut state = serializer.serialize_struct("Stats", 32)?; | |
157 | ||
158 | state.serialize_field("type", &self.kind)?; | |
159 | state.serialize_field("timeouts", &atomic_load!(self.timeouts))?; | |
160 | state.serialize_field("requests", &atomic_load!(self.requests))?; | |
161 | state.serialize_field("expected_per_scan", &atomic_load!(self.expected_per_scan))?; | |
162 | state.serialize_field("total_expected", &atomic_load!(self.total_expected))?; | |
163 | state.serialize_field("errors", &atomic_load!(self.errors))?; | |
164 | state.serialize_field("successes", &atomic_load!(self.successes))?; | |
165 | state.serialize_field("redirects", &atomic_load!(self.redirects))?; | |
166 | state.serialize_field("client_errors", &atomic_load!(self.client_errors))?; | |
167 | state.serialize_field("server_errors", &atomic_load!(self.server_errors))?; | |
168 | state.serialize_field("total_scans", &atomic_load!(self.total_scans))?; | |
169 | state.serialize_field("initial_targets", &atomic_load!(self.initial_targets))?; | |
170 | state.serialize_field("links_extracted", &atomic_load!(self.links_extracted))?; | |
171 | state.serialize_field("status_200s", &atomic_load!(self.status_200s))?; | |
172 | state.serialize_field("status_301s", &atomic_load!(self.status_301s))?; | |
173 | state.serialize_field("status_302s", &atomic_load!(self.status_302s))?; | |
174 | state.serialize_field("status_401s", &atomic_load!(self.status_401s))?; | |
175 | state.serialize_field("status_403s", &atomic_load!(self.status_403s))?; | |
176 | state.serialize_field("status_429s", &atomic_load!(self.status_429s))?; | |
177 | state.serialize_field("status_500s", &atomic_load!(self.status_500s))?; | |
178 | state.serialize_field("status_503s", &atomic_load!(self.status_503s))?; | |
179 | state.serialize_field("status_504s", &atomic_load!(self.status_504s))?; | |
180 | state.serialize_field("status_508s", &atomic_load!(self.status_508s))?; | |
181 | state.serialize_field("wildcards_filtered", &atomic_load!(self.wildcards_filtered))?; | |
182 | state.serialize_field("responses_filtered", &atomic_load!(self.responses_filtered))?; | |
183 | state.serialize_field( | |
184 | "resources_discovered", | |
185 | &atomic_load!(self.resources_discovered), | |
186 | )?; | |
187 | state.serialize_field("url_format_errors", &atomic_load!(self.url_format_errors))?; | |
188 | state.serialize_field("redirection_errors", &atomic_load!(self.redirection_errors))?; | |
189 | state.serialize_field("connection_errors", &atomic_load!(self.connection_errors))?; | |
190 | state.serialize_field("request_errors", &atomic_load!(self.request_errors))?; | |
191 | state.serialize_field("directory_scan_times", &self.directory_scan_times)?; | |
192 | state.serialize_field("total_runtime", &self.total_runtime)?; | |
193 | ||
194 | state.end() | |
195 | } | |
196 | } | |
197 | ||
198 | /// Deserialize implementation for Stats | |
199 | impl<'a> Deserialize<'a> for Stats { | |
200 | /// Deserialize a Stats object from a serde_json::Value | |
201 | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | |
202 | where | |
203 | D: Deserializer<'a>, | |
204 | { | |
205 | let stats = Self::new(0, false); | |
206 | ||
207 | let map: HashMap<String, Value> = HashMap::deserialize(deserializer)?; | |
208 | ||
209 | for (key, value) in &map { | |
210 | match key.as_str() { | |
211 | "timeouts" => { | |
212 | if let Some(num) = value.as_u64() { | |
213 | if let Ok(parsed) = usize::try_from(num) { | |
214 | atomic_increment!(stats.timeouts, parsed); | |
215 | } | |
216 | } | |
217 | } | |
218 | "requests" => { | |
219 | if let Some(num) = value.as_u64() { | |
220 | if let Ok(parsed) = usize::try_from(num) { | |
221 | atomic_increment!(stats.requests, parsed); | |
222 | } | |
223 | } | |
224 | } | |
225 | "expected_per_scan" => { | |
226 | if let Some(num) = value.as_u64() { | |
227 | if let Ok(parsed) = usize::try_from(num) { | |
228 | atomic_increment!(stats.expected_per_scan, parsed); | |
229 | } | |
230 | } | |
231 | } | |
232 | "total_expected" => { | |
233 | if let Some(num) = value.as_u64() { | |
234 | if let Ok(parsed) = usize::try_from(num) { | |
235 | atomic_increment!(stats.total_expected, parsed); | |
236 | } | |
237 | } | |
238 | } | |
239 | "errors" => { | |
240 | if let Some(num) = value.as_u64() { | |
241 | if let Ok(parsed) = usize::try_from(num) { | |
242 | atomic_increment!(stats.errors, parsed); | |
243 | } | |
244 | } | |
245 | } | |
246 | "successes" => { | |
247 | if let Some(num) = value.as_u64() { | |
248 | if let Ok(parsed) = usize::try_from(num) { | |
249 | atomic_increment!(stats.successes, parsed); | |
250 | } | |
251 | } | |
252 | } | |
253 | "redirects" => { | |
254 | if let Some(num) = value.as_u64() { | |
255 | if let Ok(parsed) = usize::try_from(num) { | |
256 | atomic_increment!(stats.redirects, parsed); | |
257 | } | |
258 | } | |
259 | } | |
260 | "client_errors" => { | |
261 | if let Some(num) = value.as_u64() { | |
262 | if let Ok(parsed) = usize::try_from(num) { | |
263 | atomic_increment!(stats.client_errors, parsed); | |
264 | } | |
265 | } | |
266 | } | |
267 | "server_errors" => { | |
268 | if let Some(num) = value.as_u64() { | |
269 | if let Ok(parsed) = usize::try_from(num) { | |
270 | atomic_increment!(stats.server_errors, parsed); | |
271 | } | |
272 | } | |
273 | } | |
274 | "total_scans" => { | |
275 | if let Some(num) = value.as_u64() { | |
276 | if let Ok(parsed) = usize::try_from(num) { | |
277 | atomic_increment!(stats.total_scans, parsed); | |
278 | } | |
279 | } | |
280 | } | |
281 | "initial_targets" => { | |
282 | if let Some(num) = value.as_u64() { | |
283 | if let Ok(parsed) = usize::try_from(num) { | |
284 | atomic_increment!(stats.initial_targets, parsed); | |
285 | } | |
286 | } | |
287 | } | |
288 | "links_extracted" => { | |
289 | if let Some(num) = value.as_u64() { | |
290 | if let Ok(parsed) = usize::try_from(num) { | |
291 | atomic_increment!(stats.links_extracted, parsed); | |
292 | } | |
293 | } | |
294 | } | |
295 | "status_200s" => { | |
296 | if let Some(num) = value.as_u64() { | |
297 | if let Ok(parsed) = usize::try_from(num) { | |
298 | atomic_increment!(stats.status_200s, parsed); | |
299 | } | |
300 | } | |
301 | } | |
302 | "status_301s" => { | |
303 | if let Some(num) = value.as_u64() { | |
304 | if let Ok(parsed) = usize::try_from(num) { | |
305 | atomic_increment!(stats.status_301s, parsed); | |
306 | } | |
307 | } | |
308 | } | |
309 | "status_302s" => { | |
310 | if let Some(num) = value.as_u64() { | |
311 | if let Ok(parsed) = usize::try_from(num) { | |
312 | atomic_increment!(stats.status_302s, parsed); | |
313 | } | |
314 | } | |
315 | } | |
316 | "status_401s" => { | |
317 | if let Some(num) = value.as_u64() { | |
318 | if let Ok(parsed) = usize::try_from(num) { | |
319 | atomic_increment!(stats.status_401s, parsed); | |
320 | } | |
321 | } | |
322 | } | |
323 | "status_403s" => { | |
324 | if let Some(num) = value.as_u64() { | |
325 | if let Ok(parsed) = usize::try_from(num) { | |
326 | atomic_increment!(stats.status_403s, parsed); | |
327 | } | |
328 | } | |
329 | } | |
330 | "status_429s" => { | |
331 | if let Some(num) = value.as_u64() { | |
332 | if let Ok(parsed) = usize::try_from(num) { | |
333 | atomic_increment!(stats.status_429s, parsed); | |
334 | } | |
335 | } | |
336 | } | |
337 | "status_500s" => { | |
338 | if let Some(num) = value.as_u64() { | |
339 | if let Ok(parsed) = usize::try_from(num) { | |
340 | atomic_increment!(stats.status_500s, parsed); | |
341 | } | |
342 | } | |
343 | } | |
344 | "status_503s" => { | |
345 | if let Some(num) = value.as_u64() { | |
346 | if let Ok(parsed) = usize::try_from(num) { | |
347 | atomic_increment!(stats.status_503s, parsed); | |
348 | } | |
349 | } | |
350 | } | |
351 | "status_504s" => { | |
352 | if let Some(num) = value.as_u64() { | |
353 | if let Ok(parsed) = usize::try_from(num) { | |
354 | atomic_increment!(stats.status_504s, parsed); | |
355 | } | |
356 | } | |
357 | } | |
358 | "status_508s" => { | |
359 | if let Some(num) = value.as_u64() { | |
360 | if let Ok(parsed) = usize::try_from(num) { | |
361 | atomic_increment!(stats.status_508s, parsed); | |
362 | } | |
363 | } | |
364 | } | |
365 | "wildcards_filtered" => { | |
366 | if let Some(num) = value.as_u64() { | |
367 | if let Ok(parsed) = usize::try_from(num) { | |
368 | atomic_increment!(stats.wildcards_filtered, parsed); | |
369 | } | |
370 | } | |
371 | } | |
372 | "responses_filtered" => { | |
373 | if let Some(num) = value.as_u64() { | |
374 | if let Ok(parsed) = usize::try_from(num) { | |
375 | atomic_increment!(stats.responses_filtered, parsed); | |
376 | } | |
377 | } | |
378 | } | |
379 | "resources_discovered" => { | |
380 | if let Some(num) = value.as_u64() { | |
381 | if let Ok(parsed) = usize::try_from(num) { | |
382 | atomic_increment!(stats.resources_discovered, parsed); | |
383 | } | |
384 | } | |
385 | } | |
386 | "url_format_errors" => { | |
387 | if let Some(num) = value.as_u64() { | |
388 | if let Ok(parsed) = usize::try_from(num) { | |
389 | atomic_increment!(stats.url_format_errors, parsed); | |
390 | } | |
391 | } | |
392 | } | |
393 | "redirection_errors" => { | |
394 | if let Some(num) = value.as_u64() { | |
395 | if let Ok(parsed) = usize::try_from(num) { | |
396 | atomic_increment!(stats.redirection_errors, parsed); | |
397 | } | |
398 | } | |
399 | } | |
400 | "connection_errors" => { | |
401 | if let Some(num) = value.as_u64() { | |
402 | if let Ok(parsed) = usize::try_from(num) { | |
403 | atomic_increment!(stats.connection_errors, parsed); | |
404 | } | |
405 | } | |
406 | } | |
407 | "request_errors" => { | |
408 | if let Some(num) = value.as_u64() { | |
409 | if let Ok(parsed) = usize::try_from(num) { | |
410 | atomic_increment!(stats.request_errors, parsed); | |
411 | } | |
412 | } | |
413 | } | |
414 | "directory_scan_times" => { | |
415 | if let Some(arr) = value.as_array() { | |
416 | for val in arr { | |
417 | if let Some(parsed) = val.as_f64() { | |
418 | if let Ok(mut guard) = stats.directory_scan_times.lock() { | |
419 | guard.push(parsed) | |
420 | } | |
421 | } | |
422 | } | |
423 | } | |
424 | } | |
425 | "total_runtime" => { | |
426 | if let Some(arr) = value.as_array() { | |
427 | for val in arr { | |
428 | if let Some(parsed) = val.as_f64() { | |
429 | if let Ok(mut guard) = stats.total_runtime.lock() { | |
430 | guard.push(parsed) | |
431 | } | |
432 | } | |
433 | } | |
434 | } | |
435 | } | |
436 | _ => {} | |
437 | } | |
438 | } | |
439 | ||
440 | Ok(stats) | |
146 | 441 | } |
147 | 442 | } |
148 | 443 |
286 | 286 | Ok(()) |
287 | 287 | } |
288 | 288 | |
289 | /// determines whether or not a given url should be denied based on the user-supplied --dont-scan | |
290 | /// flag | |
291 | pub fn should_deny_url(url: &Url, handles: Arc<Handles>) -> Result<bool> { | |
292 | log::trace!( | |
293 | "enter: should_deny_url({}, {:?}, {:?})", | |
294 | url.as_str(), | |
295 | handles.config.url_denylist, | |
296 | handles.ferox_scans()? | |
297 | ); | |
298 | // normalization for comparison is to remove the trailing / if one exists, this is done for | |
299 | // the given url and any url to which it's compared | |
300 | let normed_url = Url::parse(&url.to_string().trim_end_matches('/'))?; | |
301 | ||
302 | for deny_url in &handles.config.url_denylist { | |
303 | // parse the denying url for easier comparison | |
304 | let denier = Url::parse(deny_url.trim_end_matches('/')) | |
305 | .with_context(|| format!("Could not parse {} as a url", deny_url))?; | |
306 | ||
307 | // simplest case is an exact match, check for it first | |
308 | if normed_url == denier { | |
309 | log::trace!("exit: should_deny_url -> true"); | |
310 | return Ok(true); | |
311 | } | |
312 | ||
313 | match (normed_url.host(), denier.host()) { | |
314 | // .host() will return an enum with ipv4|6 or domain and is comparable | |
315 | // whereas .domain() returns None for ip addresses | |
316 | (Some(normed_host), Some(denier_host)) => { | |
317 | if normed_host != denier_host { | |
318 | // domains don't even match, keep on keepin' on... | |
319 | continue; | |
320 | } | |
321 | } | |
322 | _ => { | |
323 | // one or the other couldn't determine the host value, which probably means | |
324 | // it's not suitable for further comparison | |
325 | continue; | |
326 | } | |
327 | } | |
328 | ||
329 | let normed_host = normed_url.host().unwrap(); // match above will catch errors | |
330 | ||
331 | // at this point, we have a matching set of ips or domain names. now we can process the | |
332 | // url path. The goal is to determine whether the given url's path is a subpath of any | |
333 | // url in the deny list, for example | |
334 | // GIVEN URL URL DENY LIST USER-SPECIFIED URLS TO SCAN | |
335 | // http://some.domain/stuff/things, [http://some.domain/stuff], [http://some.domain] => true | |
336 | // http://some.domain/stuff/things, [http://some.domain/stuff/things], [http://some.domain] => true | |
337 | // http://some.domain/stuff/things, [http://some.domain/api], [http://some.domain] => false | |
338 | // the examples above are all pretty obvious, the kicker comes when the blocking url's | |
339 | // path is a parent to a scanned url | |
340 | // http://some.domain/stuff/things, [http://some.domain/], [http://some.domain/stuff] => false | |
341 | // http://some.domain/api, [http://some.domain/], [http://some.domain/stuff] => true | |
342 | // we want to deny all children of the parent, unless that child is a child of a scan | |
343 | // we specified through -u(s) or --stdin | |
344 | ||
345 | let deny_path = denier.path(); | |
346 | let norm_path = normed_url.path(); | |
347 | ||
348 | if norm_path.starts_with(deny_path) { | |
349 | // at this point, we know that the given normalized path is a sub-path of the | |
350 | // current deny-url, now we just need to check to see if this deny-url is a parent | |
351 | // to a scanned url that is also a parent of the given url | |
352 | for ferox_scan in handles.ferox_scans()?.get_active_scans() { | |
353 | let scanner = Url::parse(ferox_scan.url().trim_end_matches('/')) | |
354 | .with_context(|| format!("Could not parse {} as a url", ferox_scan))?; | |
355 | ||
356 | if let Some(scan_host) = scanner.host() { | |
357 | // same domain/ip check we perform on the denier above | |
358 | if normed_host != scan_host { | |
359 | // domains don't even match, keep on keepin' on... | |
360 | continue; | |
361 | } | |
362 | } else { | |
363 | // couldn't process .host from scanner | |
364 | continue; | |
365 | }; | |
366 | ||
367 | let scan_path = scanner.path(); | |
368 | ||
369 | if scan_path.starts_with(deny_path) && norm_path.starts_with(scan_path) { | |
370 | // user-specified scan url is a sub-path of the deny-urls's path AND the | |
371 | // url to check is a sub-path of the user-specified scan url | |
372 | // | |
373 | // the assumption is the user knew what they wanted and we're going to give | |
374 | // the scanned url precedence, even though it's a sub-path | |
375 | log::trace!("exit: should_deny_url -> false"); | |
376 | return Ok(false); | |
377 | } | |
378 | } | |
379 | log::trace!("exit: should_deny_url -> true"); | |
380 | return Ok(true); | |
381 | } | |
382 | } | |
383 | log::trace!("exit: should_deny_url -> false"); | |
384 | Ok(false) | |
385 | } | |
386 | ||
289 | 387 | #[cfg(test)] |
290 | 388 | mod tests { |
291 | 389 | use super::*; |
390 | use crate::config::Configuration; | |
391 | use crate::scan_manager::{FeroxScans, ScanOrder}; | |
292 | 392 | |
293 | 393 | #[test] |
294 | 394 | /// set_open_file_limit with a low requested limit succeeds |
365 | 465 | fn status_colorizer_returns_as_is() { |
366 | 466 | assert_eq!(status_colorizer("farfignewton"), "farfignewton".to_string()); |
367 | 467 | } |
368 | } | |
468 | ||
469 | #[test] | |
470 | /// provide a url that should be blocked where the denier is an exact match for the tested url | |
471 | /// expect true | |
472 | fn should_deny_url_blocks_when_denier_is_exact_match() { | |
473 | let scan_url = "https://testdomain.com/"; | |
474 | let deny_url = "https://testdomain.com/denied"; | |
475 | let tested_url = Url::parse("https://testdomain.com/denied/").unwrap(); | |
476 | ||
477 | let scans = Arc::new(FeroxScans::default()); | |
478 | scans.add_directory_scan(&scan_url, ScanOrder::Initial); | |
479 | ||
480 | let mut config = Configuration::new().unwrap(); | |
481 | config.url_denylist = vec![String::from(deny_url)]; | |
482 | let config = Arc::new(config); | |
483 | ||
484 | let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0); | |
485 | ||
486 | assert!(should_deny_url(&tested_url, handles).unwrap()); | |
487 | } | |
488 | ||
489 | #[test] | |
490 | /// provide a url that has a different host than the denier but the same path, expect false | |
491 | fn should_deny_url_doesnt_compare_mismatched_domains() { | |
492 | let scan_url = "https://testdomain.com/"; | |
493 | let deny_url = "https://dev.testdomain.com/denied"; | |
494 | let tested_url = Url::parse("https://testdomain.com/denied/").unwrap(); | |
495 | ||
496 | let scans = Arc::new(FeroxScans::default()); | |
497 | scans.add_directory_scan(&scan_url, ScanOrder::Initial); | |
498 | ||
499 | let mut config = Configuration::new().unwrap(); | |
500 | config.url_denylist = vec![String::from(deny_url)]; | |
501 | let config = Arc::new(config); | |
502 | ||
503 | let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0); | |
504 | ||
505 | assert!(!should_deny_url(&tested_url, handles).unwrap()); | |
506 | } | |
507 | ||
508 | #[test] | |
509 | /// provide a denier from which we can't check a host, which results in no comparison, expect false | |
510 | fn should_deny_url_doesnt_compare_non_domains() { | |
511 | let scan_url = "https://testdomain.com/"; | |
512 | let deny_url = "unix:/run/foo.socket"; | |
513 | let tested_url = Url::parse("https://testdomain.com/denied/").unwrap(); | |
514 | ||
515 | let scans = Arc::new(FeroxScans::default()); | |
516 | scans.add_directory_scan(&scan_url, ScanOrder::Initial); | |
517 | ||
518 | let mut config = Configuration::new().unwrap(); | |
519 | config.url_denylist = vec![String::from(deny_url)]; | |
520 | let config = Arc::new(config); | |
521 | ||
522 | let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0); | |
523 | ||
524 | assert!(!should_deny_url(&tested_url, handles).unwrap()); | |
525 | } | |
526 | ||
527 | #[test] | |
528 | /// provide a url that has a different host than the denier but the same path, expect false | |
529 | /// because the denier is a parent to the tested, even tho the scanned doesn't compare, it | |
530 | /// still returns true | |
531 | fn should_deny_url_doesnt_compare_mismatched_domains_in_scanned() { | |
532 | let deny_url = "https://testdomain.com/"; | |
533 | let scan_url = "https://dev.testdomain.com/denied"; | |
534 | let tested_url = Url::parse("https://testdomain.com/denied/").unwrap(); | |
535 | ||
536 | let scans = Arc::new(FeroxScans::default()); | |
537 | scans.add_directory_scan(&scan_url, ScanOrder::Initial); | |
538 | ||
539 | let mut config = Configuration::new().unwrap(); | |
540 | config.url_denylist = vec![String::from(deny_url)]; | |
541 | let config = Arc::new(config); | |
542 | ||
543 | let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0); | |
544 | ||
545 | assert!(should_deny_url(&tested_url, handles).unwrap()); | |
546 | } | |
547 | ||
548 | #[test] | |
549 | /// provide a denier from which we can't check a host, which results in no comparison, expect false | |
550 | /// because the denier is a parent to the tested, even tho the scanned doesn't compare, it | |
551 | /// still returns true | |
552 | fn should_deny_url_doesnt_compare_non_domains_in_scanned() { | |
553 | let deny_url = "https://testdomain.com/"; | |
554 | let scan_url = "unix:/run/foo.socket"; | |
555 | let tested_url = Url::parse("https://testdomain.com/denied/").unwrap(); | |
556 | ||
557 | let scans = Arc::new(FeroxScans::default()); | |
558 | scans.add_directory_scan(&scan_url, ScanOrder::Initial); | |
559 | ||
560 | let mut config = Configuration::new().unwrap(); | |
561 | config.url_denylist = vec![String::from(deny_url)]; | |
562 | let config = Arc::new(config); | |
563 | ||
564 | let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0); | |
565 | ||
566 | assert!(should_deny_url(&tested_url, handles).unwrap()); | |
567 | } | |
568 | ||
569 | #[test] | |
570 | /// provide a denier where the tested url is a sub-path and the scanned url is not, expect true | |
571 | fn should_deny_url_blocks_child() { | |
572 | let scan_url = "https://testdomain.com/"; | |
573 | let deny_url = "https://testdomain.com/api"; | |
574 | let tested_url = Url::parse("https://testdomain.com/api/denied/").unwrap(); | |
575 | ||
576 | let scans = Arc::new(FeroxScans::default()); | |
577 | scans.add_directory_scan(&scan_url, ScanOrder::Initial); | |
578 | ||
579 | let mut config = Configuration::new().unwrap(); | |
580 | config.url_denylist = vec![String::from(deny_url)]; | |
581 | let config = Arc::new(config); | |
582 | ||
583 | let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0); | |
584 | ||
585 | assert!(should_deny_url(&tested_url, handles).unwrap()); | |
586 | } | |
587 | ||
588 | #[test] | |
589 | /// provide a denier where the tested url is not a sub-path and the scanned url is not, expect false | |
590 | fn should_deny_url_doesnt_block_non_child() { | |
591 | let scan_url = "https://testdomain.com/"; | |
592 | let deny_url = "https://testdomain.com/api"; | |
593 | let tested_url = Url::parse("https://testdomain.com/not-denied/").unwrap(); | |
594 | ||
595 | let scans = Arc::new(FeroxScans::default()); | |
596 | scans.add_directory_scan(&scan_url, ScanOrder::Initial); | |
597 | ||
598 | let mut config = Configuration::new().unwrap(); | |
599 | config.url_denylist = vec![String::from(deny_url)]; | |
600 | let config = Arc::new(config); | |
601 | ||
602 | let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0); | |
603 | ||
604 | assert!(!should_deny_url(&tested_url, handles).unwrap()); | |
605 | } | |
606 | ||
607 | #[test] | |
608 | /// provide a denier where the tested url is a sub-path and the scanned url is not, expect true | |
609 | fn should_deny_url_blocks_child_when_scan_url_isnt_parent() { | |
610 | let scan_url = "https://testdomain.com/api"; | |
611 | let deny_url = "https://testdomain.com/"; | |
612 | let tested_url = Url::parse("https://testdomain.com/stuff/").unwrap(); | |
613 | ||
614 | let scans = Arc::new(FeroxScans::default()); | |
615 | scans.add_directory_scan(&scan_url, ScanOrder::Initial); | |
616 | ||
617 | let mut config = Configuration::new().unwrap(); | |
618 | config.url_denylist = vec![String::from(deny_url)]; | |
619 | let config = Arc::new(config); | |
620 | ||
621 | let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0); | |
622 | ||
623 | assert!(should_deny_url(&tested_url, handles).unwrap()); | |
624 | } | |
625 | ||
626 | #[test] | |
627 | /// provide a denier where the tested url is not a sub-path and the scanned url is not, expect false | |
628 | fn should_deny_url_doesnt_block_child_when_scan_url_is_parent() { | |
629 | let scan_url = "https://testdomain.com/api"; | |
630 | let deny_url = "https://testdomain.com/"; | |
631 | let tested_url = Url::parse("https://testdomain.com/api/not-denied/").unwrap(); | |
632 | ||
633 | let scans = Arc::new(FeroxScans::default()); | |
634 | scans.add_directory_scan(&scan_url, ScanOrder::Initial); | |
635 | ||
636 | let mut config = Configuration::new().unwrap(); | |
637 | config.url_denylist = vec![String::from(deny_url)]; | |
638 | let config = Arc::new(config); | |
639 | ||
640 | let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0); | |
641 | ||
642 | assert!(!should_deny_url(&tested_url, handles).unwrap()); | |
643 | } | |
644 | } |
108 | 108 | .and(predicate::str::contains("Header")) |
109 | 109 | .and(predicate::str::contains("stuff: things")) |
110 | 110 | .and(predicate::str::contains("mostuff: mothings")) |
111 | .and(predicate::str::contains("─┴─")), | |
112 | ); | |
113 | } | |
114 | ||
115 | #[test] | |
116 | /// test allows non-existent wordlist to trigger the banner printing to stderr | |
117 | /// expect to see all mandatory prints + multiple dont scan entries | |
118 | fn banner_prints_denied_urls() { | |
119 | Command::cargo_bin("feroxbuster") | |
120 | .unwrap() | |
121 | .arg("--url") | |
122 | .arg("http://localhost") | |
123 | .arg("--dont-scan") | |
124 | .arg("http://dont-scan.me") | |
125 | .arg("--dont-scan") | |
126 | .arg("https://also-not.me") | |
127 | .assert() | |
128 | .success() | |
129 | .stderr( | |
130 | predicate::str::contains("─┬─") | |
131 | .and(predicate::str::contains("Target Url")) | |
132 | .and(predicate::str::contains("http://localhost")) | |
133 | .and(predicate::str::contains("Threads")) | |
134 | .and(predicate::str::contains("Wordlist")) | |
135 | .and(predicate::str::contains("Status Codes")) | |
136 | .and(predicate::str::contains("Timeout (secs)")) | |
137 | .and(predicate::str::contains("User-Agent")) | |
138 | .and(predicate::str::contains("Don't Scan")) | |
139 | .and(predicate::str::contains("http://dont-scan.me")) | |
140 | .and(predicate::str::contains("https://also-not.me")) | |
111 | 141 | .and(predicate::str::contains("─┴─")), |
112 | 142 | ); |
113 | 143 | } |
0 | mod utils; | |
1 | use assert_cmd::prelude::*; | |
2 | use assert_cmd::Command; | |
3 | use httpmock::Method::GET; | |
4 | use httpmock::MockServer; | |
5 | use predicates::prelude::*; | |
6 | use utils::{setup_tmp_directory, teardown_tmp_directory}; | |
7 | ||
8 | #[test] | |
9 | /// test that the deny list prevents a request if the requested url is a match | |
10 | fn deny_list_works_during_with_a_normal_scan() { | |
11 | let srv = MockServer::start(); | |
12 | let (tmp_dir, file) = setup_tmp_directory(&["LICENSE".to_string()], "wordlist").unwrap(); | |
13 | ||
14 | let mock = srv.mock(|when, then| { | |
15 | when.method(GET).path("/LICENSE"); | |
16 | then.status(200).body("this is a test"); | |
17 | }); | |
18 | ||
19 | let cmd = Command::cargo_bin("feroxbuster") | |
20 | .unwrap() | |
21 | .arg("--url") | |
22 | .arg(srv.url("/")) | |
23 | .arg("--wordlist") | |
24 | .arg(file.as_os_str()) | |
25 | .arg("--dont-scan") | |
26 | .arg(srv.url("/LICENSE")) | |
27 | .unwrap(); | |
28 | ||
29 | teardown_tmp_directory(tmp_dir); | |
30 | ||
31 | cmd.assert() | |
32 | .success() | |
33 | .stdout(predicate::str::contains(srv.url("/LICENSE")).not()); | |
34 | ||
35 | assert_eq!(mock.hits(), 0); | |
36 | } | |
37 | ||
38 | #[test] | |
39 | /// test that the deny list prevents requests of urls found during extraction | |
40 | fn deny_list_works_during_extraction() { | |
41 | let srv = MockServer::start(); | |
42 | let (tmp_dir, file) = setup_tmp_directory(&["LICENSE".to_string()], "wordlist").unwrap(); | |
43 | ||
44 | let mock = srv.mock(|when, then| { | |
45 | when.method(GET).path("/LICENSE"); | |
46 | then.status(200) | |
47 | .body(&srv.url("'/homepage/assets/img/icons/handshake.svg'")); | |
48 | }); | |
49 | ||
50 | let mock_two = srv.mock(|when, then| { | |
51 | when.method(GET) | |
52 | .path("/homepage/assets/img/icons/handshake.svg"); | |
53 | then.status(200); | |
54 | }); | |
55 | ||
56 | let cmd = Command::cargo_bin("feroxbuster") | |
57 | .unwrap() | |
58 | .arg("--url") | |
59 | .arg(srv.url("/")) | |
60 | .arg("--wordlist") | |
61 | .arg(file.as_os_str()) | |
62 | .arg("--extract-links") | |
63 | .arg("--dont-scan") | |
64 | .arg(srv.url("/homepage/")) | |
65 | .unwrap(); | |
66 | ||
67 | cmd.assert().success().stdout( | |
68 | predicate::str::contains("/LICENSE") | |
69 | .and(predicate::str::contains("200")) | |
70 | .and(predicate::str::contains("/homepage/assets/img/icons/handshake.svg").not()), | |
71 | ); | |
72 | ||
73 | assert_eq!(mock.hits(), 1); | |
74 | assert_eq!(mock_two.hits(), 0); | |
75 | teardown_tmp_directory(tmp_dir); | |
76 | } | |
77 | ||
78 | #[test] | |
79 | /// test that the deny list prevents requests of urls found during recursion | |
80 | fn deny_list_works_during_recursion() { | |
81 | let srv = MockServer::start(); | |
82 | let urls = [ | |
83 | "js".to_string(), | |
84 | "prod".to_string(), | |
85 | "dev".to_string(), | |
86 | "file.js".to_string(), | |
87 | ]; | |
88 | let (tmp_dir, file) = setup_tmp_directory(&urls, "wordlist").unwrap(); | |
89 | ||
90 | let js_mock = srv.mock(|when, then| { | |
91 | when.method(GET).path("/js"); | |
92 | then.status(301).header("Location", &srv.url("/js/")); | |
93 | }); | |
94 | ||
95 | let js_prod_mock = srv.mock(|when, then| { | |
96 | when.method(GET).path("/js/prod"); | |
97 | then.status(301).header("Location", &srv.url("/js/prod/")); | |
98 | }); | |
99 | ||
100 | let js_dev_mock = srv.mock(|when, then| { | |
101 | when.method(GET).path("/js/dev"); | |
102 | then.status(301).header("Location", &srv.url("/js/dev/")); | |
103 | }); | |
104 | ||
105 | let js_dev_file_mock = srv.mock(|when, then| { | |
106 | when.method(GET).path("/js/dev/file.js"); | |
107 | then.status(200) | |
108 | .body("this is a test and is more bytes than other ones"); | |
109 | }); | |
110 | ||
111 | let cmd = Command::cargo_bin("feroxbuster") | |
112 | .unwrap() | |
113 | .arg("--url") | |
114 | .arg(srv.url("/")) | |
115 | .arg("--wordlist") | |
116 | .arg(file.as_os_str()) | |
117 | .arg("-t") | |
118 | .arg("1") | |
119 | .arg("--dont-scan") | |
120 | .arg(srv.url("/js/dev")) | |
121 | .unwrap(); | |
122 | ||
123 | cmd.assert().success().stdout( | |
124 | predicate::str::is_match("301.*js") | |
125 | .unwrap() | |
126 | .and(predicate::str::is_match("301.*js/prod").unwrap()) | |
127 | .and(predicate::str::is_match("301.*js/dev").unwrap()) | |
128 | .not() | |
129 | .and(predicate::str::is_match("200.*js/dev/file.js").unwrap()) | |
130 | .not(), | |
131 | ); | |
132 | ||
133 | assert_eq!(js_mock.hits(), 1); | |
134 | assert_eq!(js_prod_mock.hits(), 1); | |
135 | assert_eq!(js_dev_mock.hits(), 0); | |
136 | assert_eq!(js_dev_file_mock.hits(), 0); | |
137 | ||
138 | teardown_tmp_directory(tmp_dir); | |
139 | } | |
140 | ||
141 | #[test] | |
142 | /// test that the deny list prevents requests of urls found during recursion when the denier is a | |
143 | /// parent of a user-specified scan | |
144 | fn deny_list_works_during_recursion_with_inverted_parents() { | |
145 | let srv = MockServer::start(); | |
146 | let urls = [ | |
147 | "js".to_string(), | |
148 | "prod".to_string(), | |
149 | "dev".to_string(), | |
150 | "api".to_string(), | |
151 | "file.js".to_string(), | |
152 | ]; | |
153 | let (tmp_dir, file) = setup_tmp_directory(&urls, "wordlist").unwrap(); | |
154 | ||
155 | let js_mock = srv.mock(|when, then| { | |
156 | when.method(GET).path("/js"); | |
157 | then.status(301).header("Location", &srv.url("/js/")); | |
158 | }); | |
159 | ||
160 | let api_mock = srv.mock(|when, then| { | |
161 | when.method(GET).path("/api"); | |
162 | then.status(200); | |
163 | }); | |
164 | ||
165 | let js_prod_mock = srv.mock(|when, then| { | |
166 | when.method(GET).path("/js/prod"); | |
167 | then.status(301).header("Location", &srv.url("/js/prod/")); | |
168 | }); | |
169 | ||
170 | let js_dev_mock = srv.mock(|when, then| { | |
171 | when.method(GET).path("/js/dev"); | |
172 | then.status(301).header("Location", &srv.url("/js/dev/")); | |
173 | }); | |
174 | ||
175 | let js_dev_file_mock = srv.mock(|when, then| { | |
176 | when.method(GET).path("/js/dev/file.js"); | |
177 | then.status(200) | |
178 | .body("this is a test and is more bytes than other ones"); | |
179 | }); | |
180 | ||
181 | let cmd = Command::cargo_bin("feroxbuster") | |
182 | .unwrap() | |
183 | .arg("--url") | |
184 | .arg(srv.url("/js")) | |
185 | .arg("--wordlist") | |
186 | .arg(file.as_os_str()) | |
187 | .arg("-t") | |
188 | .arg("1") | |
189 | .arg("-vvvv") | |
190 | .arg("--dont-scan") | |
191 | .arg(srv.url("/")) | |
192 | .unwrap(); | |
193 | ||
194 | cmd.assert().success().stdout( | |
195 | predicate::str::is_match("301.*js") | |
196 | .unwrap() | |
197 | .and(predicate::str::is_match("301.*js/prod").unwrap()) | |
198 | .and(predicate::str::is_match("301.*js/dev").unwrap()) | |
199 | .and(predicate::str::is_match("200.*js/dev/file.js").unwrap()) | |
200 | .and(predicate::str::is_match("200.*api").unwrap()) | |
201 | .not(), | |
202 | ); | |
203 | ||
204 | assert_eq!(js_mock.hits(), 1); | |
205 | assert_eq!(js_prod_mock.hits(), 1); | |
206 | assert_eq!(js_dev_mock.hits(), 1); | |
207 | assert_eq!(js_dev_file_mock.hits(), 1); | |
208 | assert_eq!(api_mock.hits(), 0); | |
209 | ||
210 | teardown_tmp_directory(tmp_dir); | |
211 | } |
223 | 223 | |
224 | 224 | teardown_tmp_directory(tmp_dir); |
225 | 225 | |
226 | assert_eq!(contents.contains("WLD"), true); | |
227 | assert_eq!(contents.contains("Got"), true); | |
228 | assert_eq!(contents.contains("200"), true); | |
229 | assert_eq!(contents.contains("(url length: 32)"), true); | |
230 | assert_eq!(contents.contains("(url length: 96)"), true); | |
226 | assert!(contents.contains("WLD")); | |
227 | assert!(contents.contains("Got")); | |
228 | assert!(contents.contains("200")); | |
229 | assert!(contents.contains("(url length: 32)")); | |
230 | assert!(contents.contains("(url length: 96)")); | |
231 | 231 | |
232 | 232 | cmd.assert().success().stdout( |
233 | 233 | predicate::str::contains("WLD") |
390 | 390 | |
391 | 391 | teardown_tmp_directory(tmp_dir); |
392 | 392 | |
393 | assert_eq!(contents.contains("WLD"), true); | |
394 | assert_eq!(contents.contains("Got"), true); | |
395 | assert_eq!(contents.contains("200"), true); | |
396 | assert_eq!(contents.contains("(url length: 32)"), true); | |
397 | assert_eq!(contents.contains("(url length: 96)"), true); | |
393 | assert!(contents.contains("WLD")); | |
394 | assert!(contents.contains("Got")); | |
395 | assert!(contents.contains("200")); | |
396 | assert!(contents.contains("(url length: 32)")); | |
397 | assert!(contents.contains("(url length: 96)")); | |
398 | 398 | |
399 | 399 | cmd.assert().success().stdout( |
400 | 400 | predicate::str::contains("WLD") |
450 | 450 | |
451 | 451 | teardown_tmp_directory(tmp_dir); |
452 | 452 | |
453 | assert_eq!(contents.contains("WLD"), true); | |
454 | assert_eq!(contents.contains("301"), true); | |
455 | assert_eq!(contents.contains("/some-redirect"), true); | |
456 | assert_eq!(contents.contains("redirects to => "), true); | |
457 | assert_eq!(contents.contains(&srv.url("/")), true); | |
458 | assert_eq!(contents.contains("(url length: 32)"), true); | |
453 | assert!(contents.contains("WLD")); | |
454 | assert!(contents.contains("301")); | |
455 | assert!(contents.contains("/some-redirect")); | |
456 | assert!(contents.contains("redirects to => ")); | |
457 | assert!(contents.contains(&srv.url("/"))); | |
458 | assert!(contents.contains("(url length: 32)")); | |
459 | 459 | |
460 | 460 | cmd.assert().success().stdout( |
461 | 461 | predicate::str::contains("redirects to => ") |
0 | use assert_cmd::Command; | |
1 | use predicates::prelude::*; | |
2 | ||
3 | #[test] | |
4 | /// specify an incorrect param (-fc) with --help after it on the command line | |
5 | /// old behavior printed | |
6 | /// error: Found argument '-c' which wasn't expected, or isn't valid in this context | |
7 | /// | |
8 | /// USAGE: | |
9 | /// feroxbuster --add-slash --url <URL>... | |
10 | /// | |
11 | /// For more information try --help | |
12 | /// | |
13 | /// the new behavior we expect to see is to print the long form help message, of which | |
14 | /// Ludicrous speed... go! is near the bottom of that output, so we can test for that | |
15 | fn parser_incorrect_param_with_tack_tack_help() { | |
16 | Command::cargo_bin("feroxbuster") | |
17 | .unwrap() | |
18 | .arg("-fc") | |
19 | .arg("--help") | |
20 | .assert() | |
21 | .failure() | |
22 | .stdout(predicate::str::contains("Ludicrous speed... go!")); | |
23 | } | |
24 | ||
25 | #[test] | |
26 | /// specify an incorrect param (-fc) with --help after it on the command line | |
27 | /// old behavior printed | |
28 | /// error: Found argument '-c' which wasn't expected, or isn't valid in this context | |
29 | /// | |
30 | /// USAGE: | |
31 | /// feroxbuster --add-slash --url <URL>... | |
32 | /// | |
33 | /// For more information try --help | |
34 | /// | |
35 | /// the new behavior we expect to see is to print the long form help message, of which | |
36 | /// Ludicrous speed... go! is near the bottom of that output, so we can test for that | |
37 | fn parser_incorrect_param_with_tack_h() { | |
38 | Command::cargo_bin("feroxbuster") | |
39 | .unwrap() | |
40 | .arg("-fc") | |
41 | .arg("-h") | |
42 | .assert() | |
43 | .failure() | |
44 | .stdout(predicate::str::contains("Ludicrous speed... go!")); | |
45 | } |