Codebase list feroxbuster / a9052ee
New upstream version 2.3.0 Sophie Brun 2 years ago
42 changed file(s) with 2026 addition(s) and 912 deletion(s). Raw diff Collapse all Expand all
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"
0 # These are supported funding model platforms
1
2 github: [epi052]
3 ko_fi: epi052
1010
1111 ## Static analysis checks
1212 - [ ] 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`
1414 - [ ] All existing tests pass
1515
1616 ## Documentation
33
44 jobs:
55 build-nix:
6 env:
7 IN_PIPELINE: true
68 runs-on: ${{ matrix.os }}
79 if: github.ref == 'refs/heads/main'
810 strategy:
911 matrix:
10 type: [ubuntu-x64, ubuntu-x86]
12 type: [ubuntu-x64, ubuntu-x86, armv7, aarch64]
1113 include:
1214 - type: ubuntu-x64
1315 os: ubuntu-latest
2123 name: x86-linux-feroxbuster
2224 path: target/i686-unknown-linux-musl/release/feroxbuster
2325 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
2438 steps:
2539 - uses: actions/checkout@v2
2640 - name: Install System Dependencies
2741 run: |
42 env
2843 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
3045 - uses: actions-rs/toolchain@v1
3146 with:
3247 toolchain: stable
4257 args: --release --target=${{ matrix.target }}
4358 - name: Strip symbols from binary
4459 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 }}
4661 - name: Build tar.gz for homebrew installs
4762 if: matrix.type == 'ubuntu-x64'
4863 run: |
7186 path: ./target/x86_64-unknown-linux-musl/debian/*
7287
7388 build-macos:
89 env:
90 IN_PIPELINE: true
7491 runs-on: macos-latest
7592 if: github.ref == 'refs/heads/main'
7693 steps:
101118 path: x86_64-macos-feroxbuster.tar.gz
102119
103120 build-windows:
121 env:
122 IN_PIPELINE: true
104123 runs-on: ${{ matrix.os }}
105124 if: github.ref == 'refs/heads/main'
106125 strategy:
133152 with:
134153 name: ${{ matrix.name }}
135154 path: ${{ matrix.path }}
136
11 # It is not intended for manual editing.
22 [[package]]
33 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"
77 dependencies = [
88 "memchr",
99 ]
1919
2020 [[package]]
2121 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"
3725
3826 [[package]]
3927 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"
4331 dependencies = [
4432 "term",
4533 ]
4634
4735 [[package]]
4836 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 = [
5441 "serde",
5542 "serde_json",
5643 ]
5744
5845 [[package]]
5946 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"
6350 dependencies = [
6451 "bstr",
6552 "doc-comment",
7158
7259 [[package]]
7360 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"
7764 dependencies = [
7865 "concurrent-queue",
7966 "event-listener",
8269
8370 [[package]]
8471 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"
8875 dependencies = [
8976 "async-task",
9077 "concurrent-queue",
9178 "fastrand",
9279 "futures-lite",
9380 "once_cell",
94 "vec-arena",
81 "slab",
9582 ]
9683
9784 [[package]]
11299
113100 [[package]]
114101 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"
118105 dependencies = [
119106 "concurrent-queue",
120107 "fastrand",
121108 "futures-lite",
122109 "libc",
123110 "log",
124 "nb-connect",
125111 "once_cell",
126112 "parking",
127113 "polling",
128 "vec-arena",
114 "slab",
115 "socket2",
129116 "waker-fn",
130117 "winapi",
131118 ]
132119
133120 [[package]]
134121 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"
138125 dependencies = [
139126 "event-listener",
140127 ]
159146
160147 [[package]]
161148 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"
165152 dependencies = [
166153 "async-io",
167154 "blocking",
168 "cfg-if 1.0.0",
155 "cfg-if",
169156 "event-listener",
170157 "futures-lite",
158 "libc",
171159 "once_cell",
172 "signal-hook 0.3.4",
160 "signal-hook",
173161 "winapi",
174162 ]
175163
209197
210198 [[package]]
211199 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"
215203 dependencies = [
216204 "proc-macro2",
217205 "quote",
278266 version = "1.2.1"
279267 source = "registry+https://github.com/rust-lang/crates.io-index"
280268 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 ]
292269
293270 [[package]]
294271 name = "blocking"
306283
307284 [[package]]
308285 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"
312289 dependencies = [
313290 "lazy_static",
314291 "memchr",
317294
318295 [[package]]
319296 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"
329300
330301 [[package]]
331302 name = "bytes"
341312
342313 [[package]]
343314 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"
353318
354319 [[package]]
355320 name = "cfg-if"
383348
384349 [[package]]
385350 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"
389354 dependencies = [
390355 "encode_unicode",
391356 "lazy_static",
397362 ]
398363
399364 [[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]]
406365 name = "core-foundation"
407366 version = "0.9.1"
408367 source = "registry+https://github.com/rust-lang/crates.io-index"
420379
421380 [[package]]
422381 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",
429387 "lazy_static",
430388 ]
431389
432390 [[package]]
433391 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"
437395 dependencies = [
438396 "bitflags",
439397 "crossterm_winapi",
440 "lazy_static",
441398 "libc",
442399 "mio",
443400 "parking_lot",
444 "signal-hook 0.1.17",
401 "signal-hook",
402 "signal-hook-mio",
445403 "winapi",
446404 ]
447405
448406 [[package]]
449407 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"
453411 dependencies = [
454412 "winapi",
455413 ]
462420
463421 [[package]]
464422 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"
468426 dependencies = [
469427 "quote",
470428 "syn",
472430
473431 [[package]]
474432 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"
478436 dependencies = [
479437 "nix",
480438 "winapi",
482440
483441 [[package]]
484442 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"
488446 dependencies = [
489447 "curl-sys",
490448 "libc",
497455
498456 [[package]]
499457 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"
503461 dependencies = [
504462 "cc",
505463 "libc",
525483
526484 [[package]]
527485 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"
531508 dependencies = [
532509 "libc",
533510 "redox_users",
535512 ]
536513
537514 [[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"
551519 dependencies = [
552520 "libc",
553521 "redox_users",
587555 source = "registry+https://github.com/rust-lang/crates.io-index"
588556 checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065"
589557 dependencies = [
590 "cfg-if 1.0.0",
558 "cfg-if",
591559 ]
592560
593561 [[package]]
594562 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"
598566 dependencies = [
599567 "atty",
600568 "humantime",
610578 checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59"
611579
612580 [[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]]
625581 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"
629585 dependencies = [
630586 "instant",
631587 ]
632588
633589 [[package]]
634590 name = "feroxbuster"
635 version = "2.2.1"
591 version = "2.3.0"
636592 dependencies = [
637593 "anyhow",
638594 "assert_cmd",
640596 "console",
641597 "crossterm",
642598 "ctrlc",
643 "dirs 3.0.1",
599 "dirs",
644600 "env_logger",
645601 "futures",
646602 "fuzzyhash",
679635 ]
680636
681637 [[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]]
694638 name = "fnv"
695639 version = "1.0.7"
696640 source = "registry+https://github.com/rust-lang/crates.io-index"
713657
714658 [[package]]
715659 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"
719663 dependencies = [
720664 "matches",
721665 "percent-encoding",
723667
724668 [[package]]
725669 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"
729673 dependencies = [
730674 "futures-channel",
731675 "futures-core",
738682
739683 [[package]]
740684 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"
744688 dependencies = [
745689 "futures-core",
746690 "futures-sink",
748692
749693 [[package]]
750694 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"
754698
755699 [[package]]
756700 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"
760704 dependencies = [
761705 "futures-core",
762706 "futures-task",
765709
766710 [[package]]
767711 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"
771715
772716 [[package]]
773717 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"
777721 dependencies = [
778722 "fastrand",
779723 "futures-core",
786730
787731 [[package]]
788732 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",
793738 "proc-macro-hack",
794739 "proc-macro2",
795740 "quote",
798743
799744 [[package]]
800745 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"
804749
805750 [[package]]
806751 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"
813755
814756 [[package]]
815757 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",
820763 "futures-channel",
821764 "futures-core",
822765 "futures-io",
839782
840783 [[package]]
841784 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",
860792 ]
861793
862794 [[package]]
874806
875807 [[package]]
876808 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"
880812 dependencies = [
881813 "bytes",
882814 "fnv",
889821 "tokio",
890822 "tokio-util",
891823 "tracing",
892 "tracing-futures",
893824 ]
894825
895826 [[package]]
909840
910841 [[package]]
911842 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"
915846 dependencies = [
916847 "bytes",
917848 "fnv",
920851
921852 [[package]]
922853 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"
926857 dependencies = [
927858 "bytes",
928859 "http",
860 "pin-project-lite",
929861 ]
930862
931863 [[package]]
932864 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"
936868
937869 [[package]]
938870 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"
942874
943875 [[package]]
944876 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"
948880 dependencies = [
949881 "assert-json-diff",
950882 "async-object-pool",
975907
976908 [[package]]
977909 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"
981913 dependencies = [
982914 "bytes",
983915 "futures-channel",
989921 "httparse",
990922 "httpdate",
991923 "itoa",
992 "pin-project",
924 "pin-project-lite",
993925 "socket2",
994926 "tokio",
995927 "tower-service",
1012944
1013945 [[package]]
1014946 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"
1018950 dependencies = [
1019951 "matches",
1020952 "unicode-bidi",
1023955
1024956 [[package]]
1025957 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"
1029961 dependencies = [
1030962 "autocfg",
1031963 "hashbrown",
1049981 source = "registry+https://github.com/rust-lang/crates.io-index"
1050982 checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
1051983 dependencies = [
1052 "cfg-if 1.0.0",
984 "cfg-if",
1053985 ]
1054986
1055987 [[package]]
1060992
1061993 [[package]]
1062994 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",
10671000 "crossbeam-utils",
10681001 "curl",
10691002 "curl-sys",
10701003 "encoding_rs",
1071 "flume",
1004 "event-listener",
10721005 "futures-lite",
10731006 "http",
10741007 "log",
10751008 "mime",
10761009 "once_cell",
1010 "polling",
10771011 "slab",
10781012 "sluice",
10791013 "tracing",
10841018
10851019 [[package]]
10861020 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"
10901024 dependencies = [
10911025 "either",
10921026 ]
10991033
11001034 [[package]]
11011035 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"
11051039 dependencies = [
11061040 "wasm-bindgen",
11071041 ]
11171051
11181052 [[package]]
11191053 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"
11231057 dependencies = [
11241058 "ascii-canvas",
11251059 "atty",
11401074
11411075 [[package]]
11421076 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"
11461080 dependencies = [
11471081 "regex",
11481082 ]
11691103
11701104 [[package]]
11711105 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"
11751109
11761110 [[package]]
11771111 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"
11811115
11821116 [[package]]
11831117 name = "libnghttp2-sys"
11911125
11921126 [[package]]
11931127 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"
11971131 dependencies = [
11981132 "cc",
11991133 "libc",
12031137
12041138 [[package]]
12051139 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"
12091143 dependencies = [
12101144 "scopeguard",
12111145 ]
12161150 source = "registry+https://github.com/rust-lang/crates.io-index"
12171151 checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
12181152 dependencies = [
1219 "cfg-if 1.0.0",
1153 "cfg-if",
12201154 "value-bag",
12211155 ]
12221156
12281162
12291163 [[package]]
12301164 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"
12341168
12351169 [[package]]
12361170 name = "mime"
12401174
12411175 [[package]]
12421176 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"
12461180 dependencies = [
12471181 "libc",
12481182 "log",
12531187
12541188 [[package]]
12551189 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 = [
12611194 "winapi",
12621195 ]
12631196
12801213 ]
12811214
12821215 [[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]]
12931216 name = "new_debug_unreachable"
12941217 version = "1.0.4"
12951218 source = "registry+https://github.com/rust-lang/crates.io-index"
12971220
12981221 [[package]]
12991222 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"
13031226 dependencies = [
13041227 "bitflags",
13051228 "cc",
1306 "cfg-if 0.1.10",
1229 "cfg-if",
13071230 "libc",
13081231 ]
13091232
13491272
13501273 [[package]]
13511274 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"
13551278
13561279 [[package]]
13571280 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"
13611284 dependencies = [
13621285 "bitflags",
1363 "cfg-if 1.0.0",
1286 "cfg-if",
13641287 "foreign-types",
1365 "lazy_static",
1366 "libc",
1288 "libc",
1289 "once_cell",
13671290 "openssl-sys",
13681291 ]
13691292
13701293 [[package]]
13711294 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"
13751298
13761299 [[package]]
13771300 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"
13811304 dependencies = [
13821305 "cc",
13831306 ]
13841307
13851308 [[package]]
13861309 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"
13901313 dependencies = [
13911314 "autocfg",
13921315 "cc",
14191342 source = "registry+https://github.com/rust-lang/crates.io-index"
14201343 checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
14211344 dependencies = [
1422 "cfg-if 1.0.0",
1345 "cfg-if",
14231346 "instant",
14241347 "libc",
1425 "redox_syscall 0.2.5",
1348 "redox_syscall",
14261349 "smallvec",
14271350 "winapi",
14281351 ]
14541377
14551378 [[package]]
14561379 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"
14601383
14611384 [[package]]
14621385 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"
14661389 dependencies = [
14671390 "pin-project-internal",
14681391 ]
14691392
14701393 [[package]]
14711394 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"
14751398 dependencies = [
14761399 "proc-macro2",
14771400 "quote",
14801403
14811404 [[package]]
14821405 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"
14861409
14871410 [[package]]
14881411 name = "pin-utils"
14981421
14991422 [[package]]
15001423 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",
15061429 "libc",
15071430 "log",
1508 "wepoll-sys",
1431 "wepoll-ffi",
15091432 "winapi",
15101433 ]
15111434
15231446
15241447 [[package]]
15251448 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"
15291452 dependencies = [
15301453 "difference",
15311454 "float-cmp",
15511474 ]
15521475
15531476 [[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]]
15781477 name = "proc-macro-hack"
15791478 version = "0.5.19"
15801479 source = "registry+https://github.com/rust-lang/crates.io-index"
15881487
15891488 [[package]]
15901489 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"
15941493 dependencies = [
15951494 "unicode-xid",
15961495 ]
16271526
16281527 [[package]]
16291528 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"
16331532 dependencies = [
16341533 "ppv-lite86",
16351534 "rand_core",
16411540 source = "registry+https://github.com/rust-lang/crates.io-index"
16421541 checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
16431542 dependencies = [
1644 "getrandom 0.2.2",
1543 "getrandom",
16451544 ]
16461545
16471546 [[package]]
16551554
16561555 [[package]]
16571556 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"
16671560 dependencies = [
16681561 "bitflags",
16691562 ]
16701563
16711564 [[package]]
16721565 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",
16801572 ]
16811573
16821574 [[package]]
16831575 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"
16871579 dependencies = [
16881580 "aho-corasick",
16891581 "memchr",
16901582 "regex-syntax",
1691 "thread_local",
16921583 ]
16931584
16941585 [[package]]
16951586 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"
17021590
17031591 [[package]]
17041592 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"
17081596
17091597 [[package]]
17101598 name = "remove_dir_all"
17171605
17181606 [[package]]
17191607 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"
17231611 dependencies = [
17241612 "base64",
17251613 "bytes",
17521640
17531641 [[package]]
17541642 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"
17731655
17741656 [[package]]
17751657 name = "ryu"
17951677
17961678 [[package]]
17971679 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"
18011683 dependencies = [
18021684 "bitflags",
18031685 "core-foundation",
18081690
18091691 [[package]]
18101692 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"
18141696 dependencies = [
18151697 "core-foundation-sys",
18161698 "libc",
18181700
18191701 [[package]]
18201702 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"
18241706 dependencies = [
18251707 "serde_derive",
18261708 ]
18271709
18281710 [[package]]
18291711 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"
18331715 dependencies = [
18341716 "proc-macro2",
18351717 "quote",
18381720
18391721 [[package]]
18401722 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"
18441726 dependencies = [
18451727 "itoa",
18461728 "ryu",
18711753
18721754 [[package]]
18731755 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"
18771769 dependencies = [
18781770 "libc",
18791771 "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",
18911773 ]
18921774
18931775 [[package]]
18941776 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"
18981780 dependencies = [
18991781 "libc",
19001782 ]
19011783
19021784 [[package]]
19031785 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"
19071789
19081790 [[package]]
19091791 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"
19131795
19141796 [[package]]
19151797 name = "sluice"
19301812
19311813 [[package]]
19321814 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",
19491821 ]
19501822
19511823 [[package]]
19681840
19691841 [[package]]
19701842 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"
19741846 dependencies = [
19751847 "proc-macro2",
19761848 "quote",
19831855 source = "registry+https://github.com/rust-lang/crates.io-index"
19841856 checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
19851857 dependencies = [
1986 "cfg-if 1.0.0",
1858 "cfg-if",
19871859 "libc",
19881860 "rand",
1989 "redox_syscall 0.2.5",
1861 "redox_syscall",
19901862 "remove_dir_all",
19911863 "winapi",
19921864 ]
19931865
19941866 [[package]]
19951867 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",
20021874 "winapi",
20031875 ]
20041876
20131885
20141886 [[package]]
20151887 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"
20191891 dependencies = [
20201892 "libc",
20211893 "winapi",
20321904
20331905 [[package]]
20341906 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"
20381910 dependencies = [
20391911 "thiserror-impl",
20401912 ]
20411913
20421914 [[package]]
20431915 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"
20471919 dependencies = [
20481920 "proc-macro2",
20491921 "quote",
20511923 ]
20521924
20531925 [[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]]
20631926 name = "tiny-keccak"
20641927 version = "2.0.2"
20651928 source = "registry+https://github.com/rust-lang/crates.io-index"
20701933
20711934 [[package]]
20721935 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"
20761939 dependencies = [
20771940 "tinyvec_macros",
20781941 ]
20851948
20861949 [[package]]
20871950 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"
20911954 dependencies = [
20921955 "autocfg",
20931956 "bytes",
21051968
21061969 [[package]]
21071970 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"
21111974 dependencies = [
21121975 "proc-macro2",
21131976 "quote",
21382001
21392002 [[package]]
21402003 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"
21442007 dependencies = [
21452008 "futures-core",
21462009 "pin-project-lite",
21492012
21502013 [[package]]
21512014 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"
21552018 dependencies = [
21562019 "bytes",
21572020 "futures-core",
21782041
21792042 [[package]]
21802043 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",
21862049 "log",
21872050 "pin-project-lite",
21882051 "tracing-attributes",
21912054
21922055 [[package]]
21932056 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"
21972060 dependencies = [
21982061 "proc-macro2",
21992062 "quote",
22022065
22032066 [[package]]
22042067 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"
22082071 dependencies = [
22092072 "lazy_static",
22102073 ]
22332096
22342097 [[package]]
22352098 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"
22392102 dependencies = [
22402103 "matches",
22412104 ]
22422105
22432106 [[package]]
22442107 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"
22482111 dependencies = [
22492112 "tinyvec",
22502113 ]
22572120
22582121 [[package]]
22592122 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"
22632126
22642127 [[package]]
22652128 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"
22692132 dependencies = [
22702133 "form_urlencoded",
22712134 "idna",
22792142 source = "registry+https://github.com/rust-lang/crates.io-index"
22802143 checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
22812144 dependencies = [
2282 "getrandom 0.2.2",
2145 "getrandom",
22832146 ]
22842147
22852148 [[package]]
22862149 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"
22902153 dependencies = [
22912154 "ctor",
2155 "version_check",
22922156 ]
22932157
22942158 [[package]]
22952159 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"
23052163
23062164 [[package]]
23072165 name = "vec_map"
23112169
23122170 [[package]]
23132171 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"
23172175
23182176 [[package]]
23192177 name = "wait-timeout"
23422200
23432201 [[package]]
23442202 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"
23512203 version = "0.10.2+wasi-snapshot-preview1"
23522204 source = "registry+https://github.com/rust-lang/crates.io-index"
23532205 checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
23542206
23552207 [[package]]
23562208 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",
23622214 "serde",
23632215 "serde_json",
23642216 "wasm-bindgen-macro",
23662218
23672219 [[package]]
23682220 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"
23722224 dependencies = [
23732225 "bumpalo",
23742226 "lazy_static",
23812233
23822234 [[package]]
23832235 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",
23892241 "js-sys",
23902242 "wasm-bindgen",
23912243 "web-sys",
23932245
23942246 [[package]]
23952247 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"
23992251 dependencies = [
24002252 "quote",
24012253 "wasm-bindgen-macro-support",
24032255
24042256 [[package]]
24052257 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"
24092261 dependencies = [
24102262 "proc-macro2",
24112263 "quote",
24162268
24172269 [[package]]
24182270 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"
24222274
24232275 [[package]]
24242276 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"
24282280 dependencies = [
24292281 "js-sys",
24302282 "wasm-bindgen",
24312283 ]
24322284
24332285 [[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"
24382290 dependencies = [
24392291 "cc",
24402292 ]
00 [package]
11 name = "feroxbuster"
2 version = "2.2.1"
2 version = "2.3.0"
33 authors = ["Ben 'epi' Risher <[email protected]>"]
44 license = "MIT"
55 edition = "2018"
1818 clap = "2.33"
1919 regex = "1"
2020 lazy_static = "1.4"
21 dirs = "3.0"
2122
2223 [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"]}
2627 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"] }
2930 clap = "2.33"
3031 lazy_static = "1.4"
3132 toml = "0.5"
3233 serde = { version = "1.0", features = ["derive", "rc"] }
33 serde_json = "1.0.62"
34 serde_json = "1.0.64"
3435 uuid = { version = "0.8", features = ["v4"] }
3536 indicatif = "0.15"
3637 console = "0.14"
3738 openssl = { version = "0.10", features = ["vendored"] }
3839 dirs = "3.0"
3940 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"
4344 fuzzyhash = "0.2.1"
4445 anyhow = "1.0"
4546 leaky-bucket = "0.10.0"
4647
4748 [dev-dependencies]
4849 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"
5253
5354 [profile.release]
5455 lto = true
6263 assets = [
6364 ["target/release/feroxbuster", "/usr/bin/", "755"],
6465 ["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"],
6569 ]
55 datadir = $(datarootdir)
66 example_config = ferox-config.toml.example
77 config_file = ferox-config.toml
8 completion_dir = shell_completions
9 completion_prefix = $(completion_dir)/$(BIN)
810
11 BIN=feroxbuster
912 SHR_SOURCES = $(shell find src -type f -wholename '*src/*.rs') Cargo.toml Cargo.lock
1013
1114 RELEASE = debug
1215 DEBUG ?= 0
13 ifeq (0,$(DEBUG))
16
17 ifeq (0, $(DEBUG))
1418 ARGS = --release
1519 RELEASE = release
1620 endif
2226
2327 TARGET = target/$(RELEASE)
2428
25 .PHONY: all clean distclean install uninstall update
26
27 BIN=feroxbuster
28 DESKTOP=$(APPID).desktop
29 .PHONY: all clean install uninstall test update
2930
3031 all: cli
32 cli: $(TARGET)/$(BIN) $(TARGET)/$(BIN).1.gz $(SHR_SOURCES)
33 install: all install-cli
3134
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
3339
3440 clean:
3541 cargo clean
3642
37 distclean: clean
38 rm -rf .cargo vendor Cargo.lock vendor.tar
39
4043 vendor: vendor.tar
4144
4245 vendor.tar:
43 mkdir -p .cargo
44 cargo vendor | head -n -1 > .cargo/config
45 echo 'directory = "vendor"' >> .cargo/config
46 cargo vendor
4647 tar pcf vendor.tar vendor
4748 rm -rf vendor
4849
4950 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)"
5155 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)"
5357
54 install: all install-cli
55
56 uninstall-cli:
58 uninstall:
5759 rm -f "$(DESTDIR)$(bindir)/$(BIN)"
5860 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"
6565
6666 extract:
67 ifeq ($(VENDORED),1)
67 ifeq (1, $(VENDORED))
6868 tar pxf vendor.tar
6969 endif
7070
7171 $(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)
7375
7476 $(TARGET)/$(BIN).1.gz: $(TARGET)/$(BIN)
7577 help2man --no-info $< | gzip -c > [email protected]
6969 - [Snap Install](#snap-install)
7070 - [Homebrew on MacOS and Linux](#homebrew-on-macos-and-linux)
7171 - [Cargo Install](#cargo-install)
72 - [Kali Install](#kali-install)
7273 - [apt Install](#apt-install)
7374 - [AUR Install](#aur-install)
7475 - [Docker Install](#docker-install)
105106 - [Silence all Output or Be Kinda Quiet (new in `v2.0.0`)](#silence-all-output-or-be-kinda-quiet-new-in-v200)
106107 - [Auto-tune or Auto-bail from Scans (new in `v2.1.0`)](#auto-tune-or-auto-bail-from-scans-new-in-v210)
107108 - [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)
108110 - [Comparison w/ Similar Tools](#-comparison-w-similar-tools)
109111 - [Common Problems/Issues (FAQ)](#-common-problemsissues-faq)
110112 - [No file descriptors available](#no-file-descriptors-available)
117119
118120 ### Download a Release
119121
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.
122125
123126 #### Linux (32 and 64-bit) & MacOS
124127
193196 cargo install feroxbuster
194197 ```
195198
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
196209 ### apt Install
197210
198211 Download `feroxbuster_amd64.deb` from the [Releases](https://github.com/epi052/feroxbuster/releases) section. After
210223
211224 ```
212225 yay -S feroxbuster-git
226 ```
227
228 ### BlackArch install
229
230 Install `feroxbuster` on BlackArch Linux:
231
232 ```
233 pacman -S feroxbuster
213234 ```
214235
215236 ### Docker Install
395416 # dont_filter = true
396417 # extract_links = true
397418 # depth = 1
419 # url_denylist = ["https://dont-scan-me.com/"]
398420 # filter_size = [5174]
399421 # filter_regex = ["^ignore me$"]
400422 # filter_similar = ["https://somesite.com/soft404"]
428450 feroxbuster [FLAGS] [OPTIONS] --url <URL>...
429451
430452 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
448498
449499 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
451503 -d, --depth <RECURSION_DEPTH>
452504 Maximum recursion depth, a depth of 0 is infinite recursion (default: 4)
453505
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
456512 -X, --filter-regex <REGEX>...
457513 Filter out messages via regular expression matching on the response's body (ex: -X '^ignore me$')
458514
459515 --filter-similar-to <UNWANTED_PAGE>...
460516 Filter out pages that are similar to the given page (ex. --filter-similar-to http://site.xyz/soft404)
461517
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
467533 --parallel <PARALLEL_SCANS>
468534 Run parallel feroxbuster instances (one child process per url passed via stdin)
469535
470536 -p, --proxy <PROXY>
471537 Proxy to use for requests (ex: http(s)://host:port, socks5(h)://host:port)
472538
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
474542 --rate-limit <RATE_LIMIT>
475543 Limit number of requests per second (per directory) (default: 0, i.e. no limit)
476544
483551 --resume-from <STATE_FILE>
484552 State file from which to resume a partially complete scan (ex. --resume-from ferox-1606586780.state)
485553
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
487557 -s, --status-codes <STATUS_CODE>...
488558 Status Codes to include (allow list) (default: 200 204 301 302 307 308 401 403 405)
489559
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
496580 ```
497581
498582 ## 📊 Scan's Display Explained
815899 Using the menu is pretty simple:
816900 - Press `ENTER` to view the menu
817901 - 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)
819903 - 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.
822907
823908 ![cancel-scan](img/cancel-scan.gif)
824909
9251010 \_ feroxbuster --silent --extract-links --auto-bail -u https://target-ten
9261011 ```
9271012
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
9281041 ## 🧐 Comparison w/ Similar Tools
9291042
9301043 There are quite a few similar tools for forced browsing/content discovery. Burp Suite Pro, Dirb, Dirbuster, etc...
9481061 | fast | ✔ | ✔ | ✔ |
9491062 | allows recursion | ✔ | | ✔ |
9501063 | can specify query parameters | ✔ | | ✔ |
951 | SOCKS proxy support | ✔ | | |
1064 | SOCKS proxy support | ✔ | | ✔ |
9521065 | multiple target scan (via stdin or multiple -u) | ✔ | | ✔ |
9531066 | configuration file for default value override | ✔ | | ✔ |
9541067 | can accept urls via STDIN as part of a pipeline | ✔ | | ✔ |
9751088 | automatically tune scans based on errors/403s/429s (`v2.1.0`) | ✔ | | |
9761089 | automatically stop scans based on errors/403s/429s (`v2.1.0`) | ✔ | | ✔ |
9771090 | run scans in parallel (1 process per target) (`v2.2.0`) | ✔ | | |
1091 | prevent requests to given domain/folder/file (`v2.3.0`) | ✔ | | |
9781092 | **huge** number of other options | | | ✔ |
9791093
9801094 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};
02 extern crate clap;
3 extern crate dirs;
14
25 use clap::Shell;
36
1922 for shell in &shells {
2023 app.gen_completions("feroxbuster", *shell, outdir);
2124 }
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 }
2285 }
2929 # redirects = true
3030 # insecure = true
3131 # extensions = ["php", "html"]
32 # url_denylist = ["http://dont-scan.me", "https://also-not.me"]
3233 # no_recursion = true
3334 # add_slash = true
3435 # stdin = true
Binary diff not shown
Binary diff not shown
22 BASE_URL=https://github.com/epi052/feroxbuster/releases/latest/download
33
44 MAC_ZIP=x86_64-macos-feroxbuster.zip
5 MAC_URL="${BASE_URL}/${MAC_ZIP}"
5 MAC_URL="$BASE_URL/$MAC_ZIP"
66
77 LIN32_ZIP=x86-linux-feroxbuster.zip
8 LIN32_URL="${BASE_URL}/${LIN32_ZIP}"
8 LIN32_URL="$BASE_URL/$LIN32_ZIP"
99
1010 LIN64_ZIP=x86_64-linux-feroxbuster.zip
11 LIN64_URL="${BASE_URL}/${LIN64_ZIP}"
11 LIN64_URL="$BASE_URL/$LIN64_ZIP"
1212
1313 EMOJI_URL=https://gist.github.com/epi052/8196b550ea51d0907ad4b93751b1b57d/raw/6112c9f32ae07922983fdc549c54fd3fb9a38e4c/NotoColorEmoji.ttf
1414
1515 echo "[+] Installing feroxbuster!"
1616
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
5223 fi
5324
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
5461
5562 chmod +x ./feroxbuster
5663
5764 echo "[+] Installed feroxbuster version $(./feroxbuster -V)"
58
59
60
4040 '--user-agent=[Sets the User-Agent (default: feroxbuster/VERSION)]' \
4141 '*-x+[File extension(s) to search for (ex: -x php -x pdf js)]' \
4242 '*--extensions=[File extension(s) to search for (ex: -x php -x pdf js)]' \
43 '*--dont-scan=[URL(s) to exclude from recursion/scans]' \
4344 '*-H+[Specify HTTP headers (ex: -H Header:val '\''stuff: things'\'')]' \
4445 '*--headers=[Specify HTTP headers (ex: -H Header:val '\''stuff: things'\'')]' \
4546 '*-Q+[Specify URL query parameters (ex: -Q token=stuff -Q secret=key)]' \
4545 [CompletionResult]::new('--user-agent', 'user-agent', [CompletionResultType]::ParameterName, 'Sets the User-Agent (default: feroxbuster/VERSION)')
4646 [CompletionResult]::new('-x', 'x', [CompletionResultType]::ParameterName, 'File extension(s) to search for (ex: -x php -x pdf js)')
4747 [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')
4849 [CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Specify HTTP headers (ex: -H Header:val ''stuff: things'')')
4950 [CompletionResult]::new('--headers', 'headers', [CompletionResultType]::ParameterName, 'Specify HTTP headers (ex: -H Header:val ''stuff: things'')')
5051 [CompletionResult]::new('-Q', 'Q', [CompletionResultType]::ParameterName, 'Specify URL query parameters (ex: -Q token=stuff -Q secret=key)')
1919
2020 case "${cmd}" in
2121 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 "
2323 if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
2424 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
2525 return 0
127127 return 0
128128 ;;
129129 -x)
130 COMPREPLY=($(compgen -f "${cur}"))
131 return 0
132 ;;
133 --dont-scan)
130134 COMPREPLY=($(compgen -f "${cur}"))
131135 return 0
132136 ;;
221225 esac
222226 }
223227
224 complete -F _feroxbuster -o bashdefault -o default feroxbuster
228 complete -F _feroxbuster -o bashdefault -o default -o plusdirs feroxbuster
1111 complete -c feroxbuster -n "__fish_use_subcommand" -l debug-log -d 'Output file to write log entries (use w/ --json for JSON entries)'
1212 complete -c feroxbuster -n "__fish_use_subcommand" -s a -l user-agent -d 'Sets the User-Agent (default: feroxbuster/VERSION)'
1313 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'
1415 complete -c feroxbuster -n "__fish_use_subcommand" -s H -l headers -d 'Specify HTTP headers (ex: -H Header:val \'stuff: things\')'
1516 complete -c feroxbuster -n "__fish_use_subcommand" -s Q -l query -d 'Specify URL query parameters (ex: -Q token=stuff -Q secret=key)'
1617 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)'
133133 /// represents Configuration.auto_bail
134134 auto_bail: BannerEntry,
135135
136 /// represents Configuration.url_denylist
137 url_denylist: Vec<BannerEntry>,
138
136139 /// current version of feroxbuster
137140 pub(super) version: String,
138141
145148 /// Create a new Banner from a Configuration and live targets
146149 pub fn new(tgts: &[String], config: &Configuration) -> Self {
147150 let mut targets = Vec::new();
151 let mut url_denylist = Vec::new();
148152 let mut code_filters = Vec::new();
149153 let mut replay_codes = Vec::new();
150154 let mut headers = Vec::new();
157161
158162 for target in tgts {
159163 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));
160168 }
161169
162170 let mut codes = vec![];
322330 rate_limit,
323331 scan_limit,
324332 time_limit,
333 url_denylist,
325334 config: cfg,
326335 version: VERSION.to_string(),
327336 update_status: UpdateStatus::Unknown,
412421 writeln!(&mut writer, "{}", target)?;
413422 }
414423
424 for denied_url in &self.url_denylist {
425 writeln!(&mut writer, "{}", denied_url)?;
426 }
427
415428 writeln!(&mut writer, "{}", self.threads)?;
416429 writeln!(&mut writer, "{}", self.wordlist)?;
417430 writeln!(&mut writer, "{}", self.status_codes)?;
247247 /// Filter out response bodies that meet a certain threshold of similarity
248248 #[serde(default)]
249249 pub filter_similar: Vec<String>,
250
251 /// URLs that should never be scanned/recursed into
252 #[serde(default)]
253 pub url_denylist: Vec<String>,
250254 }
251255
252256 impl Default for Configuration {
303307 extensions: Vec::new(),
304308 filter_size: Vec::new(),
305309 filter_regex: Vec::new(),
310 url_denylist: Vec::new(),
306311 filter_line_count: Vec::new(),
307312 filter_word_count: Vec::new(),
308313 filter_status: Vec::new(),
340345 /// - **user_agent**: `feroxbuster/VERSION`
341346 /// - **insecure**: `false` (don't be insecure, i.e. don't allow invalid certs)
342347 /// - **extensions**: `None`
348 /// - **url_denylist**: `None`
343349 /// - **filter_size**: `None`
344350 /// - **filter_similar**: `None`
345351 /// - **filter_regex**: `None`
537543 config.extensions = arg.map(|val| val.to_string()).collect();
538544 }
539545
546 if let Some(arg) = args.values_of("url_denylist") {
547 config.url_denylist = arg.map(|val| val.to_string()).collect();
548 }
549
540550 if let Some(arg) = args.values_of("filter_regex") {
541551 config.filter_regex = arg.map(|val| val.to_string()).collect();
542552 }
766776 update_if_not_default!(&mut conf.insecure, new.insecure, false);
767777 update_if_not_default!(&mut conf.extract_links, new.extract_links, false);
768778 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 );
769784 update_if_not_default!(&mut conf.headers, new.headers, HashMap::new());
770785 update_if_not_default!(&mut conf.queries, new.queries, Vec::new());
771786 update_if_not_default!(&mut conf.no_recursion, new.no_recursion, false);
2828 redirects = true
2929 insecure = true
3030 extensions = ["html", "php", "js"]
31 url_denylist = ["http://dont-scan.me", "https://also-not.me"]
3132 headers = {stuff = "things", mostuff = "mothings"}
3233 queries = [["name","value"], ["rick", "astley"]]
3334 no_recursion = true
7172 assert_eq!(config.timeout, timeout());
7273 assert_eq!(config.verbosity, 0);
7374 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);
7677 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);
8081 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);
8990 assert_eq!(config.queries, Vec::new());
91 assert_eq!(config.filter_size, Vec::<u64>::new());
9092 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());
9294 assert_eq!(config.filter_regex, Vec::<String>::new());
9395 assert_eq!(config.filter_similar, Vec::<String>::new());
9496 assert_eq!(config.filter_word_count, Vec::<usize>::new());
185187 /// parse the test config and see that the value parsed is correct
186188 fn config_reads_silent() {
187189 let config = setup_config_test();
188 assert_eq!(config.silent, true);
190 assert!(config.silent);
189191 }
190192
191193 #[test]
192194 /// parse the test config and see that the value parsed is correct
193195 fn config_reads_quiet() {
194196 let config = setup_config_test();
195 assert_eq!(config.quiet, true);
197 assert!(config.quiet);
196198 }
197199
198200 #[test]
199201 /// parse the test config and see that the value parsed is correct
200202 fn config_reads_json() {
201203 let config = setup_config_test();
202 assert_eq!(config.json, true);
204 assert!(config.json);
203205 }
204206
205207 #[test]
206208 /// parse the test config and see that the value parsed is correct
207209 fn config_reads_auto_bail() {
208210 let config = setup_config_test();
209 assert_eq!(config.auto_bail, true);
211 assert!(config.auto_bail);
210212 }
211213
212214 #[test]
213215 /// parse the test config and see that the value parsed is correct
214216 fn config_reads_auto_tune() {
215217 let config = setup_config_test();
216 assert_eq!(config.auto_tune, true);
218 assert!(config.auto_tune);
217219 }
218220
219221 #[test]
234236 /// parse the test config and see that the value parsed is correct
235237 fn config_reads_redirects() {
236238 let config = setup_config_test();
237 assert_eq!(config.redirects, true);
239 assert!(config.redirects);
238240 }
239241
240242 #[test]
241243 /// parse the test config and see that the value parsed is correct
242244 fn config_reads_insecure() {
243245 let config = setup_config_test();
244 assert_eq!(config.insecure, true);
246 assert!(config.insecure);
245247 }
246248
247249 #[test]
248250 /// parse the test config and see that the value parsed is correct
249251 fn config_reads_no_recursion() {
250252 let config = setup_config_test();
251 assert_eq!(config.no_recursion, true);
253 assert!(config.no_recursion);
252254 }
253255
254256 #[test]
255257 /// parse the test config and see that the value parsed is correct
256258 fn config_reads_stdin() {
257259 let config = setup_config_test();
258 assert_eq!(config.stdin, true);
260 assert!(config.stdin);
259261 }
260262
261263 #[test]
262264 /// parse the test config and see that the value parsed is correct
263265 fn config_reads_dont_filter() {
264266 let config = setup_config_test();
265 assert_eq!(config.dont_filter, true);
267 assert!(config.dont_filter);
266268 }
267269
268270 #[test]
269271 /// parse the test config and see that the value parsed is correct
270272 fn config_reads_add_slash() {
271273 let config = setup_config_test();
272 assert_eq!(config.add_slash, true);
274 assert!(config.add_slash);
273275 }
274276
275277 #[test]
276278 /// parse the test config and see that the value parsed is correct
277279 fn config_reads_extract_links() {
278280 let config = setup_config_test();
279 assert_eq!(config.extract_links, true);
281 assert!(config.extract_links);
280282 }
281283
282284 #[test]
288290
289291 #[test]
290292 /// 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
291303 fn config_reads_filter_regex() {
292304 let config = setup_config_test();
293305 assert_eq!(config.filter_regex, vec!["^ignore me$"]);
332344 /// parse the test config and see that the value parsed is correct
333345 fn config_reads_save_state() {
334346 let config = setup_config_test();
335 assert_eq!(config.save_state, false);
347 assert!(!config.save_state);
336348 }
337349
338350 #[test]
363375 /// parse the test config and see that the values parsed are correct
364376 fn config_reads_queries() {
365377 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 ];
369382 assert_eq!(config.queries, queries);
370383 }
371384
0 use std::collections::HashSet;
10 use std::sync::Arc;
21
32 use reqwest::StatusCode;
5251 TryRecursion(Box<FeroxResponse>),
5352
5453 /// Send a pointer to the wordlist to the recursion handler
55 UpdateWordlist(Arc<HashSet<String>>),
54 UpdateWordlist(Arc<Vec<String>>),
5655
5756 /// Instruct the ScanHandler to join on all known scans, use sender to notify main when done
5857 JoinTasks(Sender<bool>),
138138 Self {
139139 receiver,
140140 tx_file,
141 config,
142141 file_task,
142 config,
143143 }
144144 }
145145
0 use std::collections::HashSet;
10 use std::sync::Arc;
21
32 use anyhow::{bail, Result};
43 use tokio::sync::{mpsc, Semaphore};
54
6 use crate::response::FeroxResponse;
7 use crate::url::FeroxUrl;
85 use crate::{
6 response::FeroxResponse,
97 scan_manager::{FeroxScan, FeroxScans, ScanOrder},
108 scanner::FeroxScanner,
119 statistics::StatField::TotalScans,
10 url::FeroxUrl,
11 utils::should_deny_url,
1212 CommandReceiver, CommandSender, FeroxChannel, Joiner, SLEEP_DURATION,
1313 };
1414
1515 use super::command::Command::AddToUsizeField;
1616 use super::*;
17 use reqwest::Url;
1718 use tokio::time::Duration;
1819
1920 #[derive(Debug)]
5354 receiver: CommandReceiver,
5455
5556 /// wordlist (re)used for each scan
56 wordlist: std::sync::Mutex<Option<Arc<HashSet<String>>>>,
57 wordlist: std::sync::Mutex<Option<Arc<Vec<String>>>>,
5758
5859 /// group of scans that need to be joined
5960 tasks: Vec<Arc<FeroxScan>>,
104105 }
105106
106107 /// Set the wordlist
107 fn wordlist(&self, wordlist: Arc<HashSet<String>>) {
108 fn wordlist(&self, wordlist: Arc<Vec<String>>) {
108109 if let Ok(mut guard) = self.wordlist.lock() {
109110 if guard.is_none() {
110111 let _ = std::mem::replace(&mut *guard, Some(wordlist));
174175 }
175176
176177 /// 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>>> {
178179 if let Ok(guard) = self.wordlist.lock().as_ref() {
179180 if let Some(list) = guard.as_ref() {
180181 return Ok(list.clone());
187188 /// wrapper around scanning a url to stay DRY
188189 async fn ordered_scan_url(&mut self, targets: Vec<String>, order: ScanOrder) -> Result<()> {
189190 log::trace!("enter: ordered_scan_url({:?}, {:?})", targets, order);
191 let should_test_deny = !self.handles.config.url_denylist.is_empty();
190192
191193 for target in targets {
192194 if self.data.contains(&target) && matches!(order, ScanOrder::Latest) {
203205 self.data.add_directory_scan(&target, order).1 // add the new target; return FeroxScan
204206 };
205207
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
206215 let list = self.get_wordlist()?;
207216
208217 log::info!("scan handler received {} - beginning scan", target);
243252 async fn try_recursion(&mut self, response: Box<FeroxResponse>) -> Result<()> {
244253 log::trace!("enter: try_recursion({:?})", response,);
245254
255 if !response.is_directory() {
256 // not a directory, quick exit
257 return Ok(());
258 }
259
246260 let mut base_depth = 1_usize;
247261
248262 for (base_url, base_url_depth) in &self.depths {
256270 return Ok(());
257271 }
258272
259 if !response.is_directory() {
260 // not a directory
261 return Ok(());
262 }
263
264273 let targets = vec![response.url().to_string()];
265274 self.ordered_scan_url(targets, ScanOrder::Latest).await?;
266275
00 use super::*;
1 use crate::utils::should_deny_url;
12 use crate::{
23 client,
34 event_handlers::{
5253
5354 /// Extractor implementation
5455 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);
6269 let recursive = if self.handles.config.no_recursion {
6370 RecursionStatus::NotRecursive
6471 } else {
120127 rx.await?;
121128 }
122129 }
130 log::trace!("exit: request_links");
123131 Ok(())
124132 }
125133
301309 bail!("previously seen url");
302310 }
303311
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
304323 // make the request and store the response
305324 let new_response = logged_request(&new_url, self.handles.clone()).await?;
306325
390409 FeroxResponse::from(response, true, self.handles.config.output_level).await;
391410
392411 log::trace!("exit: get_robots_file -> {}", ferox_response);
393 return Ok(ferox_response);
412 Ok(ferox_response)
394413 }
395414
396415 /// update total number of links extracted and expected responses
6666 assert_eq!(r_paths.len(), expected.len());
6767 assert_eq!(b_paths.len(), expected.len());
6868 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()));
7171 }
7272 }
7373
8484 assert_eq!(r_paths.len(), expected.len());
8585 assert_eq!(b_paths.len(), expected.len());
8686 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()));
8989 }
9090 }
9191
101101 assert_eq!(r_paths.len(), expected.len());
102102 assert_eq!(b_paths.len(), expected.len());
103103 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()));
106106 }
107107 }
108108
117117 assert_eq!(r_paths.len(), expected.len());
118118 assert_eq!(b_paths.len(), expected.len());
119119 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()));
122122 }
123123 }
124124
00 use std::{
1 collections::HashSet,
21 env::args,
32 fs::File,
43 io::{stderr, BufRead, BufReader},
4039 }
4140
4241 /// 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>>> {
4443 log::trace!("enter: get_unique_words_from_wordlist({})", path);
4544
4645 let file = File::open(&path).with_context(|| format!("Could not open {}", path))?;
4746
4847 let reader = BufReader::new(file);
4948
50 let mut words = HashSet::new();
49 let mut words = Vec::new();
5150
5251 for line in reader.lines() {
5352 let result = match line {
5958 continue;
6059 }
6160
62 words.insert(result);
61 words.push(result);
6362 }
6463
6564 log::trace!(
7776 // so that will allow for cheap/safe sharing of a single wordlist across multi-target scans
7877 // as well as additional directories found as part of recursion
7978
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)?;
8580
8681 if words.len() == 0 {
8782 bail!("Did not find any words in {}", handles.config.wordlist);
00 use clap::{App, Arg, ArgGroup};
11 use lazy_static::lazy_static;
22 use regex::Regex;
3 use std::env;
4 use std::process;
35
46 lazy_static! {
57 /// Regex used to validate values passed to --time-limit
1517
1618 /// 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
1719 pub fn initialize() -> App<'static, 'static> {
18 App::new("feroxbuster")
20 let mut app = App::new("feroxbuster")
1921 .version(env!("CARGO_PKG_VERSION"))
2022 .author("Ben 'epi' Risher (@epi052)")
2123 .about("A fast, simple, recursive content discovery tool written in Rust")
216218 ),
217219 )
218220 .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(
219232 Arg::with_name("headers")
220233 .short("H")
221234 .long("headers")
409422
410423 Ludicrous speed... go!
411424 ./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
413439 }
414440
415441 /// Validate that a string is formatted as a number followed by s, m, h, or d (10d, 30s, etc...)
3030 let separator = "─".to_string();
3131
3232 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)",
3434 style("comma-separated").yellow(),
3535 style("cancel").red(),
36 style("ex").cyan(),
3637 );
3738
3839 let name = format!(
4243 "💀"
4344 );
4445
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
4553 let longest = measure_text_width(&instructions).max(measure_text_width(&name));
4654
4755 let border = separator.repeat(longest);
4856
4957 let padded_name = pad_str(&name, longest, Alignment::Center, None);
58 let padded_force = pad_str(&force_msg, longest, Alignment::Center, None);
5059
5160 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);
5362
5463 Self {
5564 separator,
92101 self.term.write_line(msg).unwrap_or_default();
93102 }
94103
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
96121 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
106161 }
107162
108163 /// 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)> {
110165 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))
112169 } else {
113170 None
114171 }
3535 pub(super) scan_type: ScanType,
3636
3737 /// The order in which the scan was received
38 pub(super) scan_order: ScanOrder,
38 pub(crate) scan_order: ScanOrder,
3939
4040 /// Number of requests to populate the progress bar with
4141 pub(super) num_requests: u64,
251251 }
252252
253253 /// 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 {
255255 let menu_pause_duration = Duration::from_millis(SLEEP_DURATION);
256256
257257 let mut num_cancelled = 0_usize;
272272 Err(..) => continue,
273273 };
274274
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 };
276280
277281 if input == 'y' || input == '\n' {
278282 self.menu.println(&format!("Stopping {}...", selected.url));
304308
305309 let mut num_cancelled = 0_usize;
306310
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;
309313 };
310314
311315 self.menu.clear_screen();
4646
4747 /// Simple call to produce a JSON string using the given FeroxState
4848 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"))
5151 }
5252 }
5151 let urls = FeroxScans::default();
5252 let url = "http://unknown_url";
5353 let (result, _scan) = urls.add_scan(url, ScanType::Directory, ScanOrder::Latest);
54 assert_eq!(result, true);
54 assert!(result);
5555 }
5656
5757 #[test]
7070 Some(pb),
7171 );
7272
73 assert_eq!(urls.insert(scan), true);
73 assert!(urls.insert(scan));
7474
7575 let (result, _scan) = urls.add_scan(url, ScanType::Directory, ScanOrder::Latest);
7676
77 assert_eq!(result, false);
77 assert!(!result);
7878 }
7979
8080 #[test]
9292 Some(pb),
9393 );
9494
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());
104102
105103 scan.stop_progress_bar();
106104
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());
116112 }
117113
118114 #[test]
130126 None,
131127 );
132128
133 assert_eq!(urls.insert(scan), true);
129 assert!(urls.insert(scan));
134130
135131 let (result, _scan) = urls.add_scan(url, ScanType::File, ScanOrder::Latest);
136132
137 assert_eq!(result, false);
133 assert!(!result);
138134 }
139135
140136 #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
170166 .await
171167 .unwrap();
172168
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));
175171
176172 urls.display_scans().await;
177173 }
329325
330326 assert_eq!(response.url().as_str(), "https://nerdcore.com/css");
331327 assert_eq!(response.url().path(), "/css");
332 assert_eq!(response.wildcard(), true);
328 assert!(response.wildcard());
333329 assert_eq!(response.status().as_u16(), 301);
334330 assert_eq!(response.content_length(), 173);
335331 assert_eq!(response.line_count(), 10);
382378
383379 let json_state = ferox_state.as_json().unwrap();
384380 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"}}}}]"#,
386382 saved_id, VERSION
387383 );
388384 println!("{}\n{}", expected, json_state);
520516 fn split_to_nums_is_correct() {
521517 let menu = Menu::new();
522518
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());
526526 }
527527
528528 #[test]
4040 log::trace!("exit: start_max_time_thread");
4141
4242 #[cfg(test)]
43 panic!(handles);
43 panic!("{:?}", handles);
4444 #[cfg(not(test))]
4545 let _ = TermInputHandler::sigint_handler(handles.clone());
4646 }
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};
11
22 use anyhow::{bail, Result};
33 use futures::{stream, StreamExt};
3939 order: ScanOrder,
4040
4141 /// wordlist that's already been read from disk
42 wordlist: Arc<HashSet<String>>,
42 wordlist: Arc<Vec<String>>,
4343
4444 /// limiter that restricts the number of active FeroxScanners
4545 scan_limiter: Arc<Semaphore>,
5151 pub fn new(
5252 target_url: &str,
5353 order: ScanOrder,
54 wordlist: Arc<HashSet<String>>,
54 wordlist: Arc<Vec<String>>,
5555 scan_limiter: Arc<Semaphore>,
5656 handles: Arc<Handles>,
5757 ) -> Self {
8282 .target(RobotsTxt)
8383 .build()?;
8484
85 let _ = extractor.extract().await;
85 let links = extractor.extract().await?;
86 extractor.request_links(links).await?;
8687 }
8788
8889 let scanned_urls = self.handles.ferox_scans()?;
8989 atomic_store!(self.remove_limit, true);
9090 }
9191 }
92 self.set_limit(heap.value() as usize);
9392 } else if heap.has_children() {
9493 // streak not at 3, just check that we can move down, and do so
9594 heap.move_left();
96 self.set_limit(heap.value() as usize);
9795 } else {
9896 // tree bottomed out, need to move back up the tree a bit
9997 let current = heap.value();
103101 if current > heap.value() {
104102 heap.move_up();
105103 }
106
107 self.set_limit(heap.value() as usize);
108104 }
105 self.set_limit(heap.value() as usize);
109106 }
110107 }
111108
199196 pd.adjust_up(&3);
200197 assert_eq!(pd.heap.read().unwrap().value(), 300);
201198 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));
203200 }
204201
205202 #[test]
216213 pd.adjust_up(&3);
217214 assert_eq!(pd.heap.read().unwrap().value(), 200);
218215 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));
220217 }
221218
222219 #[test]
233230 pd.adjust_up(&3);
234231 assert_eq!(pd.heap.read().unwrap().value(), 350);
235232 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));
237234 }
238235
239236 #[test]
250247 pd.adjust_up(&3);
251248 assert_eq!(pd.heap.read().unwrap().value(), 300);
252249 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));
254251 }
255252
256253 #[test]
268265 pd.adjust_up(&0);
269266 assert_eq!(pd.heap.read().unwrap().value(), 43);
270267 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));
272269 }
273270
274271 #[test]
286283 pd.adjust_up(&0);
287284 assert_eq!(pd.heap.read().unwrap().value(), 37);
288285 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));
290287 }
291288
292289 #[test]
2626 };
2727
2828 use super::{policy_data::PolicyData, FeroxScanner, PolicyTrigger};
29 use crate::utils::should_deny_url;
30 use std::collections::HashSet;
2931
3032 /// Makes multiple requests based on the presence of extensions
3133 pub(super) struct Requester {
4446 /// FeroxScan associated with the creation of this Requester
4547 ferox_scan: Arc<FeroxScan>,
4648
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
4754 /// simple lock to control access to tuning to a single thread (per-scan)
4855 ///
4956 /// need a usize to determine the number of consecutive non-error calls that a requester has
5057 /// 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)
5259 tuning_lock: Mutex<usize>,
5360 }
5461
7279 Ok(Self {
7380 ferox_scan,
7481 policy_data,
82 seen_links: RwLock::new(HashSet::<String>::new()),
7583 rate_limiter: RwLock::new(rate_limiter),
7684 handles: scanner.handles.clone(),
7785 target_url: scanner.target_url.to_owned(),
297305 let urls =
298306 FeroxUrl::from_string(&self.target_url, self.handles.clone()).formatted_urls(word)?;
299307
308 let should_test_deny = !self.handles.config.url_denylist.is_empty();
309
300310 for url in urls {
301311 // auto_tune is true, or rate_limit was set (mutually exclusive to user)
302312 // and a rate_limiter has been created
310320 log::warn!("Could not rate limit scan: {}", e);
311321 self.handles.stats.send(AddError(Other)).unwrap_or_default();
312322 }
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;
313328 }
314329
315330 let response = logged_request(&url, self.handles.clone()).await?;
366381 .handles(self.handles.clone())
367382 .build()?;
368383
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?;
370404 }
371405
372406 // everything else should be reported
535569
536570 let requester = Requester {
537571 handles,
572 seen_links: RwLock::new(HashSet::<String>::new()),
538573 tuning_lock: Mutex::new(0),
539574 ferox_scan: Arc::new(FeroxScan::default()),
540575 target_url: "http://localhost".to_string(),
562597
563598 let requester = Requester {
564599 handles,
600 seen_links: RwLock::new(HashSet::<String>::new()),
565601 tuning_lock: Mutex::new(0),
566602 ferox_scan: ferox_scan.clone(),
567603 target_url: "http://localhost".to_string(),
586622
587623 let requester = Requester {
588624 handles,
625 seen_links: RwLock::new(HashSet::<String>::new()),
589626 tuning_lock: Mutex::new(0),
590627 ferox_scan: ferox_scan.clone(),
591628 target_url: "http://localhost".to_string(),
625662
626663 let requester = Requester {
627664 handles,
665 seen_links: RwLock::new(HashSet::<String>::new()),
628666 tuning_lock: Mutex::new(0),
629667 ferox_scan: ferox_scan.clone(),
630668 target_url: "http://localhost".to_string(),
679717 let req_clone = scan_two.clone();
680718 let requester = Requester {
681719 handles,
720 seen_links: RwLock::new(HashSet::<String>::new()),
682721 tuning_lock: Mutex::new(0),
683722 ferox_scan: req_clone,
684723 target_url: "http://one/one/stuff.php".to_string(),
712751
713752 let requester = Requester {
714753 handles,
754 seen_links: RwLock::new(HashSet::<String>::new()),
715755 tuning_lock: Mutex::new(0),
716756 ferox_scan: Arc::new(FeroxScan::default()),
717757 target_url: "http://one/one/stuff.php".to_string(),
733773
734774 let requester = Requester {
735775 handles,
776 seen_links: RwLock::new(HashSet::<String>::new()),
736777 tuning_lock: Mutex::new(0),
737778 ferox_scan: Arc::new(FeroxScan::default()),
738779 target_url: "http://localhost".to_string(),
755796
756797 let requester = Arc::new(Requester {
757798 handles,
799 seen_links: RwLock::new(HashSet::<String>::new()),
758800 tuning_lock: Mutex::new(0),
759801 ferox_scan: Arc::new(FeroxScan::default()),
760802 target_url: "http://localhost".to_string(),
771813
772814 requester.cool_down().await;
773815
774 assert_eq!(resp.await.unwrap(), true);
816 assert!(resp.await.unwrap());
775817 println!("{}", start.elapsed().as_millis());
776818 assert!(start.elapsed().as_millis() >= 3500);
777819 }
784826
785827 let requester = Requester {
786828 handles,
829 seen_links: RwLock::new(HashSet::<String>::new()),
787830 tuning_lock: Mutex::new(0),
788831 ferox_scan: Arc::new(FeroxScan::default()),
789832 target_url: "http://localhost".to_string(),
821864
822865 let requester = Requester {
823866 handles,
867 seen_links: RwLock::new(HashSet::<String>::new()),
824868 tuning_lock: Mutex::new(0),
825869 ferox_scan: Arc::new(scan),
826870 target_url: "http://localhost".to_string(),
856900
857901 let requester = Requester {
858902 handles,
903 seen_links: RwLock::new(HashSet::<String>::new()),
859904 tuning_lock: Mutex::new(0),
860905 ferox_scan: Arc::new(scan),
861906 target_url: "http://localhost".to_string(),
883928
884929 let mut requester = Requester {
885930 handles,
931 seen_links: RwLock::new(HashSet::<String>::new()),
886932 tuning_lock: Mutex::new(0),
887933 ferox_scan: Arc::new(FeroxScan::default()),
888934 target_url: "http://localhost".to_string(),
890936 policy_data: PolicyData::new(RequesterPolicy::AutoBail, 7),
891937 };
892938
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));
902942 requester.ferox_scan.progress_bar().set_position(10);
903943 requester.ferox_scan.add_429();
904944 requester.ferox_scan.add_429();
905945 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));
915949 requester.ferox_scan = Arc::new(FeroxScan::default());
916950 requester.ferox_scan.progress_bar().set_position(10);
917951 requester.ferox_scan.add_403();
923957 requester.ferox_scan.add_403();
924958 requester.ferox_scan.add_403();
925959 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));
930961 }
931962
932963 #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
940971
941972 let requester = Requester {
942973 handles,
974 seen_links: RwLock::new(HashSet::<String>::new()),
943975 tuning_lock: Mutex::new(0),
944976 ferox_scan: Arc::new(FeroxScan::default()),
945977 target_url: "http://localhost".to_string(),
9821014
9831015 let requester = Requester {
9841016 handles,
1017 seen_links: RwLock::new(HashSet::<String>::new()),
9851018 tuning_lock: Mutex::new(0),
9861019 ferox_scan: scan.clone(),
9871020 target_url: "http://localhost".to_string(),
00 use std::{
1 collections::HashMap,
2 convert::TryFrom,
13 fs::File,
24 io::BufReader,
35 sync::{
810
911 use anyhow::{Context, Result};
1012 use reqwest::StatusCode;
11 use serde::{Deserialize, Serialize};
13 use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer};
14 use serde_json::Value;
1215
1316 use crate::{
1417 traits::FeroxSerialize,
1821 use super::{error::StatError, field::StatField};
1922
2023 /// Data collection of statistics related to a scan
21 #[derive(Default, Deserialize, Debug, Serialize)]
24 #[derive(Default, Debug)]
2225 pub struct Stats {
23 #[serde(rename = "type")]
2426 /// Name of this type of struct, used for serialization, i.e. `{"type":"statistics"}`
2527 kind: String,
2628
124126 total_runtime: Mutex<Vec<f64>>,
125127
126128 /// tracker for the number of extensions the user specified
127 #[serde(skip)]
128129 num_extensions: usize,
129130
130131 /// tracker for whether to use json during serialization or not
131 #[serde(skip)]
132132 json: bool,
133133 }
134134
143143 /// Simple call to produce a JSON string using the given Stats object
144144 fn as_json(&self) -> Result<String> {
145145 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)
146441 }
147442 }
148443
286286 Ok(())
287287 }
288288
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
289387 #[cfg(test)]
290388 mod tests {
291389 use super::*;
390 use crate::config::Configuration;
391 use crate::scan_manager::{FeroxScans, ScanOrder};
292392
293393 #[test]
294394 /// set_open_file_limit with a low requested limit succeeds
365465 fn status_colorizer_returns_as_is() {
366466 assert_eq!(status_colorizer("farfignewton"), "farfignewton".to_string());
367467 }
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 }
108108 .and(predicate::str::contains("Header"))
109109 .and(predicate::str::contains("stuff: things"))
110110 .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"))
111141 .and(predicate::str::contains("─┴─")),
112142 );
113143 }
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 }
223223
224224 teardown_tmp_directory(tmp_dir);
225225
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)"));
231231
232232 cmd.assert().success().stdout(
233233 predicate::str::contains("WLD")
390390
391391 teardown_tmp_directory(tmp_dir);
392392
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)"));
398398
399399 cmd.assert().success().stdout(
400400 predicate::str::contains("WLD")
450450
451451 teardown_tmp_directory(tmp_dir);
452452
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)"));
459459
460460 cmd.assert().success().stdout(
461461 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 }