Merge pull request #360 from epi052/329-dont-scan-enhancements

329 dont scan enhancements
This commit is contained in:
epi
2021-10-15 16:44:11 -05:00
committed by GitHub
17 changed files with 483 additions and 206 deletions

192
Cargo.lock generated
View File

@@ -47,9 +47,9 @@ dependencies = [
[[package]]
name = "assert_cmd"
version = "2.0.1"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b800c4403e8105d959595e1f88119e78bc12bc874c4336973658b648a746ba93"
checksum = "e996dc7940838b7ef1096b882e29ec30a3149a3a443cdc8dba19ed382eca1fe2"
dependencies = [
"bstr",
"doc-comment",
@@ -265,9 +265,9 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
version = "1.2.1"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "blocking"
@@ -285,9 +285,9 @@ dependencies = [
[[package]]
name = "bstr"
version = "0.2.16"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279"
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
dependencies = [
"lazy_static",
"memchr",
@@ -296,9 +296,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.7.0"
version = "3.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631"
checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538"
[[package]]
name = "bytes"
@@ -313,10 +313,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba"
[[package]]
name = "cc"
version = "1.0.70"
name = "castaway"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
checksum = "ed247d1586918e46f2bbe0f13b06498db8dab5a8c1093f156652e9f2e0a73fc3"
[[package]]
name = "cc"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
[[package]]
name = "cfg-if"
@@ -350,13 +356,13 @@ dependencies = [
[[package]]
name = "console"
version = "0.14.1"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45"
checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"once_cell",
"regex",
"terminal_size",
"unicode-width",
@@ -365,9 +371,9 @@ dependencies = [
[[package]]
name = "core-foundation"
version = "0.9.1"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3"
dependencies = [
"core-foundation-sys",
"libc",
@@ -375,9 +381,9 @@ dependencies = [
[[package]]
name = "core-foundation-sys"
version = "0.8.2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "crossbeam-utils"
@@ -432,9 +438,9 @@ dependencies = [
[[package]]
name = "ctrlc"
version = "3.2.0"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "377c9b002a72a0b2c1a18c62e2f3864bdfea4a015e3683a96e24aa45dd6c02d1"
checksum = "a19c6cedffdc8c03a3346d723eb20bd85a13362bb96dc2ac000842c6381ec7bf"
dependencies = [
"nix",
"winapi",
@@ -442,9 +448,9 @@ dependencies = [
[[package]]
name = "curl"
version = "0.4.38"
version = "0.4.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "003cb79c1c6d1c93344c7e1201bb51c2148f24ec2bd9c253709d6b2efb796515"
checksum = "aaa3b8db7f3341ddef15786d250106334d4a6c4b0ae4a46cd77082777d9849b9"
dependencies = [
"curl-sys",
"libc",
@@ -457,9 +463,9 @@ dependencies = [
[[package]]
name = "curl-sys"
version = "0.4.47+curl-7.79.0"
version = "0.4.49+curl-7.79.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ab94a47d9b61f2d905beb7a3d46aba7704c9f1dfcf84e7d178998d9e95f7989"
checksum = "e0f44960aea24a786a46907b8824ebc0e66ca06bf4e4978408c7499620343483"
dependencies = [
"cc",
"libc",
@@ -620,10 +626,12 @@ dependencies = [
"rlimit",
"serde",
"serde_json",
"serde_regex",
"tempfile",
"tokio",
"tokio-util",
"toml",
"url",
"uuid",
]
@@ -814,9 +822,9 @@ dependencies = [
[[package]]
name = "h2"
version = "0.3.4"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7f3675cfef6a30c8031cf9e6493ebdc3bb3272a3fea3923c4210d1830e6a472"
checksum = "6c06815895acec637cd6ed6e9662c935b866d20a106f8361892893a7d9234964"
dependencies = [
"bytes",
"fnv",
@@ -848,9 +856,9 @@ dependencies = [
[[package]]
name = "http"
version = "0.2.4"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11"
checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b"
dependencies = [
"bytes",
"fnv",
@@ -986,9 +994,9 @@ dependencies = [
[[package]]
name = "instant"
version = "0.1.10"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d"
checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
dependencies = [
"cfg-if",
]
@@ -1001,11 +1009,12 @@ checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9"
[[package]]
name = "isahc"
version = "1.5.0"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "431445cb4ba85a80cb1438a9ae8042dadb78ae4046ecee89ad027b614aa0ddb7"
checksum = "40ef5402b1791c9fc479ef9871601a2f10e4cc0f14414a5c9c6e043fb51e5a56"
dependencies = [
"async-channel",
"castaway",
"crossbeam-utils",
"curl",
"curl-sys",
@@ -1118,15 +1127,15 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760"
[[package]]
name = "libc"
version = "0.2.102"
version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103"
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
[[package]]
name = "libnghttp2-sys"
version = "0.1.6+1.43.0"
version = "0.1.7+1.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0af55541a8827e138d59ec9e5877fb6095ece63fb6f4da45e7491b4fbd262855"
checksum = "57ed28aba195b38d5ff02b9170cbff627e336a20925e43b4945390401c5dc93f"
dependencies = [
"cc",
"libc",
@@ -1238,9 +1247,9 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
[[package]]
name = "nix"
version = "0.22.1"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7555d6c7164cc913be1ce7f95cbecdabda61eb2ccd89008524af306fb7f5031"
checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188"
dependencies = [
"bitflags",
"cc",
@@ -1326,9 +1335,9 @@ dependencies = [
[[package]]
name = "openssl-sys"
version = "0.9.66"
version = "0.9.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1996d2d305e561b70d1ee0c53f1542833f4e1ac6ce9a6708b6ff2738ca67dc82"
checksum = "69df2d8dfc6ce3aaf44b40dec6f487d5a886516cf6879c49e98e0710f310a058"
dependencies = [
"autocfg",
"cc",
@@ -1434,9 +1443,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.19"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb"
[[package]]
name = "polling"
@@ -1453,9 +1462,9 @@ dependencies = [
[[package]]
name = "ppv-lite86"
version = "0.2.10"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
checksum = "c3ca011bd0129ff4ae15cd04c4eef202cadf6c51c21e47aba319b4e0501db741"
[[package]]
name = "precomputed-hash"
@@ -1465,9 +1474,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "predicates"
version = "2.0.2"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c143348f141cc87aab5b950021bac6145d0e5ae754b0591de23244cee42c9308"
checksum = "5c6ce811d0b2e103743eec01db1c50612221f173084ce2f7941053e94b6bb474"
dependencies = [
"difflib",
"float-cmp",
@@ -1485,12 +1494,12 @@ checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451"
[[package]]
name = "predicates-tree"
version = "1.0.3"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7dd0fd014130206c9352efbdc92be592751b2b9274dff685348341082c6ea3d"
checksum = "338c7be2905b732ae3984a2f40032b5e94fd8f52505b186c7d4d68d193445df7"
dependencies = [
"predicates-core",
"treeline",
"termtree",
]
[[package]]
@@ -1507,9 +1516,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
[[package]]
name = "proc-macro2"
version = "1.0.29"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70"
dependencies = [
"unicode-xid",
]
@@ -1525,9 +1534,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
"proc-macro2",
]
@@ -1625,9 +1634,9 @@ dependencies = [
[[package]]
name = "reqwest"
version = "0.11.4"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "246e9f61b9bb77df069a947682be06e31ac43ea37862e244a69f177694ea6d22"
checksum = "51c732d463dd300362ffb44b7b125f299c23d2990411a4253824630ebc7467fb"
dependencies = [
"base64",
"bytes",
@@ -1647,6 +1656,7 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
"tokio-native-tls",
@@ -1697,9 +1707,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "security-framework"
version = "2.3.1"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467"
checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87"
dependencies = [
"bitflags",
"core-foundation",
@@ -1809,9 +1819,9 @@ checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
[[package]]
name = "slab"
version = "0.4.4"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590"
checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
[[package]]
name = "sluice"
@@ -1826,9 +1836,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.6.1"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
[[package]]
name = "socket2"
@@ -1842,12 +1852,13 @@ dependencies = [
[[package]]
name = "string_cache"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ddb1139b5353f96e429e1a5e19fbaf663bddedaa06d1dbd49f82e352601209a"
checksum = "923f0f39b6267d37d23ce71ae7235602134b250ace715dd2c90421998ddac0c6"
dependencies = [
"lazy_static",
"new_debug_unreachable",
"parking_lot",
"phf_shared",
"precomputed-hash",
]
@@ -1860,9 +1871,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.76"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84"
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
dependencies = [
"proc-macro2",
"quote",
@@ -1913,6 +1924,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "termtree"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78fbf2dd23e79c28ccfa2472d3e6b3b189866ffef1aeb91f17c2d968b6586378"
[[package]]
name = "textwrap"
version = "0.11.0"
@@ -1924,18 +1941,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.29"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.29"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [
"proc-macro2",
"quote",
@@ -1953,9 +1970,9 @@ dependencies = [
[[package]]
name = "tinyvec"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5241dd6f21443a3606b432718b166d3cedc962fd4b8bea54a8bc7f514ebda986"
checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7"
dependencies = [
"tinyvec_macros",
]
@@ -1968,9 +1985,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.11.0"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4efe6fc2395938c8155973d7be49fe8d03a843726e285e100a8a383cc0154ce"
checksum = "c2c2416fdedca8443ae44b4527de1ea633af61d8f7169ffa6e72c5b53d24efcc"
dependencies = [
"autocfg",
"bytes",
@@ -1988,9 +2005,9 @@ dependencies = [
[[package]]
name = "tokio-macros"
version = "1.3.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110"
checksum = "b2dd85aeaba7b68df939bd357c6afb36c87951be9e80bf9c859f2fc3e9fca0fd"
dependencies = [
"proc-macro2",
"quote",
@@ -2061,9 +2078,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
[[package]]
name = "tracing"
version = "0.1.27"
version = "0.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2ba9ab62b7d6497a8638dfda5e5c4fb3b2d5a7fca4118f2b96151c8ef1a437e"
checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
dependencies = [
"cfg-if",
"log",
@@ -2074,9 +2091,9 @@ dependencies = [
[[package]]
name = "tracing-attributes"
version = "0.1.16"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98863d0dd09fa59a1b79c6750ad80dbda6b75f4e71c437a6a1a8cb91a8bcbd77"
checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e"
dependencies = [
"proc-macro2",
"quote",
@@ -2085,9 +2102,9 @@ dependencies = [
[[package]]
name = "tracing-core"
version = "0.1.20"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46125608c26121c81b0c6d693eab5a420e416da7e43c426d2e8f7df8da8a3acf"
checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4"
dependencies = [
"lazy_static",
]
@@ -2102,12 +2119,6 @@ dependencies = [
"tracing",
]
[[package]]
name = "treeline"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
[[package]]
name = "try-lock"
version = "0.2.3"
@@ -2116,9 +2127,9 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "unicode-bidi"
version = "0.3.6"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "246f4c42e67e7a4e3c6106ff716a5d067d4132a642840b242e357e468a2a0085"
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
[[package]]
name = "unicode-normalization"
@@ -2151,6 +2162,7 @@ dependencies = [
"idna",
"matches",
"percent-encoding",
"serde",
]
[[package]]
@@ -2228,8 +2240,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
dependencies = [
"cfg-if",
"serde",
"serde_json",
"wasm-bindgen-macro",
]

View File

@@ -28,6 +28,8 @@ tokio-util = {version = "0.6", features = ["codec"]}
log = "0.4"
env_logger = "0.9"
reqwest = { version = "0.11", features = ["socks"] }
url = { version = "2.2", features = ["serde"]} # uses feature unification to add 'serde' to reqwest::Url
serde_regex = "1.1"
clap = "2.33"
lazy_static = "1.4"
toml = "0.5"
@@ -35,7 +37,7 @@ serde = { version = "1.0", features = ["derive", "rc"] }
serde_json = "1.0"
uuid = { version = "0.8", features = ["v4"] }
indicatif = "0.15"
console = "0.14"
console = "0.15"
openssl = { version = "0.10", features = ["vendored"] }
dirs = "4.0"
regex = "1"

View File

@@ -32,6 +32,7 @@
# insecure = true
# extensions = ["php", "html"]
# url_denylist = ["http://dont-scan.me", "https://also-not.me"]
# regex_denylist = ["/deny.*"]
# no_recursion = true
# add_slash = true
# stdin = true

View File

@@ -41,7 +41,7 @@ _feroxbuster() {
'--user-agent=[Sets the User-Agent (default: feroxbuster/VERSION)]' \
'*-x+[File extension(s) to search for (ex: -x php -x pdf js)]' \
'*--extensions=[File extension(s) to search for (ex: -x php -x pdf js)]' \
'*--dont-scan=[URL(s) to exclude from recursion/scans]' \
'*--dont-scan=[URL(s) or Regex Pattern(s) to exclude from recursion/scans]' \
'*-H+[Specify HTTP headers (ex: -H Header:val '\''stuff: things'\'')]' \
'*--headers=[Specify HTTP headers (ex: -H Header:val '\''stuff: things'\'')]' \
'*-Q+[Specify URL query parameters (ex: -Q token=stuff -Q secret=key)]' \

View File

@@ -46,7 +46,7 @@ Register-ArgumentCompleter -Native -CommandName 'feroxbuster' -ScriptBlock {
[CompletionResult]::new('--user-agent', 'user-agent', [CompletionResultType]::ParameterName, 'Sets the User-Agent (default: feroxbuster/VERSION)')
[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('--dont-scan', 'dont-scan', [CompletionResultType]::ParameterName, 'URL(s) to exclude from recursion/scans')
[CompletionResult]::new('--dont-scan', 'dont-scan', [CompletionResultType]::ParameterName, 'URL(s) or Regex Pattern(s) to exclude from recursion/scans')
[CompletionResult]::new('-H', 'H', [CompletionResultType]::ParameterName, 'Specify HTTP headers (ex: -H Header:val ''stuff: things'')')
[CompletionResult]::new('--headers', 'headers', [CompletionResultType]::ParameterName, 'Specify HTTP headers (ex: -H Header:val ''stuff: things'')')
[CompletionResult]::new('-Q', 'Q', [CompletionResultType]::ParameterName, 'Specify URL query parameters (ex: -Q token=stuff -Q secret=key)')

View File

@@ -12,7 +12,7 @@ complete -c feroxbuster -n "__fish_use_subcommand" -l resume-from -d 'State file
complete -c feroxbuster -n "__fish_use_subcommand" -l debug-log -d 'Output file to write log entries (use w/ --json for JSON entries)'
complete -c feroxbuster -n "__fish_use_subcommand" -s a -l user-agent -d 'Sets the User-Agent (default: feroxbuster/VERSION)'
complete -c feroxbuster -n "__fish_use_subcommand" -s x -l extensions -d 'File extension(s) to search for (ex: -x php -x pdf js)'
complete -c feroxbuster -n "__fish_use_subcommand" -l dont-scan -d 'URL(s) to exclude from recursion/scans'
complete -c feroxbuster -n "__fish_use_subcommand" -l dont-scan -d 'URL(s) or Regex Pattern(s) to exclude from recursion/scans'
complete -c feroxbuster -n "__fish_use_subcommand" -s H -l headers -d 'Specify HTTP headers (ex: -H Header:val \'stuff: things\')'
complete -c feroxbuster -n "__fish_use_subcommand" -s Q -l query -d 'Specify URL query parameters (ex: -Q token=stuff -Q secret=key)'
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)'

View File

@@ -168,7 +168,19 @@ impl Banner {
}
for denied_url in &config.url_denylist {
url_denylist.push(BannerEntry::new("🚫", "Don't Scan", denied_url));
url_denylist.push(BannerEntry::new(
"🚫",
"Don't Scan Url",
denied_url.as_str(),
));
}
for denied_regex in &config.regex_denylist {
url_denylist.push(BannerEntry::new(
"🚫",
"Don't Scan Regex",
denied_regex.as_str(),
));
}
let mut codes = vec![];

View File

@@ -10,7 +10,8 @@ use crate::{
};
use anyhow::{anyhow, Context, Result};
use clap::{value_t, ArgMatches};
use reqwest::{Client, StatusCode};
use regex::Regex;
use reqwest::{Client, StatusCode, Url};
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
@@ -255,7 +256,10 @@ pub struct Configuration {
/// URLs that should never be scanned/recursed into
#[serde(default)]
pub url_denylist: Vec<String>,
pub url_denylist: Vec<Url>,
#[serde(with = "serde_regex", default)]
pub regex_denylist: Vec<Regex>,
}
impl Default for Configuration {
@@ -314,6 +318,7 @@ impl Default for Configuration {
filter_size: Vec::new(),
filter_regex: Vec::new(),
url_denylist: Vec::new(),
regex_denylist: Vec::new(),
filter_line_count: Vec::new(),
filter_word_count: Vec::new(),
filter_status: Vec::new(),
@@ -353,6 +358,7 @@ impl Configuration {
/// - **insecure**: `false` (don't be insecure, i.e. don't allow invalid certs)
/// - **extensions**: `None`
/// - **url_denylist**: `None`
/// - **regex_denylist**: `None`
/// - **filter_size**: `None`
/// - **filter_similar**: `None`
/// - **filter_regex**: `None`
@@ -550,8 +556,54 @@ impl Configuration {
config.extensions = arg.map(|val| val.to_string()).collect();
}
if args.is_present("stdin") {
config.stdin = true;
} else if let Some(url) = args.value_of("url") {
config.target_url = String::from(url);
}
if let Some(arg) = args.values_of("url_denylist") {
config.url_denylist = arg.map(|val| val.to_string()).collect();
// compile all regular expressions and absolute urls used for --dont-scan
//
// when --dont-scan is used, the should_deny_url function is called at least once per
// url to be scanned. With the addition of regex support, I want to move parsing
// out of should_deny_url and into here, so it's performed once instead of thousands
// of times
for denier in arg.into_iter() {
// could be an absolute url or a regex, need to determine which and populate the
// appropriate vector
match Url::parse(denier.trim_end_matches('/')) {
Ok(absolute) => {
// denier is an absolute url and can be parsed as such
config.url_denylist.push(absolute);
}
Err(err) => {
// there are some expected errors that happen when we try to parse a url
// ex: Url::parse("/login") -> Err("relative URL without a base")
// ex: Url::parse("http:") -> Err("empty host")
//
// these are known errors and are used to determine a valid value to
// --dont-scan, when it's not an absolute url
//
// when expected errors are encountered, we're going to assume
// that the input is a regular expression to be parsed. The possibility
// exists that the user rolled their face across the keyboard and we're
// dealing with the results, in which case we'll report it as an error and
// give up
if err.to_string().contains("relative URL without a base")
|| err.to_string().contains("empty host")
{
let regex = Regex::new(denier)
.unwrap_or_else(|e| report_and_exit(&e.to_string()));
config.regex_denylist.push(regex);
} else {
// unexpected error has occurred; bail
report_and_exit(&err.to_string());
}
}
}
}
}
if let Some(arg) = args.values_of("filter_regex") {
@@ -639,12 +691,6 @@ impl Configuration {
config.json = true;
}
if args.is_present("stdin") {
config.stdin = true;
} else if let Some(url) = args.value_of("url") {
config.target_url = String::from(url);
}
////
// organizational breakpoint; all options below alter the Client configuration
////
@@ -787,11 +833,15 @@ impl Configuration {
update_if_not_default!(&mut conf.insecure, new.insecure, false);
update_if_not_default!(&mut conf.extract_links, new.extract_links, false);
update_if_not_default!(&mut conf.extensions, new.extensions, Vec::<String>::new());
update_if_not_default!(
&mut conf.url_denylist,
new.url_denylist,
Vec::<String>::new()
);
update_if_not_default!(&mut conf.url_denylist, new.url_denylist, Vec::<Url>::new());
if !new.regex_denylist.is_empty() {
// cant use the update_if_not_default macro due to the following error
//
// binary operation `!=` cannot be applied to type `Vec<regex::Regex>`
//
// if we get a non-empty list of regex in the new config, override the old
conf.regex_denylist = new.regex_denylist;
}
update_if_not_default!(&mut conf.headers, new.headers, HashMap::new());
update_if_not_default!(&mut conf.queries, new.queries, Vec::new());
update_if_not_default!(&mut conf.no_recursion, new.no_recursion, false);

View File

@@ -1,6 +1,8 @@
use super::utils::*;
use super::*;
use crate::{traits::FeroxSerialize, DEFAULT_CONFIG_NAME};
use regex::Regex;
use reqwest::Url;
use std::{collections::HashMap, fs::write};
use tempfile::TempDir;
@@ -30,6 +32,7 @@ fn setup_config_test() -> Configuration {
insecure = true
extensions = ["html", "php", "js"]
url_denylist = ["http://dont-scan.me", "https://also-not.me"]
regex_denylist = ["/deny.*"]
headers = {stuff = "things", mostuff = "mothings"}
queries = [["name","value"], ["rick", "astley"]]
no_recursion = true
@@ -89,10 +92,11 @@ fn default_configuration() {
assert!(!config.redirects);
assert!(!config.extract_links);
assert!(!config.insecure);
assert!(config.regex_denylist.is_empty());
assert_eq!(config.queries, Vec::new());
assert_eq!(config.filter_size, Vec::<u64>::new());
assert_eq!(config.extensions, Vec::<String>::new());
assert_eq!(config.url_denylist, Vec::<String>::new());
assert_eq!(config.url_denylist, Vec::<Url>::new());
assert_eq!(config.filter_regex, Vec::<String>::new());
assert_eq!(config.filter_similar, Vec::<String>::new());
assert_eq!(config.filter_word_count, Vec::<usize>::new());
@@ -290,13 +294,26 @@ fn config_reads_extensions() {
assert_eq!(config.extensions, vec!["html", "php", "js"]);
}
#[test]
/// parse the test config and see that the value parsed is correct
fn config_reads_regex_denylist() {
let config = setup_config_test();
assert_eq!(
config.regex_denylist[0].as_str(),
Regex::new("/deny.*").unwrap().as_str()
);
}
#[test]
/// parse the test config and see that the value parsed is correct
fn config_reads_url_denylist() {
let config = setup_config_test();
assert_eq!(
config.url_denylist,
vec!["http://dont-scan.me", "https://also-not.me"]
vec![
Url::parse("http://dont-scan.me").unwrap(),
Url::parse("https://also-not.me").unwrap(),
]
);
}

View File

@@ -189,7 +189,8 @@ impl ScanHandler {
/// wrapper around scanning a url to stay DRY
async fn ordered_scan_url(&mut self, targets: Vec<String>, order: ScanOrder) -> Result<()> {
log::trace!("enter: ordered_scan_url({:?}, {:?})", targets, order);
let should_test_deny = !self.handles.config.url_denylist.is_empty();
let should_test_deny = !self.handles.config.url_denylist.is_empty()
|| !self.handles.config.regex_denylist.is_empty();
for target in targets {
if self.data.contains(&target) && matches!(order, ScanOrder::Latest) {

View File

@@ -310,14 +310,16 @@ impl<'a> Extractor<'a> {
bail!("previously seen url");
}
if !self.handles.config.url_denylist.is_empty()
if (!self.handles.config.url_denylist.is_empty()
|| !self.handles.config.regex_denylist.is_empty())
&& should_deny_url(&new_url, self.handles.clone())?
{
// can't allow a denied url to be requested
bail!(
"prevented request to {} due to {:?}",
"prevented request to {} due to {:?} || {:?}",
url,
self.handles.config.url_denylist
self.handles.config.url_denylist,
self.handles.config.regex_denylist,
);
}

View File

@@ -154,6 +154,28 @@ async fn get_targets(handles: Arc<Handles>) -> Result<Vec<String>> {
targets.push(handles.config.target_url.clone());
}
// remove footgun that arises if a --dont-scan value matches on a base url
for target in &targets {
for denier in &handles.config.regex_denylist {
if denier.is_match(target) {
bail!(
"The regex '{}' matches {}; the scan will never start",
denier,
target
);
}
}
for denier in &handles.config.url_denylist {
if denier.as_str().trim_end_matches('/') == target.trim_end_matches('/') {
bail!(
"The url '{}' matches {}; the scan will never start",
denier,
target
);
}
}
}
log::trace!("exit: get_targets -> {:?}", targets);
Ok(targets)
@@ -231,7 +253,7 @@ async fn wrapped_main(config: Arc<Configuration>) -> Result<()> {
Err(e) => {
// should only happen in the event that there was an error reading from stdin
clean_up(handles, tasks).await?;
bail!("Could not get determine initial targets: {}", e);
bail!("Could not determine initial targets: {}", e);
}
};

View File

@@ -235,7 +235,7 @@ pub fn initialize() -> App<'static, 'static> {
.multiple(true)
.use_delimiter(true)
.help(
"URL(s) to exclude from recursion/scans",
"URL(s) or Regex Pattern(s) to exclude from recursion/scans",
),
)
.arg(

View File

@@ -306,7 +306,8 @@ impl Requester {
let urls =
FeroxUrl::from_string(&self.target_url, self.handles.clone()).formatted_urls(word)?;
let should_test_deny = !self.handles.config.url_denylist.is_empty();
let should_test_deny = !self.handles.config.url_denylist.is_empty()
|| !self.handles.config.regex_denylist.is_empty();
for url in urls {
// auto_tune is true, or rate_limit was set (mutually exclusive to user)

View File

@@ -1,6 +1,7 @@
use anyhow::{bail, Context, Result};
use console::{strip_ansi_codes, style, user_attended};
use indicatif::ProgressBar;
use regex::Regex;
use reqwest::{Client, Response, StatusCode, Url};
#[cfg(not(target_os = "windows"))]
use rlimit::{getrlimit, setrlimit, Resource};
@@ -307,6 +308,110 @@ where
Ok(())
}
/// determine if a url should be denied based on the given absolute url
fn should_deny_absolute(url_to_test: &Url, denier: &Url, handles: Arc<Handles>) -> Result<bool> {
log::trace!(
"enter: should_deny_absolute({}, {:?})",
url_to_test.as_str(),
denier.as_str(),
);
// simplest case is an exact match, check for it first
if url_to_test == denier {
log::trace!("exit: should_deny_absolute -> true");
return Ok(true);
}
match (url_to_test.host(), denier.host()) {
// .host() will return an enum with ipv4|6 or domain and is comparable
// whereas .domain() returns None for ip addresses
(Some(normed_host), Some(denier_host)) => {
if normed_host != denier_host {
// domains don't even match
return Ok(false);
}
}
_ => {
// one or the other couldn't determine the host value, which probably means
// it's not suitable for further comparison
return Ok(false);
}
}
let tested_host = url_to_test.host().unwrap(); // match above will catch errors
// at this point, we have a matching set of ips or domain names. now we can process the
// url path. The goal is to determine whether the given url's path is a subpath of any
// url in the deny list, for example
// GIVEN URL URL DENY LIST USER-SPECIFIED URLS TO SCAN
// http://some.domain/stuff/things, [http://some.domain/stuff], [http://some.domain] => true
// http://some.domain/stuff/things, [http://some.domain/stuff/things], [http://some.domain] => true
// http://some.domain/stuff/things, [http://some.domain/api], [http://some.domain] => false
// the examples above are all pretty obvious, the kicker comes when the blocking url's
// path is a parent to a scanned url
// http://some.domain/stuff/things, [http://some.domain/], [http://some.domain/stuff] => false
// http://some.domain/api, [http://some.domain/], [http://some.domain/stuff] => true
// we want to deny all children of the parent, unless that child is a child of a scan
// we specified through -u(s) or --stdin
let deny_path = denier.path();
let tested_path = url_to_test.path();
if tested_path.starts_with(deny_path) {
// at this point, we know that the given normalized path is a sub-path of the
// current deny-url, now we just need to check to see if this deny-url is a parent
// to a scanned url that is also a parent of the given url
for ferox_scan in handles.ferox_scans()?.get_active_scans() {
let scanner = Url::parse(ferox_scan.url().trim_end_matches('/'))
.with_context(|| format!("Could not parse {} as a url", ferox_scan))?;
if let Some(scan_host) = scanner.host() {
// same domain/ip check we perform on the denier above
if tested_host != scan_host {
// domains don't even match, keep on keepin' on...
continue;
}
} else {
// couldn't process .host from scanner
continue;
};
let scan_path = scanner.path();
if scan_path.starts_with(deny_path) && tested_path.starts_with(scan_path) {
// user-specified scan url is a sub-path of the deny-urls's path AND the
// url to check is a sub-path of the user-specified scan url
//
// the assumption is the user knew what they wanted and we're going to give
// the scanned url precedence, even though it's a sub-path
log::trace!("exit: should_deny_absolute -> false");
return Ok(false);
}
}
log::trace!("exit: should_deny_absolute -> true");
return Ok(true);
}
log::trace!("exit: should_deny_absolute -> false");
Ok(false)
}
/// determine if a url should be denied based on the given regular expression
///
/// the regex ONLY matches against the PATH of the url (not the scheme, host, port, etc)
fn should_deny_regex(url_to_test: &Url, denier: &Regex) -> bool {
log::trace!(
"enter: should_deny_regex({}, {})",
url_to_test.as_str(),
denier,
);
let result = denier.is_match(url_to_test.as_str());
log::trace!("exit: should_deny_regex -> {}", result);
result
}
/// determines whether or not a given url should be denied based on the user-supplied --dont-scan
/// flag
pub fn should_deny_url(url: &Url, handles: Arc<Handles>) -> Result<bool> {
@@ -316,91 +421,29 @@ pub fn should_deny_url(url: &Url, handles: Arc<Handles>) -> Result<bool> {
handles.config.url_denylist,
handles.ferox_scans()?
);
// normalization for comparison is to remove the trailing / if one exists, this is done for
// the given url and any url to which it's compared
let normed_url = Url::parse(url.to_string().trim_end_matches('/'))?;
for deny_url in &handles.config.url_denylist {
// parse the denying url for easier comparison
let denier = Url::parse(deny_url.trim_end_matches('/'))
.with_context(|| format!("Could not parse {} as a url", deny_url))?;
// simplest case is an exact match, check for it first
if normed_url == denier {
log::trace!("exit: should_deny_url -> true");
return Ok(true);
}
match (normed_url.host(), denier.host()) {
// .host() will return an enum with ipv4|6 or domain and is comparable
// whereas .domain() returns None for ip addresses
(Some(normed_host), Some(denier_host)) => {
if normed_host != denier_host {
// domains don't even match, keep on keepin' on...
continue;
}
}
_ => {
// one or the other couldn't determine the host value, which probably means
// it's not suitable for further comparison
continue;
for denier in &handles.config.url_denylist {
// note to self: it may seem as though we can use regex only for --dont-scan, however, in
// doing so, we lose the ability to block a parent directory while scanning a child
if let Ok(should_deny) = should_deny_absolute(&normed_url, denier, handles.clone()) {
if should_deny {
return Ok(true);
}
}
}
let normed_host = normed_url.host().unwrap(); // match above will catch errors
// at this point, we have a matching set of ips or domain names. now we can process the
// url path. The goal is to determine whether the given url's path is a subpath of any
// url in the deny list, for example
// GIVEN URL URL DENY LIST USER-SPECIFIED URLS TO SCAN
// http://some.domain/stuff/things, [http://some.domain/stuff], [http://some.domain] => true
// http://some.domain/stuff/things, [http://some.domain/stuff/things], [http://some.domain] => true
// http://some.domain/stuff/things, [http://some.domain/api], [http://some.domain] => false
// the examples above are all pretty obvious, the kicker comes when the blocking url's
// path is a parent to a scanned url
// http://some.domain/stuff/things, [http://some.domain/], [http://some.domain/stuff] => false
// http://some.domain/api, [http://some.domain/], [http://some.domain/stuff] => true
// we want to deny all children of the parent, unless that child is a child of a scan
// we specified through -u(s) or --stdin
let deny_path = denier.path();
let norm_path = normed_url.path();
if norm_path.starts_with(deny_path) {
// at this point, we know that the given normalized path is a sub-path of the
// current deny-url, now we just need to check to see if this deny-url is a parent
// to a scanned url that is also a parent of the given url
for ferox_scan in handles.ferox_scans()?.get_active_scans() {
let scanner = Url::parse(ferox_scan.url().trim_end_matches('/'))
.with_context(|| format!("Could not parse {} as a url", ferox_scan))?;
if let Some(scan_host) = scanner.host() {
// same domain/ip check we perform on the denier above
if normed_host != scan_host {
// domains don't even match, keep on keepin' on...
continue;
}
} else {
// couldn't process .host from scanner
continue;
};
let scan_path = scanner.path();
if scan_path.starts_with(deny_path) && norm_path.starts_with(scan_path) {
// user-specified scan url is a sub-path of the deny-urls's path AND the
// url to check is a sub-path of the user-specified scan url
//
// the assumption is the user knew what they wanted and we're going to give
// the scanned url precedence, even though it's a sub-path
log::trace!("exit: should_deny_url -> false");
return Ok(false);
}
}
log::trace!("exit: should_deny_url -> true");
for denier in &handles.config.regex_denylist {
if should_deny_regex(&normed_url, denier) {
return Ok(true);
}
}
// made it to the end of the deny lists unscathed, return false, indicating we should not deny
// this particular url
log::trace!("exit: should_deny_url -> false");
Ok(false)
}
@@ -525,7 +568,7 @@ mod tests {
scans.add_directory_scan(scan_url, ScanOrder::Initial);
let mut config = Configuration::new().unwrap();
config.url_denylist = vec![String::from(deny_url)];
config.url_denylist = vec![Url::parse(deny_url).unwrap()];
let config = Arc::new(config);
let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0);
@@ -544,7 +587,7 @@ mod tests {
scans.add_directory_scan(scan_url, ScanOrder::Initial);
let mut config = Configuration::new().unwrap();
config.url_denylist = vec![String::from(deny_url)];
config.url_denylist = vec![Url::parse(deny_url).unwrap()];
let config = Arc::new(config);
let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0);
@@ -563,7 +606,7 @@ mod tests {
scans.add_directory_scan(scan_url, ScanOrder::Initial);
let mut config = Configuration::new().unwrap();
config.url_denylist = vec![String::from(deny_url)];
config.url_denylist = vec![Url::parse(deny_url).unwrap()];
let config = Arc::new(config);
let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0);
@@ -584,7 +627,7 @@ mod tests {
scans.add_directory_scan(scan_url, ScanOrder::Initial);
let mut config = Configuration::new().unwrap();
config.url_denylist = vec![String::from(deny_url)];
config.url_denylist = vec![Url::parse(deny_url).unwrap()];
let config = Arc::new(config);
let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0);
@@ -605,7 +648,7 @@ mod tests {
scans.add_directory_scan(scan_url, ScanOrder::Initial);
let mut config = Configuration::new().unwrap();
config.url_denylist = vec![String::from(deny_url)];
config.url_denylist = vec![Url::parse(deny_url).unwrap()];
let config = Arc::new(config);
let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0);
@@ -624,7 +667,7 @@ mod tests {
scans.add_directory_scan(scan_url, ScanOrder::Initial);
let mut config = Configuration::new().unwrap();
config.url_denylist = vec![String::from(deny_url)];
config.url_denylist = vec![Url::parse(deny_url).unwrap()];
let config = Arc::new(config);
let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0);
@@ -643,7 +686,7 @@ mod tests {
scans.add_directory_scan(scan_url, ScanOrder::Initial);
let mut config = Configuration::new().unwrap();
config.url_denylist = vec![String::from(deny_url)];
config.url_denylist = vec![Url::parse(deny_url).unwrap()];
let config = Arc::new(config);
let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0);
@@ -662,7 +705,7 @@ mod tests {
scans.add_directory_scan(scan_url, ScanOrder::Initial);
let mut config = Configuration::new().unwrap();
config.url_denylist = vec![String::from(deny_url)];
config.url_denylist = vec![Url::parse(deny_url).unwrap()];
let config = Arc::new(config);
let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0);
@@ -681,11 +724,53 @@ mod tests {
scans.add_directory_scan(scan_url, ScanOrder::Initial);
let mut config = Configuration::new().unwrap();
config.url_denylist = vec![String::from(deny_url)];
config.url_denylist = vec![Url::parse(deny_url).unwrap()];
let config = Arc::new(config);
let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0);
assert!(!should_deny_url(&tested_url, handles).unwrap());
}
#[test]
/// provide a denier where the tested url is matched against a regular expression in the path
/// of the url
fn should_deny_url_blocks_urls_based_on_regex_in_path() {
let scan_url = "https://testdomain.com/";
let deny_pattern = "/deni.*";
let tested_url = Url::parse("https://testdomain.com/denied/").unwrap();
let scans = Arc::new(FeroxScans::default());
scans.add_directory_scan(scan_url, ScanOrder::Initial);
let mut config = Configuration::new().unwrap();
config.regex_denylist = vec![Regex::new(deny_pattern).unwrap()];
let config = Arc::new(config);
let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0);
assert!(should_deny_url(&tested_url, handles).unwrap());
}
#[test]
/// provide a denier where the tested url is matched against a regular expression in the scheme
/// of the url
fn should_deny_url_blocks_urls_based_on_regex_in_scheme() {
let scan_url = "https://testdomain.com/";
let deny_pattern = "http:";
let tested_http_url = Url::parse("http://testdomain.com/denied/").unwrap();
let tested_https_url = Url::parse("https://testdomain.com/denied/").unwrap();
let scans = Arc::new(FeroxScans::default());
scans.add_directory_scan(scan_url, ScanOrder::Initial);
let mut config = Configuration::new().unwrap();
config.regex_denylist = vec![Regex::new(deny_pattern).unwrap()];
let config = Arc::new(config);
let handles = Arc::new(Handles::for_testing(Some(scans), Some(config)).0);
assert!(!should_deny_url(&tested_https_url, handles.clone()).unwrap());
assert!(should_deny_url(&tested_http_url, handles).unwrap());
}
}

View File

@@ -115,7 +115,7 @@ fn banner_prints_headers() {
#[test]
/// test allows non-existent wordlist to trigger the banner printing to stderr
/// expect to see all mandatory prints + multiple dont scan entries
/// expect to see all mandatory prints + multiple dont scan url & regex entries
fn banner_prints_denied_urls() {
Command::cargo_bin("feroxbuster")
.unwrap()
@@ -123,8 +123,9 @@ fn banner_prints_denied_urls() {
.arg("http://localhost")
.arg("--dont-scan")
.arg("http://dont-scan.me")
.arg("--dont-scan")
.arg("https://also-not.me")
.arg("https:")
.arg("/deny.*")
.assert()
.success()
.stderr(
@@ -136,9 +137,12 @@ fn banner_prints_denied_urls() {
.and(predicate::str::contains("Status Codes"))
.and(predicate::str::contains("Timeout (secs)"))
.and(predicate::str::contains("User-Agent"))
.and(predicate::str::contains("Don't Scan"))
.and(predicate::str::contains("Don't Scan Url"))
.and(predicate::str::contains("Don't Scan Regex"))
.and(predicate::str::contains("http://dont-scan.me"))
.and(predicate::str::contains("https://also-not.me"))
.and(predicate::str::contains("https:"))
.and(predicate::str::contains("/deny.*"))
.and(predicate::str::contains("─┴─")),
);
}

View File

@@ -210,3 +210,73 @@ fn deny_list_works_during_recursion_with_inverted_parents() {
teardown_tmp_directory(tmp_dir);
}
#[test]
/// test that a regex that prevents the base url from being scanned results in an early exit
fn deny_list_prevents_regex_that_denies_base_url() {
let srv = MockServer::start();
let (tmp_dir, file) = setup_tmp_directory(&["LICENSE".to_string()], "wordlist").unwrap();
let mock = srv.mock(|when, then| {
when.method(GET).path("/LICENSE");
then.status(200).body("this is a test");
});
let cmd = Command::cargo_bin("feroxbuster")
.unwrap()
.arg("--url")
.arg(srv.url("/"))
.arg("--wordlist")
.arg(file.as_os_str())
.arg("--dont-scan")
.arg("/")
.unwrap();
teardown_tmp_directory(tmp_dir);
let err_msg = format!(
"Could not determine initial targets: The regex '/' matches {}/; the scan will never start",
srv.base_url()
);
cmd.assert()
.success()
.stderr(predicate::str::contains(err_msg));
assert_eq!(mock.hits(), 0);
}
#[test]
/// test that a url that prevents the base url from being scanned results in an early exit
fn deny_list_prevents_url_that_denies_base_url() {
let srv = MockServer::start();
let (tmp_dir, file) = setup_tmp_directory(&["LICENSE".to_string()], "wordlist").unwrap();
let mock = srv.mock(|when, then| {
when.method(GET).path("/LICENSE");
then.status(200).body("this is a test");
});
let cmd = Command::cargo_bin("feroxbuster")
.unwrap()
.arg("--url")
.arg(srv.url("/"))
.arg("--wordlist")
.arg(file.as_os_str())
.arg("--dont-scan")
.arg(srv.base_url())
.unwrap();
teardown_tmp_directory(tmp_dir);
let err_msg = format!(
"Could not determine initial targets: The url '{}/' matches {}/; the scan will never start",
srv.base_url(),
srv.base_url()
);
cmd.assert()
.success()
.stderr(predicate::str::contains(err_msg));
assert_eq!(mock.hits(), 0);
}