mirror of
https://github.com/epi052/feroxbuster.git
synced 2026-05-25 14:51:12 -03:00
Compare commits
27 Commits
fix-wildca
...
self-updat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0f9a30ba9 | ||
|
|
867da048b4 | ||
|
|
31e66c1fa0 | ||
|
|
703da383a7 | ||
|
|
aa83e40c4f | ||
|
|
a77c436e04 | ||
|
|
c3455d123e | ||
|
|
2d381e7e05 | ||
|
|
7d26f368f5 | ||
|
|
36970896ca | ||
|
|
39a75f0608 | ||
|
|
ab8537beeb | ||
|
|
9e907d37d5 | ||
|
|
19e0a7f48b | ||
|
|
5e93da0a65 | ||
|
|
2704e33178 | ||
|
|
8392f6d26b | ||
|
|
ca43a767d2 | ||
|
|
291ccedba3 | ||
|
|
6d01bc8ec4 | ||
|
|
94aafccf8a | ||
|
|
8dd8871ae5 | ||
|
|
ad0df8ccd3 | ||
|
|
31cdba64e4 | ||
|
|
584fc940cd | ||
|
|
43116f9aab | ||
|
|
aec083ea58 |
@@ -542,6 +542,16 @@
|
||||
"contributions": [
|
||||
"ideas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "aancw",
|
||||
"name": "Aan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6284204?v=4",
|
||||
"profile": "https://petruknisme.com",
|
||||
"contributions": [
|
||||
"code",
|
||||
"infra"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -94,7 +94,7 @@ jobs:
|
||||
env:
|
||||
IN_PIPELINE: true
|
||||
runs-on: macos-latest
|
||||
if: github.ref == 'refs/heads/main'
|
||||
# if: github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
@@ -126,7 +126,7 @@ jobs:
|
||||
env:
|
||||
IN_PIPELINE: true
|
||||
runs-on: ${{ matrix.os }}
|
||||
if: github.ref == 'refs/heads/main'
|
||||
# if: github.ref == 'refs/heads/main'
|
||||
strategy:
|
||||
matrix:
|
||||
type: [windows-x64, windows-x86]
|
||||
|
||||
296
Cargo.lock
generated
296
Cargo.lock
generated
@@ -2,6 +2,12 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
@@ -123,12 +129,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "2.6.0"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685"
|
||||
checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
"futures-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -193,9 +198,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.64"
|
||||
version = "0.1.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
|
||||
checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -342,9 +347,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.1.6"
|
||||
version = "4.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3"
|
||||
checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
@@ -357,9 +362,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.1.3"
|
||||
version = "4.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0012995dc3a54314f4710f5631d74767e73c534b8757221708303e48eef7a19b"
|
||||
checksum = "501ff0a401473ea1d4c3b125ff95506b62c5bc5768d818634195fbb7c4ad5ff4"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
@@ -427,10 +432,19 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.6"
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
@@ -438,9 +452,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
|
||||
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
@@ -449,9 +463,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.13"
|
||||
version = "0.9.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
|
||||
checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
@@ -462,9 +476,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.14"
|
||||
version = "0.8.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
|
||||
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -502,15 +516,15 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "cssparser"
|
||||
version = "0.27.2"
|
||||
version = "0.29.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a"
|
||||
checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa"
|
||||
dependencies = [
|
||||
"cssparser-macros",
|
||||
"dtoa-short",
|
||||
"itoa 0.4.8",
|
||||
"itoa",
|
||||
"matches",
|
||||
"phf 0.8.0",
|
||||
"phf 0.10.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"smallvec",
|
||||
@@ -761,7 +775,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "feroxbuster"
|
||||
version = "2.8.0"
|
||||
version = "2.10.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
@@ -775,7 +789,7 @@ dependencies = [
|
||||
"futures",
|
||||
"gaoya",
|
||||
"httpmock",
|
||||
"indicatif",
|
||||
"indicatif 0.15.0",
|
||||
"lazy_static",
|
||||
"leaky-bucket",
|
||||
"log",
|
||||
@@ -785,6 +799,7 @@ dependencies = [
|
||||
"reqwest",
|
||||
"rlimit",
|
||||
"scraper",
|
||||
"self_update",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_regex",
|
||||
@@ -796,12 +811,34 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.9.0"
|
||||
@@ -1047,9 +1084,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.15"
|
||||
version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4"
|
||||
checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@@ -1116,7 +1153,7 @@ checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa 1.0.5",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1191,7 +1228,7 @@ dependencies = [
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa 1.0.5",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
@@ -1241,10 +1278,22 @@ checksum = "7baab56125e25686df467fe470785512329883aab42696d661247aca2a2896e4"
|
||||
dependencies = [
|
||||
"console",
|
||||
"lazy_static",
|
||||
"number_prefix",
|
||||
"number_prefix 0.3.0",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.17.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cef509aa9bc73864d6756f0d34d35504af3cf0844373afe9b8669a5b8005a729"
|
||||
dependencies = [
|
||||
"console",
|
||||
"number_prefix 0.4.0",
|
||||
"portable-atomic",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
@@ -1256,9 +1305,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3"
|
||||
checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.45.0",
|
||||
@@ -1320,15 +1369,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.8"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
@@ -1491,9 +1534,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.7.1"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
|
||||
checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
@@ -1504,6 +1547,15 @@ version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.6"
|
||||
@@ -1589,6 +1641,12 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b02fc0ff9a9e4b35b3342880f48e896ebf69f2967921fe8646bf5b7125956a"
|
||||
|
||||
[[package]]
|
||||
name = "number_prefix"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
@@ -1635,9 +1693,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "111.25.0+1.1.1t"
|
||||
version = "111.25.1+1.1.1t"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3173cd3626c43e3854b1b727422a276e568d9ec5fe8cec197822cf52cfb743d6"
|
||||
checksum = "1ef9a9cc6ea7d9d5e7c4a913dc4b48d0e359eddf01af1dfec96ba7064b4aba10"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
@@ -1713,9 +1771,7 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_shared 0.8.0",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1724,7 +1780,9 @@ version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_shared 0.10.0",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1769,12 +1827,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.8.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c"
|
||||
checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0"
|
||||
dependencies = [
|
||||
"phf_generator 0.8.0",
|
||||
"phf_shared 0.8.0",
|
||||
"phf_generator 0.10.0",
|
||||
"phf_shared 0.10.0",
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1857,6 +1915,12 @@ dependencies = [
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26f6a7b87c2e435a3241addceeeff740ff8b7e76b74c13bf9acb17fa454ea00b"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
@@ -1914,6 +1978,15 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11bafc859c6815fbaffbbbf4229ecb767ac913fecb27f9ad4343662e9ef099ea"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
@@ -2062,9 +2135,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.6.1"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
|
||||
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
@@ -2072,9 +2145,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.10.2"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b"
|
||||
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
@@ -2192,9 +2265,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.8"
|
||||
version = "0.36.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644"
|
||||
checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -2206,15 +2279,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.11"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
|
||||
checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.12"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
@@ -2233,9 +2306,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "scraper"
|
||||
version = "0.14.0"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7cb4dae083699a22a65aa9d2699c27f525e35dffaec38b10801e958ed4cf27"
|
||||
checksum = "4c557a9a03db98b0b298b497f0e16cd35a04a1fa9ee1130a6889c0714e0b73df"
|
||||
dependencies = [
|
||||
"cssparser",
|
||||
"ego-tree",
|
||||
@@ -2278,22 +2351,41 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "selectors"
|
||||
version = "0.22.0"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe"
|
||||
checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cssparser",
|
||||
"derive_more",
|
||||
"fxhash",
|
||||
"log",
|
||||
"matches",
|
||||
"phf 0.8.0",
|
||||
"phf_codegen 0.8.0",
|
||||
"precomputed-hash",
|
||||
"servo_arc",
|
||||
"smallvec",
|
||||
"thin-slice",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "self_update"
|
||||
version = "0.36.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca4e4e6f29fddb78b3e7a6e5a395e8274d4aca2d36b2278a297fa49673a5b7c7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"flate2",
|
||||
"hyper",
|
||||
"indicatif 0.17.3",
|
||||
"log",
|
||||
"quick-xml",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"semver",
|
||||
"serde_json",
|
||||
"tar",
|
||||
"tempfile",
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2324,11 +2416,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.93"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
|
||||
checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea"
|
||||
dependencies = [
|
||||
"itoa 1.0.5",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
@@ -2359,16 +2451,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"itoa 1.0.5",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "servo_arc"
|
||||
version = "0.1.1"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432"
|
||||
checksum = "d52aa42f8fdf0fed91e5ce7f23d8138441002fa31dca008acf47e6fd4721f741"
|
||||
dependencies = [
|
||||
"nodrop",
|
||||
"stable_deref_trait",
|
||||
@@ -2463,9 +2555,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.7"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
|
||||
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
@@ -2485,9 +2577,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.8.4"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08"
|
||||
checksum = "7d69e88b23f23030bf4d0e9ca7b07434f70e1c1f4d3ca7e93ce958b373654d9f"
|
||||
dependencies = [
|
||||
"new_debug_unreachable",
|
||||
"once_cell",
|
||||
@@ -2526,6 +2618,17 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"libc",
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.4.0"
|
||||
@@ -2586,26 +2689,20 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8"
|
||||
|
||||
[[package]]
|
||||
name = "thin-slice"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.38"
|
||||
version = "1.0.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||
checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.38"
|
||||
version = "1.0.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||
checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2638,9 +2735,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.25.0"
|
||||
version = "1.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
|
||||
checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
@@ -2653,7 +2750,7 @@ dependencies = [
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.42.0",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2816,9 +2913,9 @@ checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.6"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
@@ -2853,6 +2950,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9"
|
||||
|
||||
[[package]]
|
||||
name = "utf-8"
|
||||
version = "0.7.6"
|
||||
@@ -3126,9 +3229,9 @@ checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.3.3"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "faf09497b8f8b5ac5d3bb4d05c0a99be20f26fd3d5f2db7b0716e946d5103658"
|
||||
checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -3141,3 +3244,12 @@ checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
15
Cargo.toml
15
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "feroxbuster"
|
||||
version = "2.8.0"
|
||||
version = "2.10.0"
|
||||
authors = ["Ben 'epi' Risher (@epi052)"]
|
||||
license = "MIT"
|
||||
edition = "2021"
|
||||
@@ -22,16 +22,16 @@ build = "build.rs"
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "4.1.6", features = ["wrap_help", "cargo"] }
|
||||
clap_complete = "4.1.3"
|
||||
clap = { version = "4.1.8", features = ["wrap_help", "cargo"] }
|
||||
clap_complete = "4.1.4"
|
||||
regex = "1.5.5"
|
||||
lazy_static = "1.4.0"
|
||||
dirs = "4.0.0"
|
||||
|
||||
[dependencies]
|
||||
scraper = "0.14.0"
|
||||
scraper = "0.15.0"
|
||||
futures = "0.3.26"
|
||||
tokio = { version = "1.25.0", features = ["full"] }
|
||||
tokio = { version = "1.26.0", features = ["full"] }
|
||||
tokio-util = { version = "0.7.7", features = ["codec"] }
|
||||
log = "0.4.17"
|
||||
env_logger = "0.10.0"
|
||||
@@ -39,11 +39,11 @@ reqwest = { version = "0.11.10", features = ["socks"] }
|
||||
# uses feature unification to add 'serde' to reqwest::Url
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
serde_regex = "1.1.0"
|
||||
clap = { version = "4.1.6", features = ["wrap_help", "cargo"] }
|
||||
clap = { version = "4.1.8", features = ["wrap_help", "cargo"] }
|
||||
lazy_static = "1.4.0"
|
||||
toml = "0.7.2"
|
||||
serde = { version = "1.0.137", features = ["derive", "rc"] }
|
||||
serde_json = "1.0.93"
|
||||
serde_json = "1.0.94"
|
||||
uuid = { version = "1.3.0", features = ["v4"] }
|
||||
indicatif = "0.15"
|
||||
console = "0.15.2"
|
||||
@@ -56,6 +56,7 @@ ctrlc = "3.2.2"
|
||||
anyhow = "1.0.69"
|
||||
leaky-bucket = "0.12.1"
|
||||
gaoya = "0.1.2"
|
||||
self_update = {version = "0.36.0", features = ["archive-tar", "compression-flate2"]}
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.3.0"
|
||||
|
||||
@@ -23,3 +23,10 @@ clear = true
|
||||
script = """
|
||||
cargo clippy --all-targets --all-features -- -D warnings
|
||||
"""
|
||||
|
||||
# tests
|
||||
[tasks.test]
|
||||
clear = true
|
||||
script = """
|
||||
cargo nextest run --all-features --all-targets --retries 10
|
||||
"""
|
||||
|
||||
@@ -167,6 +167,12 @@ cat targets | ./feroxbuster --stdin --silent -s 200 301 302 --redirects -x js |
|
||||
./feroxbuster -u http://127.1 --query token=0123456789ABCDEF
|
||||
```
|
||||
|
||||
### Updating feroxbuster (new in v2.10.0)
|
||||
|
||||
```
|
||||
./feroxbuster --update
|
||||
```
|
||||
|
||||
## 🚀 Documentation has **moved** 🚀
|
||||
|
||||
For realsies, there used to be over 1300 lines in this README, but it's all been moved to the [new documentation site](https://epi052.github.io/feroxbuster-docs/docs/). Go check it out!
|
||||
@@ -257,6 +263,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xaeroborg"><img src="https://avatars.githubusercontent.com/u/33274680?v=4?s=100" width="100px;" alt="xaeroborg"/><br /><sub><b>xaeroborg</b></sub></a><br /><a href="#ideas-xaeroborg" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Luoooio"><img src="https://avatars.githubusercontent.com/u/26653157?v=4?s=100" width="100px;" alt="Luoooio"/><br /><sub><b>Luoooio</b></sub></a><br /><a href="#ideas-Luoooio" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://petruknisme.com"><img src="https://avatars.githubusercontent.com/u/6284204?v=4?s=100" width="100px;" alt="Aan"/><br /><sub><b>Aan</b></sub></a><br /><a href="https://github.com/epi052/feroxbuster/commits?author=aancw" title="Code">💻</a> <a href="#infra-aancw" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
# queries = [["name","value"], ["rick", "astley"]]
|
||||
# save_state = false
|
||||
# time_limit = "10m"
|
||||
# update_app = false
|
||||
|
||||
# headers can be specified on multiple lines or as an inline table
|
||||
#
|
||||
|
||||
BIN
img/logo/logo.png
Normal file
BIN
img/logo/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
@@ -24,8 +24,8 @@ _feroxbuster() {
|
||||
'--replay-proxy=[Send only unfiltered requests through a Replay Proxy, instead of all requests]:REPLAY_PROXY:_urls' \
|
||||
'*-R+[Status Codes to send through a Replay Proxy when found (default: --status-codes value)]:REPLAY_CODE: ' \
|
||||
'*--replay-codes=[Status Codes to send through a Replay Proxy when found (default: --status-codes value)]:REPLAY_CODE: ' \
|
||||
'-a+[Sets the User-Agent (default: feroxbuster/2.8.0)]:USER_AGENT: ' \
|
||||
'--user-agent=[Sets the User-Agent (default: feroxbuster/2.8.0)]:USER_AGENT: ' \
|
||||
'-a+[Sets the User-Agent (default: feroxbuster/2.10.0)]:USER_AGENT: ' \
|
||||
'--user-agent=[Sets the User-Agent (default: feroxbuster/2.10.0)]:USER_AGENT: ' \
|
||||
'*-x+[File extension(s) to search for (ex: -x php -x pdf js)]:FILE_EXTENSION: ' \
|
||||
'*--extensions=[File extension(s) to search for (ex: -x php -x pdf js)]:FILE_EXTENSION: ' \
|
||||
'*-m+[Which HTTP request method(s) should be sent (default: GET)]:HTTP_METHODS: ' \
|
||||
@@ -72,8 +72,8 @@ _feroxbuster() {
|
||||
'(-u --url)--stdin[Read url(s) from STDIN]' \
|
||||
'(-p --proxy -k --insecure --burp-replay)--burp[Set --proxy to http://127.0.0.1:8080 and set --insecure to true]' \
|
||||
'(-P --replay-proxy -k --insecure)--burp-replay[Set --replay-proxy to http://127.0.0.1:8080 and set --insecure to true]' \
|
||||
'--smart[Set --extract-links, --auto-tune, --collect-words, and --collect-backups to true]' \
|
||||
'--thorough[Use the same settings as --smart and set --collect-extensions to true]' \
|
||||
'(--rate-limit --auto-bail)--smart[Set --extract-links, --auto-tune, --collect-words, and --collect-backups to true]' \
|
||||
'(--rate-limit --auto-bail)--thorough[Use the same settings as --smart and set --collect-extensions to true]' \
|
||||
'-A[Use a random User-Agent]' \
|
||||
'--random-agent[Use a random User-Agent]' \
|
||||
'-f[Append / to each request'\''s URL]' \
|
||||
@@ -104,6 +104,8 @@ _feroxbuster() {
|
||||
'--quiet[Hide progress bars and banner (good for tmux windows w/ notifications)]' \
|
||||
'--json[Emit JSON logs to --output and --debug-log instead of normal text]' \
|
||||
'--no-state[Disable state output file (*.state)]' \
|
||||
'(-u --url -w --wordlist)-U[Update the app to the latest version]' \
|
||||
'(-u --url -w --wordlist)--update[Update the app to the latest version]' \
|
||||
'-h[Print help (see more with '\''--help'\'')]' \
|
||||
'--help[Print help (see more with '\''--help'\'')]' \
|
||||
'-V[Print version]' \
|
||||
@@ -117,4 +119,8 @@ _feroxbuster_commands() {
|
||||
_describe -t commands 'feroxbuster commands' commands "$@"
|
||||
}
|
||||
|
||||
_feroxbuster "$@"
|
||||
if [ "$funcstack[1]" = "_feroxbuster" ]; then
|
||||
_feroxbuster "$@"
|
||||
else
|
||||
compdef _feroxbuster feroxbuster
|
||||
fi
|
||||
|
||||
@@ -30,8 +30,8 @@ Register-ArgumentCompleter -Native -CommandName 'feroxbuster' -ScriptBlock {
|
||||
[CompletionResult]::new('--replay-proxy', 'replay-proxy', [CompletionResultType]::ParameterName, 'Send only unfiltered requests through a Replay Proxy, instead of all requests')
|
||||
[CompletionResult]::new('-R', 'R', [CompletionResultType]::ParameterName, 'Status Codes to send through a Replay Proxy when found (default: --status-codes value)')
|
||||
[CompletionResult]::new('--replay-codes', 'replay-codes', [CompletionResultType]::ParameterName, 'Status Codes to send through a Replay Proxy when found (default: --status-codes value)')
|
||||
[CompletionResult]::new('-a', 'a', [CompletionResultType]::ParameterName, 'Sets the User-Agent (default: feroxbuster/2.8.0)')
|
||||
[CompletionResult]::new('--user-agent', 'user-agent', [CompletionResultType]::ParameterName, 'Sets the User-Agent (default: feroxbuster/2.8.0)')
|
||||
[CompletionResult]::new('-a', 'a', [CompletionResultType]::ParameterName, 'Sets the User-Agent (default: feroxbuster/2.10.0)')
|
||||
[CompletionResult]::new('--user-agent', 'user-agent', [CompletionResultType]::ParameterName, 'Sets the User-Agent (default: feroxbuster/2.10.0)')
|
||||
[CompletionResult]::new('-x', 'x', [CompletionResultType]::ParameterName, 'File extension(s) to search for (ex: -x php -x pdf js)')
|
||||
[CompletionResult]::new('--extensions', 'extensions', [CompletionResultType]::ParameterName, 'File extension(s) to search for (ex: -x php -x pdf js)')
|
||||
[CompletionResult]::new('-m', 'm', [CompletionResultType]::ParameterName, 'Which HTTP request method(s) should be sent (default: GET)')
|
||||
@@ -110,6 +110,8 @@ Register-ArgumentCompleter -Native -CommandName 'feroxbuster' -ScriptBlock {
|
||||
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Hide progress bars and banner (good for tmux windows w/ notifications)')
|
||||
[CompletionResult]::new('--json', 'json', [CompletionResultType]::ParameterName, 'Emit JSON logs to --output and --debug-log instead of normal text')
|
||||
[CompletionResult]::new('--no-state', 'no-state', [CompletionResultType]::ParameterName, 'Disable state output file (*.state)')
|
||||
[CompletionResult]::new('-U', 'U', [CompletionResultType]::ParameterName, 'Update the app to the latest version')
|
||||
[CompletionResult]::new('--update', 'update', [CompletionResultType]::ParameterName, 'Update the app to the latest version')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version')
|
||||
|
||||
@@ -19,7 +19,7 @@ _feroxbuster() {
|
||||
|
||||
case "${cmd}" in
|
||||
feroxbuster)
|
||||
opts="-u -p -P -R -a -A -x -m -H -b -Q -f -S -X -W -N -C -s -T -r -k -t -n -d -e -L -w -D -E -B -g -I -v -q -o -h -V --url --stdin --resume-from --burp --burp-replay --smart --thorough --proxy --replay-proxy --replay-codes --user-agent --random-agent --extensions --methods --data --headers --cookies --query --add-slash --dont-scan --filter-size --filter-regex --filter-words --filter-lines --filter-status --filter-similar-to --status-codes --timeout --redirects --insecure --threads --no-recursion --depth --force-recursion --extract-links --scan-limit --parallel --rate-limit --time-limit --wordlist --auto-tune --auto-bail --dont-filter --collect-extensions --collect-backups --collect-words --dont-collect --verbosity --silent --quiet --json --output --debug-log --no-state --help --version"
|
||||
opts="-u -p -P -R -a -A -x -m -H -b -Q -f -S -X -W -N -C -s -T -r -k -t -n -d -e -L -w -D -E -B -g -I -v -q -o -U -h -V --url --stdin --resume-from --burp --burp-replay --smart --thorough --proxy --replay-proxy --replay-codes --user-agent --random-agent --extensions --methods --data --headers --cookies --query --add-slash --dont-scan --filter-size --filter-regex --filter-words --filter-lines --filter-status --filter-similar-to --status-codes --timeout --redirects --insecure --threads --no-recursion --depth --force-recursion --extract-links --scan-limit --parallel --rate-limit --time-limit --wordlist --auto-tune --auto-bail --dont-filter --collect-extensions --collect-backups --collect-words --dont-collect --verbosity --silent --quiet --json --output --debug-log --no-state --update --help --version"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
|
||||
@@ -27,8 +27,8 @@ set edit:completion:arg-completer[feroxbuster] = {|@words|
|
||||
cand --replay-proxy 'Send only unfiltered requests through a Replay Proxy, instead of all requests'
|
||||
cand -R 'Status Codes to send through a Replay Proxy when found (default: --status-codes value)'
|
||||
cand --replay-codes 'Status Codes to send through a Replay Proxy when found (default: --status-codes value)'
|
||||
cand -a 'Sets the User-Agent (default: feroxbuster/2.8.0)'
|
||||
cand --user-agent 'Sets the User-Agent (default: feroxbuster/2.8.0)'
|
||||
cand -a 'Sets the User-Agent (default: feroxbuster/2.10.0)'
|
||||
cand --user-agent 'Sets the User-Agent (default: feroxbuster/2.10.0)'
|
||||
cand -x 'File extension(s) to search for (ex: -x php -x pdf js)'
|
||||
cand --extensions 'File extension(s) to search for (ex: -x php -x pdf js)'
|
||||
cand -m 'Which HTTP request method(s) should be sent (default: GET)'
|
||||
@@ -107,6 +107,8 @@ set edit:completion:arg-completer[feroxbuster] = {|@words|
|
||||
cand --quiet 'Hide progress bars and banner (good for tmux windows w/ notifications)'
|
||||
cand --json 'Emit JSON logs to --output and --debug-log instead of normal text'
|
||||
cand --no-state 'Disable state output file (*.state)'
|
||||
cand -U 'Update the app to the latest version'
|
||||
cand --update 'Update the app to the latest version'
|
||||
cand -h 'Print help (see more with ''--help'')'
|
||||
cand --help 'Print help (see more with ''--help'')'
|
||||
cand -V 'Print version'
|
||||
|
||||
@@ -166,6 +166,9 @@ pub struct Banner {
|
||||
|
||||
/// represents Configuration.collect_words
|
||||
force_recursion: BannerEntry,
|
||||
|
||||
/// represents Configuration.update_app
|
||||
update_app: BannerEntry,
|
||||
}
|
||||
|
||||
/// implementation of Banner
|
||||
@@ -333,6 +336,7 @@ impl Banner {
|
||||
let json = BannerEntry::new("🧔", "JSON Output", &config.json.to_string());
|
||||
let output = BannerEntry::new("💾", "Output File", &config.output);
|
||||
let debug_log = BannerEntry::new("🪲", "Debugging Log", &config.debug_log);
|
||||
let update_app = BannerEntry::new("🔥", "Update app", &config.update_app.to_string());
|
||||
let extensions = BannerEntry::new(
|
||||
"💲",
|
||||
"Extensions",
|
||||
@@ -437,6 +441,7 @@ impl Banner {
|
||||
config: cfg,
|
||||
version: VERSION.to_string(),
|
||||
update_status: UpdateStatus::Unknown,
|
||||
update_app,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,6 +671,10 @@ by Ben "epi" Risher {} ver: {}"#,
|
||||
writeln!(&mut writer, "{}", self.force_recursion)?;
|
||||
}
|
||||
|
||||
if config.update_app {
|
||||
writeln!(&mut writer, "{}", self.update_app)?;
|
||||
}
|
||||
|
||||
if config.scan_limit > 0 {
|
||||
writeln!(&mut writer, "{}", self.scan_limit)?;
|
||||
}
|
||||
|
||||
@@ -309,6 +309,10 @@ pub struct Configuration {
|
||||
/// override recursion logic to always attempt recursion, still respects --depth
|
||||
#[serde(default)]
|
||||
pub force_recursion: bool,
|
||||
|
||||
/// Auto update app feature
|
||||
#[serde(default)]
|
||||
pub update_app: bool,
|
||||
}
|
||||
|
||||
impl Default for Configuration {
|
||||
@@ -358,6 +362,7 @@ impl Default for Configuration {
|
||||
collect_words: false,
|
||||
save_state: true,
|
||||
force_recursion: false,
|
||||
update_app: false,
|
||||
proxy: String::new(),
|
||||
config: String::new(),
|
||||
output: String::new(),
|
||||
@@ -441,6 +446,7 @@ impl Configuration {
|
||||
/// - **time_limit**: `None` (no limit on length of scan imposed)
|
||||
/// - **replay_proxy**: `None` (no limit on concurrent scans imposed)
|
||||
/// - **replay_codes**: [`DEFAULT_RESPONSE_CODES`](constant.DEFAULT_RESPONSE_CODES.html)
|
||||
/// - **update_app**: `false`
|
||||
///
|
||||
/// After which, any values defined in a
|
||||
/// [ferox-config.toml](constant.DEFAULT_CONFIG_NAME.html) config file will override the
|
||||
@@ -816,6 +822,10 @@ impl Configuration {
|
||||
config.force_recursion = true;
|
||||
}
|
||||
|
||||
if came_from_cli!(args, "update_app") {
|
||||
config.update_app = true;
|
||||
}
|
||||
|
||||
////
|
||||
// organizational breakpoint; all options below alter the Client configuration
|
||||
////
|
||||
@@ -992,6 +1002,7 @@ impl Configuration {
|
||||
update_if_not_default!(&mut conf.methods, new.methods, methods());
|
||||
update_if_not_default!(&mut conf.data, new.data, Vec::<u8>::new());
|
||||
update_if_not_default!(&mut conf.url_denylist, new.url_denylist, Vec::<Url>::new());
|
||||
update_if_not_default!(&mut conf.update_app, new.update_app, false);
|
||||
if !new.regex_denylist.is_empty() {
|
||||
// cant use the update_if_not_default macro due to the following error
|
||||
//
|
||||
|
||||
@@ -56,6 +56,7 @@ fn setup_config_test() -> Configuration {
|
||||
filter_word_count = [994, 992]
|
||||
filter_line_count = [34]
|
||||
filter_status = [201]
|
||||
update_app = false
|
||||
"#;
|
||||
let tmp_dir = TempDir::new().unwrap();
|
||||
let file = tmp_dir.path().join(DEFAULT_CONFIG_NAME);
|
||||
@@ -103,6 +104,7 @@ fn default_configuration() {
|
||||
assert!(!config.collect_extensions);
|
||||
assert!(!config.collect_backups);
|
||||
assert!(!config.collect_words);
|
||||
assert!(!config.update_app);
|
||||
assert!(config.regex_denylist.is_empty());
|
||||
assert_eq!(config.queries, Vec::new());
|
||||
assert_eq!(config.filter_size, Vec::<u64>::new());
|
||||
@@ -470,6 +472,13 @@ fn config_default_not_random_agent() {
|
||||
assert!(!config.random_agent);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// parse the test config and see that the value parsed is correct
|
||||
fn config_update_app() {
|
||||
let config = setup_config_test();
|
||||
assert!(!config.update_app);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
/// test that an error message is printed and panic is called when report_and_exit is called
|
||||
|
||||
@@ -248,7 +248,7 @@ impl TermOutHandler {
|
||||
.unwrap()
|
||||
.filters
|
||||
.data
|
||||
.should_filter_response(&resp);
|
||||
.should_filter_response(&resp, tx_stats.clone());
|
||||
|
||||
let contains_sentry = if !self.config.filter_status.is_empty() {
|
||||
// -C was used, meaning -s was not and we should ignore the defaults
|
||||
|
||||
@@ -6,7 +6,7 @@ use tokio::sync::{mpsc, Semaphore};
|
||||
use crate::{
|
||||
response::FeroxResponse,
|
||||
scan_manager::{FeroxScan, FeroxScans, ScanOrder},
|
||||
scanner::FeroxScanner,
|
||||
scanner::{FeroxScanner, RESPONSES},
|
||||
statistics::StatField::TotalScans,
|
||||
url::FeroxUrl,
|
||||
utils::should_deny_url,
|
||||
@@ -395,6 +395,58 @@ impl ScanHandler {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Ok(responses) = RESPONSES.responses.read() {
|
||||
for maybe_wild in responses.iter() {
|
||||
if !maybe_wild.wildcard() || !maybe_wild.is_directory() {
|
||||
// if the stored response isn't a wildcard, skip it
|
||||
// if the stored response isn't a directory, skip it
|
||||
// we're only interested in preventing recursion into wildcard directories
|
||||
continue;
|
||||
}
|
||||
|
||||
if maybe_wild.method() != response.method() {
|
||||
// methods don't match, skip it
|
||||
continue;
|
||||
}
|
||||
|
||||
// methods match and is a directory wildcard
|
||||
// need to check the wildcard's parent directory
|
||||
// for equality with the incoming response's parent directory
|
||||
//
|
||||
// if the parent directories match, we need to prevent recursion
|
||||
// into the wildcard directory
|
||||
|
||||
match (
|
||||
maybe_wild.url().path_segments(),
|
||||
response.url().path_segments(),
|
||||
) {
|
||||
// both urls must have path segments
|
||||
(Some(mut maybe_wild_segments), Some(mut response_segments)) => {
|
||||
match (
|
||||
maybe_wild_segments.nth_back(1),
|
||||
response_segments.nth_back(1),
|
||||
) {
|
||||
// both urls must have at least 2 path segments, the next to last being the parent
|
||||
(Some(maybe_wild_parent), Some(response_parent)) => {
|
||||
if maybe_wild_parent == response_parent {
|
||||
// the parent directories match, so we need to prevent recursion
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// we couldn't get the parent directory, so we'll skip this
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// we couldn't get the path segments, so we'll skip this
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let targets = vec![response.url().to_string()];
|
||||
self.ordered_scan_url(targets, ScanOrder::Latest).await?;
|
||||
|
||||
|
||||
@@ -144,7 +144,12 @@ impl<'a> Extractor<'a> {
|
||||
};
|
||||
|
||||
// filter if necessary
|
||||
if self.handles.filters.data.should_filter_response(&resp) {
|
||||
if self
|
||||
.handles
|
||||
.filters
|
||||
.data
|
||||
.should_filter_response(&resp, self.handles.stats.tx.clone())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,12 @@ use crate::response::FeroxResponse;
|
||||
|
||||
use super::{
|
||||
FeroxFilter, LinesFilter, RegexFilter, SimilarityFilter, SizeFilter, StatusCodeFilter,
|
||||
WordsFilter,
|
||||
WildcardFilter, WordsFilter,
|
||||
};
|
||||
use crate::{
|
||||
event_handlers::Command::AddToUsizeField, statistics::StatField::WildcardsFiltered,
|
||||
CommandSender,
|
||||
};
|
||||
|
||||
/// Container around a collection of `FeroxFilters`s
|
||||
#[derive(Debug, Default)]
|
||||
pub struct FeroxFilters {
|
||||
@@ -64,12 +67,21 @@ impl FeroxFilters {
|
||||
|
||||
/// Simple helper to stay DRY; determines whether or not a given `FeroxResponse` should be reported
|
||||
/// to the user or not.
|
||||
pub fn should_filter_response(&self, response: &FeroxResponse) -> bool {
|
||||
pub fn should_filter_response(
|
||||
&self,
|
||||
response: &FeroxResponse,
|
||||
tx_stats: CommandSender,
|
||||
) -> bool {
|
||||
if let Ok(filters) = self.filters.read() {
|
||||
for filter in filters.iter() {
|
||||
// wildcard.should_filter goes here
|
||||
if filter.should_filter_response(response) {
|
||||
log::debug!("filtering response due to: {:?}", filter);
|
||||
if filter.as_any().downcast_ref::<WildcardFilter>().is_some() {
|
||||
tx_stats
|
||||
.send(AddToUsizeField(WildcardsFiltered, 1))
|
||||
.unwrap_or_default();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -93,6 +105,10 @@ impl Serialize for FeroxFilters {
|
||||
seq.serialize_element(word_filter).unwrap_or_default();
|
||||
} else if let Some(size_filter) = filter.as_any().downcast_ref::<SizeFilter>() {
|
||||
seq.serialize_element(size_filter).unwrap_or_default();
|
||||
} else if let Some(wildcard_filter) =
|
||||
filter.as_any().downcast_ref::<WildcardFilter>()
|
||||
{
|
||||
seq.serialize_element(wildcard_filter).unwrap_or_default();
|
||||
} else if let Some(status_filter) =
|
||||
filter.as_any().downcast_ref::<StatusCodeFilter>()
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@ pub use self::similarity::{SimilarityFilter, SIM_HASHER};
|
||||
pub use self::size::SizeFilter;
|
||||
pub use self::status_code::StatusCodeFilter;
|
||||
pub(crate) use self::utils::{create_similarity_filter, filter_lookup};
|
||||
pub use self::wildcard::WildcardFilter;
|
||||
pub use self::words::WordsFilter;
|
||||
|
||||
mod status_code;
|
||||
@@ -28,4 +29,5 @@ mod container;
|
||||
mod tests;
|
||||
mod init;
|
||||
mod utils;
|
||||
mod wildcard;
|
||||
mod empty;
|
||||
|
||||
@@ -37,7 +37,9 @@ impl FeroxFilter for SimilarityFilter {
|
||||
|
||||
/// Compare one SimilarityFilter to another
|
||||
fn box_eq(&self, other: &dyn Any) -> bool {
|
||||
other.downcast_ref::<Self>().map_or(false, |a| self == a)
|
||||
other
|
||||
.downcast_ref::<Self>()
|
||||
.map_or(false, |a| self.hash == a.hash)
|
||||
}
|
||||
|
||||
/// Return self as Any for dynamic dispatch purposes
|
||||
|
||||
@@ -1,7 +1,41 @@
|
||||
use super::*;
|
||||
use crate::nlp::preprocess;
|
||||
use crate::DEFAULT_METHOD;
|
||||
use ::regex::Regex;
|
||||
|
||||
#[test]
|
||||
/// simply test the default values for wildcardfilter
|
||||
fn wildcard_filter_default() {
|
||||
let wcf = WildcardFilter::default();
|
||||
assert_eq!(wcf.content_length, None);
|
||||
assert_eq!(wcf.line_count, None);
|
||||
assert_eq!(wcf.word_count, None);
|
||||
assert_eq!(wcf.method, DEFAULT_METHOD.to_string());
|
||||
assert_eq!(wcf.status_code, 0);
|
||||
assert!(!wcf.dont_filter);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// just a simple test to increase code coverage by hitting as_any and the inner value
|
||||
fn wildcard_filter_as_any() {
|
||||
let mut filter = WildcardFilter::default();
|
||||
let filter2 = WildcardFilter::default();
|
||||
|
||||
assert!(filter.box_eq(filter2.as_any()));
|
||||
|
||||
assert_eq!(
|
||||
*filter.as_any().downcast_ref::<WildcardFilter>().unwrap(),
|
||||
filter2
|
||||
);
|
||||
|
||||
filter.content_length = Some(1);
|
||||
|
||||
assert_ne!(
|
||||
*filter.as_any().downcast_ref::<WildcardFilter>().unwrap(),
|
||||
filter2
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// just a simple test to increase code coverage by hitting as_any and the inner value
|
||||
fn lines_filter_as_any() {
|
||||
@@ -86,6 +120,68 @@ fn regex_filter_as_any() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// test should_filter on WilcardFilter where static logic matches
|
||||
fn wildcard_should_filter_when_static_wildcard_found() {
|
||||
let body =
|
||||
"pellentesque diam volutpat commodo sed egestas egestas fringilla phasellus faucibus";
|
||||
|
||||
let mut resp = FeroxResponse::default();
|
||||
resp.set_wildcard(true);
|
||||
resp.set_url("http://localhost");
|
||||
resp.set_text(body);
|
||||
|
||||
let filter = WildcardFilter {
|
||||
content_length: Some(body.len() as u64),
|
||||
line_count: Some(1),
|
||||
word_count: Some(10),
|
||||
method: DEFAULT_METHOD.to_string(),
|
||||
status_code: 200,
|
||||
dont_filter: false,
|
||||
};
|
||||
|
||||
assert!(filter.should_filter_response(&resp));
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// test should_filter on WilcardFilter where static logic matches but response length is 0
|
||||
fn wildcard_should_filter_when_static_wildcard_len_is_zero() {
|
||||
let mut resp = FeroxResponse::default();
|
||||
resp.set_wildcard(true);
|
||||
resp.set_url("http://localhost");
|
||||
|
||||
// default WildcardFilter is used in the code that executes when response.content_length() == 0
|
||||
let filter = WildcardFilter {
|
||||
content_length: Some(0),
|
||||
line_count: Some(0),
|
||||
word_count: Some(0),
|
||||
method: DEFAULT_METHOD.to_string(),
|
||||
status_code: 200,
|
||||
dont_filter: false,
|
||||
};
|
||||
|
||||
assert!(filter.should_filter_response(&resp));
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// test should_filter on WilcardFilter where dynamic logic matches
|
||||
fn wildcard_should_filter_when_dynamic_wildcard_found() {
|
||||
let mut resp = FeroxResponse::default();
|
||||
resp.set_wildcard(true);
|
||||
resp.set_url("http://localhost/stuff");
|
||||
resp.set_text("pellentesque diam volutpat commodo sed egestas egestas fringilla");
|
||||
|
||||
let filter = WildcardFilter {
|
||||
content_length: None,
|
||||
line_count: None,
|
||||
word_count: Some(8),
|
||||
method: DEFAULT_METHOD.to_string(),
|
||||
status_code: 200,
|
||||
dont_filter: false,
|
||||
};
|
||||
|
||||
assert!(filter.should_filter_response(&resp));
|
||||
}
|
||||
#[test]
|
||||
/// test should_filter on RegexFilter where regex matches body
|
||||
fn regexfilter_should_filter_when_regex_matches_on_response_body() {
|
||||
|
||||
180
src/filters/wildcard.rs
Normal file
180
src/filters/wildcard.rs
Normal file
@@ -0,0 +1,180 @@
|
||||
use console::style;
|
||||
|
||||
use super::*;
|
||||
use crate::utils::create_report_string;
|
||||
use crate::{config::OutputLevel, DEFAULT_METHOD};
|
||||
|
||||
/// Data holder for all relevant data needed when auto-filtering out wildcard responses
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct WildcardFilter {
|
||||
/// The content-length of this response, if known
|
||||
pub content_length: Option<u64>,
|
||||
|
||||
/// The number of lines contained in the body of this response, if known
|
||||
pub line_count: Option<usize>,
|
||||
|
||||
/// The number of words contained in the body of this response, if known
|
||||
pub word_count: Option<usize>,
|
||||
|
||||
/// method used in request that should be included with filters passed via runtime configuration
|
||||
pub method: String,
|
||||
|
||||
/// the status code returned in the response
|
||||
pub status_code: u16,
|
||||
|
||||
/// whether or not the user passed -D on the command line
|
||||
pub dont_filter: bool,
|
||||
}
|
||||
|
||||
/// implementation of WildcardFilter
|
||||
impl WildcardFilter {
|
||||
/// given a boolean representing whether -D was used or not, create a new WildcardFilter
|
||||
pub fn new(dont_filter: bool) -> Self {
|
||||
Self {
|
||||
dont_filter,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// implement default that populates `method` with its default value
|
||||
impl Default for WildcardFilter {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
content_length: None,
|
||||
line_count: None,
|
||||
word_count: None,
|
||||
method: DEFAULT_METHOD.to_string(),
|
||||
status_code: 0,
|
||||
dont_filter: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// implementation of FeroxFilter for WildcardFilter
|
||||
impl FeroxFilter for WildcardFilter {
|
||||
/// Examine size/words/lines and method to determine whether or not the response received
|
||||
/// is a wildcard response and therefore should be filtered out
|
||||
fn should_filter_response(&self, response: &FeroxResponse) -> bool {
|
||||
log::trace!("enter: should_filter_response({:?} {})", self, response);
|
||||
|
||||
// quick return if dont_filter is set
|
||||
if self.dont_filter {
|
||||
// --dont-filter applies specifically to wildcard filters, it is not a 100% catch all
|
||||
// for not filtering anything. As such, it should live in the implementation of
|
||||
// a wildcard filter
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.method != response.method().as_str() {
|
||||
// method's don't match, so this response should not be filtered out
|
||||
log::trace!("exit: should_filter_response -> false");
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.status_code != response.status().as_u16() {
|
||||
// status codes don't match, so this response should not be filtered out
|
||||
log::trace!("exit: should_filter_response -> false");
|
||||
return false;
|
||||
}
|
||||
|
||||
// methods and status codes match at this point, just need to check the other fields
|
||||
|
||||
match (self.content_length, self.word_count, self.line_count) {
|
||||
(Some(cl), Some(wc), Some(lc)) => {
|
||||
if cl == response.content_length()
|
||||
&& wc == response.word_count()
|
||||
&& lc == response.line_count()
|
||||
{
|
||||
log::debug!("filtered out {}", response.url());
|
||||
log::trace!("exit: should_filter_response -> true");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
(Some(cl), Some(wc), None) => {
|
||||
if cl == response.content_length() && wc == response.word_count() {
|
||||
log::debug!("filtered out {}", response.url());
|
||||
log::trace!("exit: should_filter_response -> true");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
(Some(cl), None, Some(lc)) => {
|
||||
if cl == response.content_length() && lc == response.line_count() {
|
||||
log::debug!("filtered out {}", response.url());
|
||||
log::trace!("exit: should_filter_response -> true");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
(None, Some(wc), Some(lc)) => {
|
||||
if wc == response.word_count() && lc == response.line_count() {
|
||||
log::debug!("filtered out {}", response.url());
|
||||
log::trace!("exit: should_filter_response -> true");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
(Some(cl), None, None) => {
|
||||
if cl == response.content_length() {
|
||||
log::debug!("filtered out {}", response.url());
|
||||
log::trace!("exit: should_filter_response -> true");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
(None, Some(wc), None) => {
|
||||
if wc == response.word_count() {
|
||||
log::debug!("filtered out {}", response.url());
|
||||
log::trace!("exit: should_filter_response -> true");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
(None, None, Some(lc)) => {
|
||||
if lc == response.line_count() {
|
||||
log::debug!("filtered out {}", response.url());
|
||||
log::trace!("exit: should_filter_response -> true");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
(None, None, None) => {
|
||||
unreachable!("wildcard filter without any filters set");
|
||||
}
|
||||
}
|
||||
|
||||
log::trace!("exit: should_filter_response -> false");
|
||||
false
|
||||
}
|
||||
|
||||
/// Compare one WildcardFilter to another
|
||||
fn box_eq(&self, other: &dyn Any) -> bool {
|
||||
other.downcast_ref::<Self>().map_or(false, |a| self == a)
|
||||
}
|
||||
|
||||
/// Return self as Any for dynamic dispatch purposes
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for WildcardFilter {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let msg = create_report_string(
|
||||
self.status_code.to_string().as_str(),
|
||||
self.method.as_str(),
|
||||
&self
|
||||
.line_count
|
||||
.map_or_else(|| "-".to_string(), |x| x.to_string()),
|
||||
&self
|
||||
.word_count
|
||||
.map_or_else(|| "-".to_string(), |x| x.to_string()),
|
||||
&self
|
||||
.content_length
|
||||
.map_or_else(|| "-".to_string(), |x| x.to_string()),
|
||||
&format!(
|
||||
"{} found {}-like response and created new filter; toggle off with {}",
|
||||
style("Auto-filtering").bright().green(),
|
||||
style("404").red(),
|
||||
style("--dont-filter").yellow()
|
||||
),
|
||||
OutputLevel::Default,
|
||||
);
|
||||
write!(f, "{}", msg)
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,21 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use console::style;
|
||||
use scraper::{Html, Selector};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::filters::{SimilarityFilter, SIM_HASHER};
|
||||
use crate::filters::{SimilarityFilter, WildcardFilter, SIM_HASHER};
|
||||
use crate::message::FeroxMessage;
|
||||
use crate::nlp::preprocess;
|
||||
use crate::scanner::RESPONSES;
|
||||
use crate::{
|
||||
config::OutputLevel,
|
||||
event_handlers::{Command, Handles},
|
||||
filters::{LinesFilter, SizeFilter, WordsFilter},
|
||||
progress::PROGRESS_PRINTER,
|
||||
response::FeroxResponse,
|
||||
skip_fail,
|
||||
url::FeroxUrl,
|
||||
utils::{ferox_print, fmt_err, logged_request, status_colorizer},
|
||||
utils::{ferox_print, fmt_err, logged_request},
|
||||
DEFAULT_METHOD,
|
||||
};
|
||||
|
||||
@@ -50,6 +49,16 @@ pub struct DirListingResult {
|
||||
pub response: FeroxResponse,
|
||||
}
|
||||
|
||||
/// wrapper around the results of running a wildcard detection against a target web page
|
||||
#[derive(Copy, Debug, Clone)]
|
||||
pub enum WildcardResult {
|
||||
/// variant that represents a wildcard directory
|
||||
WildcardDirectory(usize),
|
||||
|
||||
/// variant that represents the presence of a 404-like response
|
||||
FourOhFourLike(usize),
|
||||
}
|
||||
|
||||
/// container for heuristics related info
|
||||
pub struct HeuristicTests {
|
||||
/// Handles object for event handler interaction
|
||||
@@ -240,13 +249,16 @@ impl HeuristicTests {
|
||||
/// given a target's base url, attempt to automatically detect its 404 response
|
||||
/// pattern(s), and then set filters that will exclude those patterns from future
|
||||
/// responses
|
||||
pub async fn detect_404_like_responses(&self, target_url: &str) -> Result<u64> {
|
||||
pub async fn detect_404_like_responses(
|
||||
&self,
|
||||
target_url: &str,
|
||||
) -> Result<Option<WildcardResult>> {
|
||||
log::trace!("enter: detect_404_like_responses({:?})", target_url);
|
||||
|
||||
if self.handles.config.dont_filter {
|
||||
// early return, dont_filter scans don't need tested
|
||||
log::trace!("exit: detect_404_like_responses -> dont_filter is true");
|
||||
return Ok(0);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut req_counter = 0;
|
||||
@@ -323,28 +335,41 @@ impl HeuristicTests {
|
||||
}
|
||||
|
||||
// Command::AddFilter, &str (bytes/words/lines), usize (i.e. length associated with the type)
|
||||
let Some((command, filter_type, filter_length)) = self.examine_404_like_responses(&responses) else {
|
||||
let Some(filter) = self.examine_404_like_responses(&responses) else {
|
||||
// no match was found during analysis of responses
|
||||
responses.clear();
|
||||
continue;
|
||||
};
|
||||
|
||||
// check whether we already know about this filter
|
||||
match command {
|
||||
Command::AddFilter(ref filter) => {
|
||||
if let Ok(guard) = self.handles.filters.data.filters.read() {
|
||||
if guard.contains(filter) {
|
||||
// match was found, but already known; clear the vec and continue to the next
|
||||
responses.clear();
|
||||
continue;
|
||||
// report to the user, if appropriate
|
||||
if matches!(
|
||||
self.handles.config.output_level,
|
||||
OutputLevel::Default | OutputLevel::Quiet
|
||||
) {
|
||||
// sentry value to control whether or not to print the filter
|
||||
// used because we only want to print the same filter once
|
||||
let mut print_sentry = true;
|
||||
|
||||
if let Ok(filters) = self.handles.filters.data.filters.read() {
|
||||
for other in filters.iter() {
|
||||
if let Some(other_wildcard) =
|
||||
other.as_any().downcast_ref::<WildcardFilter>()
|
||||
{
|
||||
if &*filter == other_wildcard {
|
||||
print_sentry = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
||||
if print_sentry {
|
||||
ferox_print(&format!("{}", filter), &PROGRESS_PRINTER);
|
||||
}
|
||||
}
|
||||
|
||||
// create the new filter
|
||||
self.handles.filters.send(command)?;
|
||||
self.handles.filters.send(Command::AddFilter(filter))?;
|
||||
|
||||
// if we're here, we've detected a 404-like response pattern, and we're already filtering for size/word/line
|
||||
//
|
||||
@@ -360,22 +385,35 @@ impl HeuristicTests {
|
||||
.filters
|
||||
.send(Command::AddFilter(Box::new(sim_filter)))?;
|
||||
|
||||
if responses[0].is_directory() {
|
||||
// response is either a 3XX with a Location header that matches url + '/'
|
||||
// or it's a 2XX that ends with a '/'
|
||||
// or it's a 403 that ends with a '/'
|
||||
|
||||
// set the wildcard flag to true, so we can check it when preventing
|
||||
// recursion in event_handlers/scans.rs
|
||||
responses[0].set_wildcard(true);
|
||||
|
||||
// add the response to the global list of responses
|
||||
RESPONSES.insert(responses[0].clone());
|
||||
|
||||
// function-internal magic number, indicates that we've detected a wildcard directory
|
||||
req_counter += 100;
|
||||
}
|
||||
|
||||
// reset the responses for the next method, if it exists
|
||||
responses.clear();
|
||||
|
||||
// report to the user, if appropriate
|
||||
if matches!(
|
||||
self.handles.config.output_level,
|
||||
OutputLevel::Default | OutputLevel::Quiet
|
||||
) {
|
||||
let msg = format!("{} {:>8} {:>9} {:>9} {:>9} {} => {} {}-like response ({} {}); toggle this behavior by using {}\n", status_colorizer("WLD"), "-", "-", "-", "-", style(target_url).cyan(), style("auto-filtering").bright().green(), style("404").red(), style(filter_length).cyan(), filter_type, style("--dont-filter").yellow());
|
||||
ferox_print(&msg, &PROGRESS_PRINTER);
|
||||
}
|
||||
}
|
||||
|
||||
log::trace!("exit: detect_404_like_responses");
|
||||
|
||||
Ok(req_counter)
|
||||
let retval = if req_counter > 100 {
|
||||
WildcardResult::WildcardDirectory(req_counter)
|
||||
} else {
|
||||
WildcardResult::FourOhFourLike(req_counter)
|
||||
};
|
||||
|
||||
Ok(Some(retval))
|
||||
}
|
||||
|
||||
/// for all responses, examine chars/words/lines
|
||||
@@ -386,11 +424,13 @@ impl HeuristicTests {
|
||||
fn examine_404_like_responses(
|
||||
&self,
|
||||
responses: &[FeroxResponse],
|
||||
) -> Option<(Command, &'static str, usize)> {
|
||||
) -> Option<Box<WildcardFilter>> {
|
||||
let mut size_sentry = true;
|
||||
let mut word_sentry = true;
|
||||
let mut line_sentry = true;
|
||||
|
||||
let method = responses[0].method();
|
||||
let status_code = responses[0].status();
|
||||
let content_length = responses[0].content_length();
|
||||
let word_count = responses[0].word_count();
|
||||
let line_count = responses[0].line_count();
|
||||
@@ -411,36 +451,61 @@ impl HeuristicTests {
|
||||
}
|
||||
}
|
||||
|
||||
// the if/else-if/else nature of the block means that we'll get the most
|
||||
// specific match, if one is to be had
|
||||
//
|
||||
// each block returns the information needed to send the filter away and
|
||||
// display a message to the user
|
||||
if size_sentry {
|
||||
// - command to send to the filters handler
|
||||
// - the unit-type we're filtering on (bytes/words/lines)
|
||||
// - the value associated with the unit-type on which we're filtering
|
||||
Some((
|
||||
Command::AddFilter(Box::new(SizeFilter { content_length })),
|
||||
"bytes",
|
||||
content_length as usize,
|
||||
))
|
||||
} else if word_sentry {
|
||||
Some((
|
||||
Command::AddFilter(Box::new(WordsFilter { word_count })),
|
||||
"words",
|
||||
word_count,
|
||||
))
|
||||
} else if line_sentry {
|
||||
Some((
|
||||
Command::AddFilter(Box::new(LinesFilter { line_count })),
|
||||
"lines",
|
||||
line_count,
|
||||
))
|
||||
} else {
|
||||
// no match was found; clear the vec and continue to the next
|
||||
None
|
||||
if !size_sentry && !word_sentry && !line_sentry {
|
||||
// none of the response lengths match, so we can't filter on any of them
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut wildcard = WildcardFilter {
|
||||
content_length: None,
|
||||
line_count: None,
|
||||
word_count: None,
|
||||
method: method.to_string(),
|
||||
status_code: status_code.as_u16(),
|
||||
dont_filter: self.handles.config.dont_filter,
|
||||
};
|
||||
|
||||
match (size_sentry, word_sentry, line_sentry) {
|
||||
(true, true, true) => {
|
||||
// all three types of length match, so we can't filter on any of them
|
||||
wildcard.content_length = Some(content_length);
|
||||
wildcard.word_count = Some(word_count);
|
||||
wildcard.line_count = Some(line_count);
|
||||
}
|
||||
(true, true, false) => {
|
||||
// content length and word count match, so we can filter on either
|
||||
wildcard.content_length = Some(content_length);
|
||||
wildcard.word_count = Some(word_count);
|
||||
}
|
||||
(true, false, true) => {
|
||||
// content length and line count match, so we can filter on either
|
||||
wildcard.content_length = Some(content_length);
|
||||
wildcard.line_count = Some(line_count);
|
||||
}
|
||||
(false, true, true) => {
|
||||
// word count and line count match, so we can filter on either
|
||||
wildcard.word_count = Some(word_count);
|
||||
wildcard.line_count = Some(line_count);
|
||||
}
|
||||
(true, false, false) => {
|
||||
// content length matches, so we can filter on that
|
||||
wildcard.content_length = Some(content_length);
|
||||
}
|
||||
(false, true, false) => {
|
||||
// word count matches, so we can filter on that
|
||||
wildcard.word_count = Some(word_count);
|
||||
}
|
||||
(false, false, true) => {
|
||||
// line count matches, so we can filter on that
|
||||
wildcard.line_count = Some(line_count);
|
||||
}
|
||||
(false, false, false) => {
|
||||
// none of the length types match, so we can't filter on any of them
|
||||
unreachable!("no wildcard size matches; handled by the if statement above");
|
||||
}
|
||||
};
|
||||
|
||||
Some(Box::new(wildcard))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
32
src/main.rs
32
src/main.rs
@@ -1,11 +1,14 @@
|
||||
use std::io::stdin;
|
||||
use std::{
|
||||
env::args,
|
||||
env::{
|
||||
args,
|
||||
consts::{ARCH, OS},
|
||||
},
|
||||
fs::{create_dir, remove_file, File},
|
||||
io::{stderr, BufRead, BufReader},
|
||||
ops::Index,
|
||||
path::Path,
|
||||
process::Command,
|
||||
process::{exit, Command},
|
||||
sync::{atomic::Ordering, Arc},
|
||||
};
|
||||
|
||||
@@ -38,6 +41,7 @@ use feroxbuster::{
|
||||
use feroxbuster::{utils::set_open_file_limit, DEFAULT_OPEN_FILE_LIMIT};
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use self_update::cargo_crate_version;
|
||||
|
||||
lazy_static! {
|
||||
/// Limits the number of parallel scans active at any given time when using --parallel
|
||||
@@ -219,6 +223,30 @@ async fn wrapped_main(config: Arc<Configuration>) -> Result<()> {
|
||||
PROGRESS_BAR.join().unwrap();
|
||||
});
|
||||
|
||||
// check if update_app is true
|
||||
if config.update_app {
|
||||
let target_os = format!("{}-{}", ARCH, OS);
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let status = self_update::backends::github::Update::configure()
|
||||
.repo_owner("epi052")
|
||||
.repo_name("feroxbuster")
|
||||
.bin_name("feroxbuster")
|
||||
.target(target_os.as_str())
|
||||
.show_download_progress(true)
|
||||
.current_version(cargo_crate_version!())
|
||||
.build()
|
||||
.unwrap()
|
||||
.update()
|
||||
.unwrap();
|
||||
|
||||
println!("Updated version: `{}`!", status.version());
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// cloning an Arc is cheap (it's basically a pointer into the heap)
|
||||
// so that will allow for cheap/safe sharing of a single wordlist across multi-target scans
|
||||
// as well as additional directories found as part of recursion
|
||||
|
||||
@@ -40,7 +40,7 @@ pub fn initialize() -> Command {
|
||||
Arg::new("url")
|
||||
.short('u')
|
||||
.long("url")
|
||||
.required_unless_present_any(["stdin", "resume_from"])
|
||||
.required_unless_present_any(["stdin", "resume_from", "update_app"])
|
||||
.help_heading("Target selection")
|
||||
.value_name("URL")
|
||||
.use_value_delimiter(true)
|
||||
@@ -91,12 +91,14 @@ pub fn initialize() -> Command {
|
||||
.long("smart")
|
||||
.num_args(0)
|
||||
.help_heading("Composite settings")
|
||||
.conflicts_with_all(["rate_limit", "auto_bail"])
|
||||
.help("Set --extract-links, --auto-tune, --collect-words, and --collect-backups to true"),
|
||||
).arg(
|
||||
Arg::new("thorough")
|
||||
.long("thorough")
|
||||
.num_args(0)
|
||||
.help_heading("Composite settings")
|
||||
.conflicts_with_all(["rate_limit", "auto_bail"])
|
||||
.help("Use the same settings as --smart and set --collect-extensions to true"),
|
||||
);
|
||||
|
||||
@@ -473,6 +475,7 @@ pub fn initialize() -> Command {
|
||||
Arg::new("wordlist")
|
||||
.short('w')
|
||||
.long("wordlist")
|
||||
.required_unless_present_any(["update_app"])
|
||||
.value_hint(ValueHint::FilePath)
|
||||
.value_name("FILE")
|
||||
.help("Path to the wordlist")
|
||||
@@ -607,6 +610,15 @@ pub fn initialize() -> Command {
|
||||
.args(["debug_log", "output"])
|
||||
.multiple(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("update_app")
|
||||
.short('U')
|
||||
.long("update")
|
||||
.conflicts_with_all(["url", "wordlist"])
|
||||
.num_args(0)
|
||||
.help_heading("Update settings")
|
||||
.help("Update the app to the latest version"),
|
||||
)
|
||||
.after_long_help(EPILOGUE);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -3,11 +3,12 @@ use super::*;
|
||||
use crate::event_handlers::Handles;
|
||||
use crate::filters::{
|
||||
EmptyFilter, LinesFilter, RegexFilter, SimilarityFilter, SizeFilter, StatusCodeFilter,
|
||||
WordsFilter,
|
||||
WildcardFilter, WordsFilter,
|
||||
};
|
||||
use crate::traits::FeroxFilter;
|
||||
use crate::Command::AddFilter;
|
||||
use crate::{
|
||||
banner::Banner,
|
||||
config::OutputLevel,
|
||||
progress::PROGRESS_PRINTER,
|
||||
progress::{add_bar, BarType},
|
||||
@@ -182,6 +183,10 @@ impl FeroxScans {
|
||||
serde_json::from_value::<WordsFilter>(filter.clone())
|
||||
{
|
||||
Box::new(deserialized)
|
||||
} else if let Ok(deserialized) =
|
||||
serde_json::from_value::<WildcardFilter>(filter.clone())
|
||||
{
|
||||
Box::new(deserialized)
|
||||
} else if let Ok(deserialized) =
|
||||
serde_json::from_value::<SizeFilter>(filter.clone())
|
||||
{
|
||||
@@ -446,6 +451,12 @@ impl FeroxScans {
|
||||
};
|
||||
|
||||
self.menu.clear_screen();
|
||||
|
||||
let banner = Banner::new(&[handles.config.target_url.clone()], &handles.config);
|
||||
banner
|
||||
.print_to(&self.menu.term, handles.config.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
self.menu.show_progress_bars();
|
||||
|
||||
result
|
||||
|
||||
@@ -10,6 +10,7 @@ use lazy_static::lazy_static;
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
use crate::filters::{create_similarity_filter, EmptyFilter, SimilarityFilter};
|
||||
use crate::heuristics::WildcardResult;
|
||||
use crate::Command::AddFilter;
|
||||
use crate::{
|
||||
event_handlers::{
|
||||
@@ -303,7 +304,21 @@ impl FeroxScanner {
|
||||
// wildcard test
|
||||
let num_reqs_made = test.detect_404_like_responses(&self.target_url).await?;
|
||||
|
||||
progress_bar.inc(num_reqs_made);
|
||||
match num_reqs_made {
|
||||
Some(WildcardResult::WildcardDirectory(num_reqs)) => {
|
||||
let message = format!(
|
||||
"=> {} dir! {} recursion",
|
||||
style("Wildcard").blue().bright(),
|
||||
style("stopped").red()
|
||||
);
|
||||
progress_bar.set_message(&message);
|
||||
progress_bar.inc(num_reqs as u64);
|
||||
}
|
||||
Some(WildcardResult::FourOhFourLike(num_reqs)) => {
|
||||
progress_bar.inc(num_reqs as u64);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Arc clones to be passed around to the various scans
|
||||
|
||||
@@ -437,7 +437,7 @@ impl Requester {
|
||||
.handles
|
||||
.filters
|
||||
.data
|
||||
.should_filter_response(&ferox_response)
|
||||
.should_filter_response(&ferox_response, self.handles.stats.tx.clone())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
//! collection of all traits used
|
||||
use crate::filters::{
|
||||
LinesFilter, RegexFilter, SimilarityFilter, SizeFilter, StatusCodeFilter, WordsFilter,
|
||||
LinesFilter, RegexFilter, SimilarityFilter, SizeFilter, StatusCodeFilter, WildcardFilter,
|
||||
WordsFilter,
|
||||
};
|
||||
use crate::response::FeroxResponse;
|
||||
use crate::utils::status_colorizer;
|
||||
use anyhow::Result;
|
||||
use crossterm::style::{style, Stylize};
|
||||
use serde::Serialize;
|
||||
@@ -36,6 +38,44 @@ impl Display for dyn FeroxFilter {
|
||||
write!(f, "Response size: {}", style(filter.content_length).cyan())
|
||||
} else if let Some(filter) = self.as_any().downcast_ref::<RegexFilter>() {
|
||||
write!(f, "Regex: {}", style(&filter.raw_string).cyan())
|
||||
} else if let Some(filter) = self.as_any().downcast_ref::<WildcardFilter>() {
|
||||
let mut msg = format!(
|
||||
"{} requests with {} responses ",
|
||||
style(&filter.method).cyan(),
|
||||
status_colorizer(&filter.status_code.to_string())
|
||||
);
|
||||
|
||||
match (filter.content_length, filter.word_count, filter.line_count) {
|
||||
(None, None, None) => {
|
||||
unreachable!("wildcard filter without any filters set");
|
||||
}
|
||||
(None, None, Some(lc)) => {
|
||||
msg.push_str(&format!("containing {} lines", lc));
|
||||
}
|
||||
(None, Some(wc), None) => {
|
||||
msg.push_str(&format!("containing {} words", wc));
|
||||
}
|
||||
(None, Some(wc), Some(lc)) => {
|
||||
msg.push_str(&format!("containing {} words and {} lines", wc, lc));
|
||||
}
|
||||
(Some(cl), None, None) => {
|
||||
msg.push_str(&format!("containing {} bytes", cl));
|
||||
}
|
||||
(Some(cl), None, Some(lc)) => {
|
||||
msg.push_str(&format!("containing {} bytes and {} lines", cl, lc));
|
||||
}
|
||||
(Some(cl), Some(wc), None) => {
|
||||
msg.push_str(&format!("containing {} bytes and {} words", cl, wc));
|
||||
}
|
||||
(Some(cl), Some(wc), Some(lc)) => {
|
||||
msg.push_str(&format!(
|
||||
"containing {} bytes, {} words, and {} lines",
|
||||
cl, wc, lc
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, "{}", msg)
|
||||
} else if let Some(filter) = self.as_any().downcast_ref::<StatusCodeFilter>() {
|
||||
write!(f, "Status code: {}", style(filter.filter_code).cyan())
|
||||
} else if let Some(filter) = self.as_any().downcast_ref::<SimilarityFilter>() {
|
||||
|
||||
@@ -1420,3 +1420,19 @@ fn banner_prints_force_recursion() {
|
||||
.and(predicate::str::contains("─┴─")),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// test allows non-existent wordlist to trigger the banner printing to stderr
|
||||
/// expect to see all mandatory prints + force recursion
|
||||
fn banner_prints_update_app() {
|
||||
Command::cargo_bin("feroxbuster")
|
||||
.unwrap()
|
||||
.arg("--update")
|
||||
.assert()
|
||||
.success()
|
||||
.stderr(
|
||||
predicate::str::contains("─┬─")
|
||||
.and(predicate::str::contains("Update app"))
|
||||
.and(predicate::str::contains("─┴─")),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -180,9 +180,12 @@ fn test_static_wildcard_request_found() -> Result<(), Box<dyn std::error::Error>
|
||||
teardown_tmp_directory(tmp_dir);
|
||||
|
||||
cmd.assert().success().stdout(
|
||||
predicate::str::contains("WLD").and(predicate::str::contains(
|
||||
"auto-filtering 404-like response (1 lines);",
|
||||
)),
|
||||
predicate::str::contains("GET")
|
||||
.and(predicate::str::contains(
|
||||
"Auto-filtering found 404-like response and created new filter",
|
||||
))
|
||||
.and(predicate::str::contains("200"))
|
||||
.and(predicate::str::contains("1l")),
|
||||
);
|
||||
|
||||
assert_eq!(mock.hits(), 1);
|
||||
@@ -273,14 +276,14 @@ fn heuristics_wildcard_test_with_two_static_wildcards_with_silent_enabled(
|
||||
|
||||
let mock = srv.mock(|when, then| {
|
||||
when.method(GET)
|
||||
.path_matches(Regex::new("/[a-zA-Z0-9]{32}/").unwrap());
|
||||
.path_matches(Regex::new("/.?[a-zA-Z0-9]{32,}").unwrap());
|
||||
then.status(200)
|
||||
.body("this is a testAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
|
||||
});
|
||||
|
||||
let mock2 = srv.mock(|when, then| {
|
||||
when.method(GET)
|
||||
.path_matches(Regex::new("/[a-zA-Z0-9]{96}/").unwrap());
|
||||
.path_matches(Regex::new("/LICENSE").unwrap());
|
||||
then.status(200)
|
||||
.body("this is a testAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
|
||||
});
|
||||
@@ -291,7 +294,6 @@ fn heuristics_wildcard_test_with_two_static_wildcards_with_silent_enabled(
|
||||
.arg(srv.url("/"))
|
||||
.arg("--wordlist")
|
||||
.arg(file.as_os_str())
|
||||
.arg("--add-slash")
|
||||
.arg("--silent")
|
||||
.arg("--threads")
|
||||
.arg("1")
|
||||
@@ -299,9 +301,11 @@ fn heuristics_wildcard_test_with_two_static_wildcards_with_silent_enabled(
|
||||
|
||||
teardown_tmp_directory(tmp_dir);
|
||||
|
||||
cmd.assert().success().stdout(predicate::str::is_empty());
|
||||
cmd.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains(srv.url("/")));
|
||||
|
||||
assert_eq!(mock.hits(), 1);
|
||||
assert_eq!(mock.hits(), 4);
|
||||
assert_eq!(mock2.hits(), 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -573,7 +573,7 @@ fn scanner_recursion_works_with_403_directories() {
|
||||
let found_anyway = srv.mock(|when, then| {
|
||||
when.method(GET).path("/ignored/LICENSE");
|
||||
then.status(200)
|
||||
.body("this is a test\nThat rugf really tied the room together");
|
||||
.body("this is a test\nThat rug really tied the room together");
|
||||
});
|
||||
|
||||
let cmd = Command::cargo_bin("feroxbuster")
|
||||
@@ -588,9 +588,10 @@ fn scanner_recursion_works_with_403_directories() {
|
||||
predicate::str::contains("/LICENSE")
|
||||
.count(2)
|
||||
.and(predicate::str::contains("200"))
|
||||
.and(predicate::str::contains("WLD"))
|
||||
.and(predicate::str::contains("404"))
|
||||
.and(predicate::str::contains("53c Auto-filtering"))
|
||||
.and(predicate::str::contains(
|
||||
"auto-filtering 404-like response (53 bytes);",
|
||||
"Auto-filtering found 404-like response and created new filter;",
|
||||
))
|
||||
.and(predicate::str::contains("14c"))
|
||||
.and(predicate::str::contains("0c"))
|
||||
|
||||
Reference in New Issue
Block a user