Compare commits

...

18 Commits

Author SHA1 Message Date
epi
46e1d00e41 Merge pull request #430 from epi052/429-add-orig-url-to-json-output
429 add orig url to json output
2021-12-19 13:40:11 -06:00
epi
c0bfca4dbf update dependencies 2021-12-19 13:39:30 -06:00
epi
355b50bdc1 updated menu name in main display 2021-12-19 13:12:05 -06:00
epi
db2822b2cb revised a test 2021-12-19 13:03:44 -06:00
epi
f92116c16d enum variants 2021-12-19 12:58:59 -06:00
epi
1c48f754a2 added some tests 2021-12-19 11:12:51 -06:00
epi
1a26e6a992 removed lint 2021-12-11 20:29:50 -06:00
epi
23653b0cc3 added "add scan" to interactive menu 2021-12-11 20:23:52 -06:00
epi
85c529fd48 updated dependencies 2021-12-07 06:57:51 -06:00
epi
1614780c11 appeased clippy 2021-12-07 06:31:25 -06:00
epi
74121bf9be added original url to json output; updated tests 2021-12-07 06:13:53 -06:00
epi
75810ff697 updated AC badge 2021-10-19 07:47:59 -05:00
epi
e79d51b23d Merge pull request #402 from epi052/all-contributors/add-dsaxton
docs: add dsaxton as a contributor for ideas, code
2021-10-19 07:42:44 -05:00
allcontributors[bot]
4f87aa6ab6 docs: update .all-contributorsrc [skip ci] 2021-10-19 12:42:29 +00:00
allcontributors[bot]
9922999cfb docs: update README.md [skip ci] 2021-10-19 12:42:28 +00:00
epi
a73ad768b6 Merge pull request #401 from epi052/all-contributors/add-cortantief
docs: add cortantief as a contributor for bug, code
2021-10-19 07:40:58 -05:00
allcontributors[bot]
8e22a0881f docs: update .all-contributorsrc [skip ci] 2021-10-19 12:40:38 +00:00
allcontributors[bot]
d1fc7a4969 docs: update README.md [skip ci] 2021-10-19 12:40:37 +00:00
26 changed files with 422 additions and 253 deletions

View File

@@ -272,6 +272,26 @@
"contributions": [
"bug"
]
},
{
"login": "cortantief",
"name": "cortantief",
"avatar_url": "https://avatars.githubusercontent.com/u/34527333?v=4",
"profile": "https://github.com/cortantief",
"contributions": [
"bug",
"code"
]
},
{
"login": "dsaxton",
"name": "Daniel Saxton",
"avatar_url": "https://avatars.githubusercontent.com/u/2658661?v=4",
"profile": "https://github.com/dsaxton",
"contributions": [
"ideas",
"code"
]
}
],
"contributorsPerLine": 7,

274
Cargo.lock generated
View File

@@ -13,18 +13,18 @@ dependencies = [
[[package]]
name = "ansi_term"
version = "0.11.0"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.44"
version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1"
checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
[[package]]
name = "ascii-canvas"
@@ -148,9 +148,9 @@ dependencies = [
[[package]]
name = "async-process"
version = "1.2.0"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b21b63ab5a0db0369deb913540af2892750e42d949faacc7a61495ac418a1692"
checksum = "83137067e3a2a6a06d67168e49e68a0957d215410473a740cea95a2425c0b7c6"
dependencies = [
"async-io",
"blocking",
@@ -199,9 +199,9 @@ checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0"
[[package]]
name = "async-trait"
version = "0.1.51"
version = "0.1.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e"
checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3"
dependencies = [
"proc-macro2",
"quote",
@@ -271,9 +271,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "blocking"
version = "1.0.2"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9"
checksum = "046e47d4b2d391b1f6f8b407b1deb8dee56c1852ccd868becf2710f601b5f427"
dependencies = [
"async-channel",
"async-task",
@@ -296,9 +296,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.7.1"
version = "3.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538"
checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
[[package]]
name = "bytes"
@@ -308,21 +308,21 @@ checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
name = "cache-padded"
version = "1.1.1"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba"
checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
[[package]]
name = "castaway"
version = "0.1.1"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed247d1586918e46f2bbe0f13b06498db8dab5a8c1093f156652e9f2e0a73fc3"
checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6"
[[package]]
name = "cc"
version = "1.0.71"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
[[package]]
name = "cfg-if"
@@ -332,9 +332,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.33.3"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
@@ -448,9 +448,9 @@ dependencies = [
[[package]]
name = "curl"
version = "0.4.39"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaa3b8db7f3341ddef15786d250106334d4a6c4b0ae4a46cd77082777d9849b9"
checksum = "1bc6d233563261f8db6ffb83bbaad5a73837a6e6b28868e926337ebbdece0be3"
dependencies = [
"curl-sys",
"libc",
@@ -463,9 +463,9 @@ dependencies = [
[[package]]
name = "curl-sys"
version = "0.4.49+curl-7.79.1"
version = "0.4.51+curl-7.80.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0f44960aea24a786a46907b8824ebc0e66ca06bf4e4978408c7499620343483"
checksum = "d130987e6a6a34fe0889e1083022fa48cd90e6709a84be3fb8dd95801de5af20"
dependencies = [
"cc",
"libc",
@@ -565,9 +565,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "encoding_rs"
version = "0.8.28"
version = "0.8.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065"
checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df"
dependencies = [
"cfg-if",
]
@@ -593,16 +593,16 @@ checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59"
[[package]]
name = "fastrand"
version = "1.5.0"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e"
checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2"
dependencies = [
"instant",
]
[[package]]
name = "feroxbuster"
version = "2.4.0"
version = "2.4.1"
dependencies = [
"anyhow",
"assert_cmd",
@@ -683,9 +683,9 @@ dependencies = [
[[package]]
name = "futures"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca"
checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4"
dependencies = [
"futures-channel",
"futures-core",
@@ -698,9 +698,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888"
checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b"
dependencies = [
"futures-core",
"futures-sink",
@@ -708,15 +708,15 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d"
checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7"
[[package]]
name = "futures-executor"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c"
checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a"
dependencies = [
"futures-core",
"futures-task",
@@ -725,9 +725,9 @@ dependencies = [
[[package]]
name = "futures-io"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377"
checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2"
[[package]]
name = "futures-lite"
@@ -746,12 +746,10 @@ dependencies = [
[[package]]
name = "futures-macro"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb"
checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c"
dependencies = [
"autocfg",
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
@@ -759,23 +757,22 @@ dependencies = [
[[package]]
name = "futures-sink"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11"
checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508"
[[package]]
name = "futures-task"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99"
checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72"
[[package]]
name = "futures-util"
version = "0.3.17"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481"
checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164"
dependencies = [
"autocfg",
"futures-channel",
"futures-core",
"futures-io",
@@ -785,8 +782,6 @@ dependencies = [
"memchr",
"pin-project-lite",
"pin-utils",
"proc-macro-hack",
"proc-macro-nested",
"slab",
]
@@ -809,9 +804,9 @@ dependencies = [
[[package]]
name = "gloo-timers"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f"
checksum = "6f16c88aa13d2656ef20d1c042086b8767bbe2bdb62526894275a1b062161b2e"
dependencies = [
"futures-channel",
"futures-core",
@@ -822,9 +817,9 @@ dependencies = [
[[package]]
name = "h2"
version = "0.3.6"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c06815895acec637cd6ed6e9662c935b866d20a106f8361892893a7d9234964"
checksum = "8f072413d126e57991455e0a922b31e4c8ba7c2ffbebf6b78b4f8521397d65cd"
dependencies = [
"bytes",
"fnv",
@@ -862,14 +857,14 @@ checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b"
dependencies = [
"bytes",
"fnv",
"itoa",
"itoa 0.4.8",
]
[[package]]
name = "http-body"
version = "0.4.3"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "399c583b2979440c60be0821a6199eca73bc3c8dcd9d070d75ac726e2c6186e5"
checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6"
dependencies = [
"bytes",
"http",
@@ -884,15 +879,15 @@ checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503"
[[package]]
name = "httpdate"
version = "1.0.1"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440"
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "httpmock"
version = "0.6.2"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "218c2c3dabf4497c87d042b43984a5fd03bcd8a6adb0056ade7df20f88df225a"
checksum = "67fc2a6377230dc7cc007c74c34665f92589b4a73ed503f1c91ede8de6df35f0"
dependencies = [
"assert-json-diff",
"async-object-pool",
@@ -924,9 +919,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hyper"
version = "0.14.13"
version = "0.14.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15d1cfb9e4f68655fa04c01f59edb405b6074a0f7118ea881e5026e4a1cd8593"
checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55"
dependencies = [
"bytes",
"futures-channel",
@@ -937,7 +932,7 @@ dependencies = [
"http-body",
"httparse",
"httpdate",
"itoa",
"itoa 0.4.8",
"pin-project-lite",
"socket2",
"tokio",
@@ -994,9 +989,9 @@ dependencies = [
[[package]]
name = "instant"
version = "0.1.11"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
@@ -1009,9 +1004,9 @@ checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9"
[[package]]
name = "isahc"
version = "1.5.1"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ef5402b1791c9fc479ef9871601a2f10e4cc0f14414a5c9c6e043fb51e5a56"
checksum = "d140e84730d325378912ede32d7cd53ef1542725503b3353e5ec8113c7c6f588"
dependencies = [
"async-channel",
"castaway",
@@ -1036,9 +1031,9 @@ dependencies = [
[[package]]
name = "itertools"
version = "0.10.1"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
dependencies = [
"either",
]
@@ -1049,6 +1044,12 @@ version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "itoa"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "js-sys"
version = "0.3.55"
@@ -1127,9 +1128,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760"
[[package]]
name = "libc"
version = "0.2.103"
version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
[[package]]
name = "libnghttp2-sys"
@@ -1186,9 +1187,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "memoffset"
version = "0.6.4"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
@@ -1201,9 +1202,9 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "mio"
version = "0.7.13"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16"
checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
dependencies = [
"libc",
"log",
@@ -1247,9 +1248,9 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
[[package]]
name = "nix"
version = "0.23.0"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188"
checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
dependencies = [
"bitflags",
"cc",
@@ -1300,15 +1301,15 @@ checksum = "17b02fc0ff9a9e4b35b3342880f48e896ebf69f2967921fe8646bf5b7125956a"
[[package]]
name = "once_cell"
version = "1.8.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "openssl"
version = "0.10.36"
version = "0.10.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d9facdb76fec0b73c406f125d44d86fdad818d66fef0531eec9233ca425ff4a"
checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95"
dependencies = [
"bitflags",
"cfg-if",
@@ -1326,18 +1327,18 @@ checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a"
[[package]]
name = "openssl-src"
version = "111.16.0+1.1.1l"
version = "111.17.0+1.1.1m"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ab2173f69416cf3ec12debb5823d244127d23a9b127d5a5189aa97c5fa2859f"
checksum = "05d6a336abd10814198f66e2a91ccd7336611f30334119ca8ce300536666fcf4"
dependencies = [
"cc",
]
[[package]]
name = "openssl-sys"
version = "0.9.67"
version = "0.9.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69df2d8dfc6ce3aaf44b40dec6f487d5a886516cf6879c49e98e0710f310a058"
checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb"
dependencies = [
"autocfg",
"cc",
@@ -1443,15 +1444,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.20"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb"
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
[[package]]
name = "polling"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92341d779fa34ea8437ef4d82d440d5e1ce3f3ff7f824aa64424cd481f9a1f25"
checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259"
dependencies = [
"cfg-if",
"libc",
@@ -1462,9 +1463,9 @@ dependencies = [
[[package]]
name = "ppv-lite86"
version = "0.2.14"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3ca011bd0129ff4ae15cd04c4eef202cadf6c51c21e47aba319b4e0501db741"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
[[package]]
name = "precomputed-hash"
@@ -1474,9 +1475,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "predicates"
version = "2.0.3"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6ce811d0b2e103743eec01db1c50612221f173084ce2f7941053e94b6bb474"
checksum = "95e5a7689e456ab905c22c2b48225bb921aba7c8dfa58440d68ba13f6222a715"
dependencies = [
"difflib",
"float-cmp",
@@ -1502,23 +1503,11 @@ dependencies = [
"termtree",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro-nested"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
[[package]]
name = "proc-macro2"
version = "1.0.30"
version = "1.0.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70"
checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1"
dependencies = [
"unicode-xid",
]
@@ -1634,9 +1623,9 @@ dependencies = [
[[package]]
name = "reqwest"
version = "0.11.5"
version = "0.11.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51c732d463dd300362ffb44b7b125f299c23d2990411a4253824630ebc7467fb"
checksum = "07bea77bc708afa10e59905c3d4af7c8fd43c9214251673095ff8b14345fcbc5"
dependencies = [
"base64",
"bytes",
@@ -1679,15 +1668,15 @@ dependencies = [
[[package]]
name = "rustversion"
version = "1.0.5"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088"
checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
[[package]]
name = "ryu"
version = "1.0.5"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]]
name = "schannel"
@@ -1730,18 +1719,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.130"
version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
checksum = "8b9875c23cf305cd1fd7eb77234cbb705f21ea6a72c637a5c6db5fe4b8e7f008"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.130"
version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
checksum = "ecc0db5cb2556c0e558887d9bbdcf6ac4471e83ff66cf696e5419024d1606276"
dependencies = [
"proc-macro2",
"quote",
@@ -1750,11 +1739,11 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.68"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
dependencies = [
"itoa",
"itoa 1.0.1",
"ryu",
"serde",
]
@@ -1776,16 +1765,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9"
dependencies = [
"form_urlencoded",
"itoa",
"itoa 0.4.8",
"ryu",
"serde",
]
[[package]]
name = "signal-hook"
version = "0.3.10"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c98891d737e271a2954825ef19e46bd16bdb98e2746f2eec4f7a4ef7946efd1"
checksum = "c35dfd12afb7828318348b8c408383cf5071a086c1d4ab1c0f9840ec92dbb922"
dependencies = [
"libc",
"signal-hook-registry",
@@ -1871,9 +1860,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.80"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
dependencies = [
"proc-macro2",
"quote",
@@ -1926,9 +1915,9 @@ dependencies = [
[[package]]
name = "termtree"
version = "0.2.1"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78fbf2dd23e79c28ccfa2472d3e6b3b189866ffef1aeb91f17c2d968b6586378"
checksum = "13a4ec180a2de59b57434704ccfad967f789b12737738798fa08798cd5824c16"
[[package]]
name = "textwrap"
@@ -1970,9 +1959,9 @@ dependencies = [
[[package]]
name = "tinyvec"
version = "1.5.0"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7"
checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
dependencies = [
"tinyvec_macros",
]
@@ -1985,11 +1974,10 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.12.0"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2c2416fdedca8443ae44b4527de1ea633af61d8f7169ffa6e72c5b53d24efcc"
checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838"
dependencies = [
"autocfg",
"bytes",
"libc",
"memchr",
@@ -2005,9 +1993,9 @@ dependencies = [
[[package]]
name = "tokio-macros"
version = "1.5.0"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2dd85aeaba7b68df939bd357c6afb36c87951be9e80bf9c859f2fc3e9fca0fd"
checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
dependencies = [
"proc-macro2",
"quote",
@@ -2038,9 +2026,9 @@ dependencies = [
[[package]]
name = "tokio-stream"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b2f3f698253f03119ac0102beaa64f67a67e08074d03a22d18784104543727f"
checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3"
dependencies = [
"futures-core",
"pin-project-lite",
@@ -2049,9 +2037,9 @@ dependencies = [
[[package]]
name = "tokio-util"
version = "0.6.8"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d3725d3efa29485e87311c5b699de63cde14b00ed4d256b8318aa30ca452cd"
checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0"
dependencies = [
"bytes",
"futures-core",
@@ -2176,9 +2164,9 @@ dependencies = [
[[package]]
name = "value-bag"
version = "1.0.0-alpha.7"
version = "1.0.0-alpha.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd320e1520f94261153e96f7534476ad869c14022aee1e59af7c778075d840ae"
checksum = "79923f7731dc61ebfba3633098bf3ac533bbd35ccd8c57e7088d9a5eebe0263f"
dependencies = [
"ctor",
"version_check",

View File

@@ -1,6 +1,6 @@
[package]
name = "feroxbuster"
version = "2.4.0"
version = "2.4.1"
authors = ["Ben 'epi' Risher <epibar052@gmail.com>"]
license = "MIT"
edition = "2018"
@@ -23,14 +23,14 @@ dirs = "4.0"
[dependencies]
futures = { version = "0.3"}
tokio = { version = "1.11", features = ["full"] }
tokio = { version = "1.15", features = ["full"] }
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"
clap = "2.34"
lazy_static = "1.4"
toml = "0.5"
serde = { version = "1.0", features = ["derive", "rc"] }
@@ -50,9 +50,9 @@ leaky-bucket = "0.10.0"
[dev-dependencies]
tempfile = "3.1"
httpmock = "0.6.2"
httpmock = "0.6"
assert_cmd = "2.0"
predicates = "2.0"
predicates = "2.1"
[profile.release]
lto = true

View File

@@ -35,7 +35,7 @@
[![All Contributors](https://img.shields.io/badge/all_contributors-15-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
<a href="https://github.com/epi052/feroxbuster/graphs/contributors">
<img src="https://img.shields.io/badge/all_contributors-14-orange.svg" />
<img src="https://img.shields.io/badge/all_contributors-31-orange.svg" />
</a>
</p>
@@ -219,6 +219,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
</tr>
<tr>
<td align="center"><a href="https://github.com/hunter0x8"><img src="https://avatars.githubusercontent.com/u/46222314?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Muhammad Ahsan</b></sub></a><br /><a href="https://github.com/epi052/feroxbuster/issues?q=author%3Ahunter0x8" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/cortantief"><img src="https://avatars.githubusercontent.com/u/34527333?v=4?s=100" width="100px;" alt=""/><br /><sub><b>cortantief</b></sub></a><br /><a href="https://github.com/epi052/feroxbuster/issues?q=author%3Acortantief" title="Bug reports">🐛</a> <a href="https://github.com/epi052/feroxbuster/commits?author=cortantief" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/dsaxton"><img src="https://avatars.githubusercontent.com/u/2658661?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Saxton</b></sub></a><br /><a href="#ideas-dsaxton" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/epi052/feroxbuster/commits?author=dsaxton" title="Code">💻</a></td>
</tr>
</table>

View File

@@ -380,7 +380,7 @@ by Ben "epi" Risher {} ver: {}"#,
let instructions = format!(
" 🏁 Press [{}] to use the {}",
style("ENTER").yellow(),
style("Scan Cancel Menu").bright().yellow(),
style("Scan Management Menu").bright().yellow(),
);
format!("{}\n{}\n{}", bottom, instructions, addl_section)

View File

@@ -459,7 +459,7 @@ impl Configuration {
/// Parse all possible versions of the ferox-config.toml file, adhering to the order of
/// precedence outlined above
fn parse_config_files(mut config: &mut Self) -> Result<()> {
fn parse_config_files(config: &mut Self) -> Result<()> {
// Next, we parse the ferox-config.toml file, if present and set the values
// therein to overwrite our default values. Deserialized defaults are specified
// in the Configuration struct so that we don't change anything that isn't
@@ -475,7 +475,7 @@ impl Configuration {
let config_file = PathBuf::new()
.join("/etc/feroxbuster")
.join(DEFAULT_CONFIG_NAME);
Self::parse_and_merge_config(config_file, &mut config)?;
Self::parse_and_merge_config(config_file, config)?;
// merge a config found at ~/.config/feroxbuster/ferox-config.toml
// config_dir() resolves to one of the following
@@ -484,7 +484,7 @@ impl Configuration {
// - windows: {FOLDERID_RoamingAppData}
let config_dir = dirs::config_dir().ok_or_else(|| anyhow!("Couldn't load config"))?;
let config_file = config_dir.join("feroxbuster").join(DEFAULT_CONFIG_NAME);
Self::parse_and_merge_config(config_file, &mut config)?;
Self::parse_and_merge_config(config_file, config)?;
// merge a config found in same the directory as feroxbuster executable
let exe_path = current_exe()?;
@@ -492,12 +492,12 @@ impl Configuration {
.parent()
.ok_or_else(|| anyhow!("Couldn't load config"))?;
let config_file = bin_dir.join(DEFAULT_CONFIG_NAME);
Self::parse_and_merge_config(config_file, &mut config)?;
Self::parse_and_merge_config(config_file, config)?;
// merge a config found in the user's current working directory
let cwd = current_dir()?;
let config_file = cwd.join(DEFAULT_CONFIG_NAME);
Self::parse_and_merge_config(config_file, &mut config)?;
Self::parse_and_merge_config(config_file, config)?;
Ok(())
}
@@ -804,7 +804,7 @@ impl Configuration {
config.config = conf_str;
// update the settings
Self::merge_config(&mut config, settings);
Self::merge_config(config, settings);
}
Ok(())
}

View File

@@ -48,6 +48,9 @@ pub enum Command {
/// Send a group of urls to be scanned (only used for the urls passed in explicitly by the user)
ScanInitialUrls(Vec<String>),
/// Send a single url to be scanned (presumably added from the interactive menu)
ScanNewUrl(String),
/// Determine whether or not recursion is appropriate, given a FeroxResponse, if so start a scan
TryRecursion(Box<FeroxResponse>),

View File

@@ -85,13 +85,7 @@ impl Handles {
let configuration = config.unwrap_or_else(|| Arc::new(Configuration::new().unwrap()));
let (tx, rx) = mpsc::unbounded_channel::<Command>();
let terminal_handle = TermOutHandle::new(tx.clone(), tx.clone());
let stats_handle = StatsHandle::new(
Arc::new(Stats::new(
configuration.extensions.len(),
configuration.json,
)),
tx.clone(),
);
let stats_handle = StatsHandle::new(Arc::new(Stats::new(configuration.json)), tx.clone());
let filters_handle = FiltersHandle::new(Arc::new(FeroxFilters::default()), tx.clone());
let handles = Self::new(stats_handle, filters_handle, terminal_handle, configuration);
if let Some(sh) = scanned_urls {

View File

@@ -33,7 +33,7 @@ pub struct TermInputHandler {
///
/// kicks off the following handlers related to terminal input:
/// ctrl+c handler that saves scan state to disk
/// enter handler that listens for enter during scans to drop into interactive scan cancel menu
/// enter handler that listens for enter during scans to drop into interactive scan management menu
impl TermInputHandler {
/// Create new event handler
pub fn new(handles: Arc<Handles>) -> Self {

View File

@@ -146,6 +146,15 @@ impl ScanHandler {
Command::ScanInitialUrls(targets) => {
self.ordered_scan_url(targets, ScanOrder::Initial).await?;
}
Command::ScanNewUrl(target) => {
// added as part of interactive menu ability (2.4.1) to add a new scan.
// we don't have a way of knowing if they're adding a new url entirely (i.e.
// new base url), or simply adding a new sub-directory found some other way.
// Since we can't know, we'll start a scan as though we received the scan
// from -u | --stdin
self.ordered_scan_url(vec![target], ScanOrder::Initial)
.await?;
}
Command::UpdateWordlist(wordlist) => {
self.wordlist(wordlist);
}

View File

@@ -155,7 +155,7 @@ impl StatsHandler {
pub fn initialize(config: Arc<Configuration>) -> (Joiner, StatsHandle) {
log::trace!("enter: initialize");
let data = Arc::new(Stats::new(config.extensions.len(), config.json));
let data = Arc::new(Stats::new(config.json));
let (tx, rx): FeroxChannel<Command> = mpsc::unbounded_channel();
let mut handler = StatsHandler::new(data.clone(), rx);

View File

@@ -198,11 +198,11 @@ impl<'a> Extractor<'a> {
/// - homepage/assets/img/
/// - homepage/assets/
/// - homepage/
fn add_all_sub_paths(&self, url_path: &str, mut links: &mut HashSet<String>) -> Result<()> {
fn add_all_sub_paths(&self, url_path: &str, links: &mut HashSet<String>) -> Result<()> {
log::trace!("enter: add_all_sub_paths({}, {:?})", url_path, links);
for sub_path in self.get_sub_paths_from_path(url_path) {
self.add_link_to_set_of_links(&sub_path, &mut links)?;
self.add_link_to_set_of_links(&sub_path, links)?;
}
log::trace!("exit: add_all_sub_paths");
@@ -327,7 +327,7 @@ impl<'a> Extractor<'a> {
let new_response = logged_request(&new_url, self.handles.clone()).await?;
let new_ferox_response =
FeroxResponse::from(new_response, true, self.handles.config.output_level).await;
FeroxResponse::from(new_response, url, true, self.handles.config.output_level).await;
log::trace!("exit: request_link -> {:?}", new_ferox_response);
@@ -410,7 +410,7 @@ impl<'a> Extractor<'a> {
.await?;
let ferox_response =
FeroxResponse::from(response, true, self.handles.config.output_level).await;
FeroxResponse::from(response, &self.url, true, self.handles.config.output_level).await;
log::trace!("exit: get_robots_file -> {}", ferox_response);
Ok(ferox_response)

View File

@@ -231,7 +231,8 @@ async fn extractor_get_links_with_absolute_url_that_differs_from_target_domain()
let (handles, _rx) = Handles::for_testing(None, None);
let handles = Arc::new(handles);
let ferox_response = FeroxResponse::from(response, true, OutputLevel::Default).await;
let ferox_response =
FeroxResponse::from(response, &srv.url(""), true, OutputLevel::Default).await;
let extractor = Extractor {
links_regex: Regex::new(LINKFINDER_REGEX).unwrap(),

View File

@@ -75,7 +75,8 @@ pub async fn initialize(handles: Arc<Handles>) -> Result<()> {
let resp = skip_fail!(logged_request(&url, handles.clone()).await);
// if successful, create a filter based on the response's body
let fr = FeroxResponse::from(resp, true, handles.config.output_level).await;
let fr =
FeroxResponse::from(resp, similarity_filter, true, handles.config.output_level).await;
// hash the response body and store the resulting hash in the filter object
let hash = FuzzyHash::new(&fr.text()).to_string();

View File

@@ -175,8 +175,13 @@ impl HeuristicTests {
.contains(&response.status().as_u16())
{
// found a wildcard response
let mut ferox_response =
FeroxResponse::from(response, true, self.handles.config.output_level).await;
let mut ferox_response = FeroxResponse::from(
response,
&target.target,
true,
self.handles.config.output_level,
)
.await;
ferox_response.set_wildcard(true);
if self

View File

@@ -30,6 +30,9 @@ pub struct FeroxResponse {
/// The final `Url` of this `FeroxResponse`
url: Url,
/// The original url from which the final `Url` was derived
original_url: String,
/// The `StatusCode` of this `FeroxResponse`
status: StatusCode,
@@ -61,6 +64,7 @@ impl Default for FeroxResponse {
fn default() -> Self {
Self {
url: Url::parse("http://localhost").unwrap(),
original_url: "".to_string(),
status: Default::default(),
text: "".to_string(),
content_length: 0,
@@ -186,7 +190,12 @@ impl FeroxResponse {
}
/// Create a new `FeroxResponse` from the given `Response`
pub async fn from(response: Response, read_body: bool, output_level: OutputLevel) -> Self {
pub async fn from(
response: Response,
original_url: &str,
read_body: bool,
output_level: OutputLevel,
) -> Self {
let url = response.url().clone();
let status = response.status();
let headers = response.headers().clone();
@@ -213,6 +222,7 @@ impl FeroxResponse {
FeroxResponse {
url,
original_url: original_url.to_string(),
status,
content_length,
text,
@@ -423,7 +433,7 @@ impl Serialize for FeroxResponse {
S: Serializer,
{
let mut headers = HashMap::new();
let mut state = serializer.serialize_struct("FeroxResponse", 7)?;
let mut state = serializer.serialize_struct("FeroxResponse", 8)?;
// need to convert the HeaderMap to a HashMap in order to pass it to the serializer
for (key, value) in &self.headers {
@@ -434,6 +444,7 @@ impl Serialize for FeroxResponse {
state.serialize_field("type", "response")?;
state.serialize_field("url", self.url.as_str())?;
state.serialize_field("original_url", self.original_url.as_str())?;
state.serialize_field("path", self.url.path())?;
state.serialize_field("wildcard", &self.wildcard)?;
state.serialize_field("status", &self.status.as_u16())?;
@@ -455,6 +466,7 @@ impl<'de> Deserialize<'de> for FeroxResponse {
{
let mut response = Self {
url: Url::parse("http://localhost").unwrap(),
original_url: String::new(),
status: StatusCode::OK,
text: String::new(),
content_length: 0,
@@ -476,6 +488,11 @@ impl<'de> Deserialize<'de> for FeroxResponse {
}
}
}
"original_url" => {
if let Some(og_url) = value.as_str() {
response.original_url = String::from(og_url);
}
}
"status" => {
if let Some(num) = value.as_u64() {
if let Ok(smaller) = u16::try_from(num) {
@@ -540,6 +557,7 @@ mod tests {
let url = Url::parse("http://localhost").unwrap();
let response = FeroxResponse {
url,
original_url: String::new(),
status: Default::default(),
text: "".to_string(),
content_length: 0,
@@ -561,6 +579,7 @@ mod tests {
let url = Url::parse("http://localhost/one/two").unwrap();
let response = FeroxResponse {
url,
original_url: String::new(),
status: Default::default(),
text: "".to_string(),
content_length: 0,
@@ -582,6 +601,7 @@ mod tests {
let url = Url::parse("http://localhost").unwrap();
let response = FeroxResponse {
url,
original_url: String::new(),
status: Default::default(),
text: "".to_string(),
content_length: 0,
@@ -603,6 +623,7 @@ mod tests {
let url = Url::parse("http://localhost/one/two").unwrap();
let response = FeroxResponse {
url,
original_url: String::new(),
status: Default::default(),
text: "".to_string(),
content_length: 0,
@@ -624,6 +645,7 @@ mod tests {
let url = Url::parse("http://localhost/one/two/three").unwrap();
let response = FeroxResponse {
url,
original_url: String::new(),
status: Default::default(),
text: "".to_string(),
content_length: 0,

View File

@@ -1,27 +1,39 @@
use crate::progress::PROGRESS_BAR;
use console::{measure_text_width, pad_str, style, Alignment, Term};
use indicatif::ProgressDrawTarget;
use regex::Regex;
/// Data container for a command entered by the user interactively
#[derive(Debug)]
pub enum MenuCmd {
/// user wants to add a url to be scanned
Add(String),
/// user wants to cancel one or more active scans
Cancel(Vec<usize>, bool),
}
/// Data container for a command result to be used internally by the ferox_scanner
#[derive(Debug)]
pub enum MenuCmdResult {
/// Url to be added to the scan queue
Url(String),
/// Number of scans that were actually cancelled, can be 0
NumCancelled(usize),
}
/// Interactive scan cancellation menu
#[derive(Debug)]
pub(super) struct Menu {
/// character to use as visual separator of lines
separator: String,
/// name of menu
name: String,
/// header: name surrounded by separators
header: String,
/// instructions
instructions: String,
/// footer: instructions surrounded by separators
footer: String,
/// target for output
term: Term,
pub(super) term: Term,
}
/// Implementation of Menu
@@ -30,42 +42,43 @@ impl Menu {
pub(super) fn new() -> Self {
let separator = "".to_string();
let instructions = format!(
"Enter a {} list of indexes/ranges to {} ({}: 1-4,8,9-13)",
style("comma-separated").yellow(),
style("cancel").red(),
style("ex").cyan(),
);
let name = format!(
"{} {} {}",
"💀",
style("Scan Cancel Menu").bright().yellow(),
style("Scan Management Menu").bright().yellow(),
"💀"
);
let force_msg = format!(
"Add {} to {} confirmation ({}: 3-5 -f)",
style("-f").yellow(),
style("skip").yellow(),
style("ex").cyan(),
let add_cmd = format!(
" {}[{}] NEW_URL (ex: {} http://localhost)\n",
style("a").green(),
style("dd").green(),
style("add").green()
);
let longest = measure_text_width(&instructions).max(measure_text_width(&name));
let canx_cmd = format!(
" {}[{}] [-f] SCAN_ID[-SCAN_ID[,...]] (ex: {} 1-4,8,9-13 or {} -f 3)",
style("c").red(),
style("ancel").red(),
style("cancel").red(),
style("c").red(),
);
let mut commands = String::from("Commands:\n");
commands.push_str(&add_cmd);
commands.push_str(&canx_cmd);
let longest = measure_text_width(&canx_cmd).max(measure_text_width(&name));
let border = separator.repeat(longest);
let padded_name = pad_str(&name, longest, Alignment::Center, None);
let padded_force = pad_str(&force_msg, longest, Alignment::Center, None);
let header = format!("{}\n{}\n{}", border, padded_name, border);
let footer = format!("{}\n{}\n{}\n{}", border, instructions, padded_force, border);
let footer = format!("{}\n{}\n{}", border, commands, border);
Self {
separator,
name,
header,
instructions,
footer,
term: Term::stderr(),
}
@@ -161,14 +174,36 @@ impl Menu {
nums
}
/// get comma-separated list of scan indexes from the user
pub(super) fn get_scans_from_user(&self) -> Option<(Vec<usize>, bool)> {
if let Ok(line) = self.term.read_line() {
let force = line.contains("-f");
let line = line.replace("-f", "");
Some((self.split_to_nums(&line), force))
} else {
None
/// get input from the user and translate it to a `MenuCmd`
pub(super) fn get_command_input_from_user(&self, line: &str) -> Option<MenuCmd> {
let line = line.trim(); // normalize input if there are leading spaces
match line.chars().next().unwrap_or('_').to_ascii_lowercase() {
'c' => {
// cancel command; start by determining if -f was used
let force = line.contains("-f");
// then remove c[ancel] from the command so it can be passed to the number
// splitter
let re = Regex::new(r"^[cC][ancelANCEL]*").unwrap();
let line = line.replace("-f", "");
let line = re.replace(&line, "").to_string();
Some(MenuCmd::Cancel(self.split_to_nums(&line), force))
}
'a' => {
// add command
// similar to cancel, we need to remove the a[dd] substring, the rest should be
// a url
let re = Regex::new(r"^[aA][dD]*").unwrap();
let line = re.replace(line, "").to_string().trim().to_string();
Some(MenuCmd::Add(line))
}
_ => {
// invalid input
None
}
}
}

View File

@@ -9,6 +9,7 @@ mod state;
mod tests;
pub(self) use menu::Menu;
pub use menu::{MenuCmd, MenuCmdResult};
pub use order::ScanOrder;
pub use response_container::FeroxResponses;
pub use scan::{FeroxScan, ScanStatus, ScanType};

View File

@@ -4,6 +4,7 @@ use crate::{
config::OutputLevel,
progress::PROGRESS_PRINTER,
progress::{add_bar, BarType},
scan_manager::{MenuCmd, MenuCmdResult},
scanner::RESPONSES,
traits::FeroxSerialize,
SLEEP_DURATION,
@@ -300,23 +301,33 @@ impl FeroxScans {
}
/// CLI menu that allows for interactive cancellation of recursed-into directories
async fn interactive_menu(&self) -> usize {
async fn interactive_menu(&self) -> Option<MenuCmdResult> {
self.menu.hide_progress_bars();
self.menu.clear_screen();
self.menu.print_header();
self.display_scans().await;
self.menu.print_footer();
let mut num_cancelled = 0_usize;
let menu_cmd = if let Ok(line) = self.menu.term.read_line() {
self.menu.get_command_input_from_user(&line)
} else {
None
};
if let Some((input, force)) = self.menu.get_scans_from_user() {
num_cancelled += self.cancel_scans(input, force).await;
let result = match menu_cmd {
Some(MenuCmd::Cancel(indices, should_force)) => {
// cancel the things
let num_cancelled = self.cancel_scans(indices, should_force).await;
Some(MenuCmdResult::NumCancelled(num_cancelled))
}
Some(MenuCmd::Add(url)) => Some(MenuCmdResult::Url(url)),
None => None,
};
self.menu.clear_screen();
self.menu.show_progress_bars();
num_cancelled
result
}
/// prints all known responses that the scanner has already seen
@@ -364,19 +375,19 @@ impl FeroxScans {
///
/// When the value stored in `PAUSE_SCAN` becomes `false`, the function returns, exiting the busy
/// loop
pub async fn pause(&self, get_user_input: bool) -> usize {
pub async fn pause(&self, get_user_input: bool) -> Option<MenuCmdResult> {
// function uses tokio::time, not std
// local testing showed a pretty slow increase (less than linear) in CPU usage as # of
// concurrent scans rose when SLEEP_DURATION was set to 500, using that as the default for now
let mut interval = time::interval(time::Duration::from_millis(SLEEP_DURATION));
let mut num_cancelled = 0_usize;
let mut command_result = None;
if INTERACTIVE_BARRIER.load(Ordering::Relaxed) == 0 {
INTERACTIVE_BARRIER.fetch_add(1, Ordering::Relaxed);
if get_user_input {
num_cancelled += self.interactive_menu().await;
command_result = self.interactive_menu().await;
PAUSE_SCAN.store(false, Ordering::Relaxed);
self.print_known_responses();
}
@@ -393,8 +404,8 @@ impl FeroxScans {
INTERACTIVE_BARRIER.fetch_sub(1, Ordering::Relaxed);
}
log::trace!("exit: pause_scan -> {}", num_cancelled);
return num_cancelled;
log::trace!("exit: pause_scan -> {:?}", command_result);
return command_result;
}
}
}

View File

@@ -303,7 +303,7 @@ fn ferox_scans_serialize() {
#[test]
/// given a FeroxResponses, test that it serializes into the proper JSON entry
fn ferox_responses_serialize() {
let json_response = r#"{"type":"response","url":"https://nerdcore.com/css","path":"/css","wildcard":true,"status":301,"content_length":173,"line_count":10,"word_count":16,"headers":{"server":"nginx/1.16.1"}}"#;
let json_response = r#"{"type":"response","url":"https://nerdcore.com/css","original_url":"https://nerdcore.com","path":"/css","wildcard":true,"status":301,"content_length":173,"line_count":10,"word_count":16,"headers":{"server":"nginx/1.16.1"}}"#;
let response: FeroxResponse = serde_json::from_str(json_response).unwrap();
let responses = FeroxResponses::default();
@@ -321,7 +321,7 @@ fn ferox_responses_serialize() {
/// given a FeroxResponse, test that it serializes into the proper JSON entry
fn ferox_response_serialize_and_deserialize() {
// deserialize
let json_response = r#"{"type":"response","url":"https://nerdcore.com/css","path":"/css","wildcard":true,"status":301,"content_length":173,"line_count":10,"word_count":16,"headers":{"server":"nginx/1.16.1"}}"#;
let json_response = r#"{"type":"response","url":"https://nerdcore.com/css","original_url":"https://nerdcore.com","path":"/css","wildcard":true,"status":301,"content_length":173,"line_count":10,"word_count":16,"headers":{"server":"nginx/1.16.1"}}"#;
let response: FeroxResponse = serde_json::from_str(json_response).unwrap();
assert_eq!(response.url().as_str(), "https://nerdcore.com/css");
@@ -354,7 +354,7 @@ fn feroxstates_feroxserialize_implementation() {
ferox_scans.insert(ferox_scan);
let config = Configuration::new().unwrap();
let stats = Arc::new(Stats::new(config.extensions.len(), config.json));
let stats = Arc::new(Stats::new(config.json));
let json_response = r#"{"type":"response","url":"https://nerdcore.com/css","path":"/css","wildcard":true,"status":301,"content_length":173,"line_count":10,"word_count":16,"headers":{"server":"nginx/1.16.1"}}"#;
let response: FeroxResponse = serde_json::from_str(json_response).unwrap();
@@ -572,6 +572,14 @@ async fn ferox_scan_abort() {
/// and their correctness can be verified easily manually; just calling for now
fn menu_print_header_and_footer() {
let menu = Menu::new();
let menu_cmd_1 = MenuCmd::Add(String::from("http://localhost"));
let menu_cmd_2 = MenuCmd::Cancel(vec![0], false);
let menu_cmd_res_1 = MenuCmdResult::Url(String::from("http://localhost"));
let menu_cmd_res_2 = MenuCmdResult::NumCancelled(2);
println!(
"{:?}{:?}{:?}{:?}",
menu_cmd_1, menu_cmd_2, menu_cmd_res_1, menu_cmd_res_2
);
menu.clear_screen();
menu.print_header();
menu.print_footer();
@@ -579,6 +587,57 @@ fn menu_print_header_and_footer() {
menu.show_progress_bars();
}
#[test]
/// ensure command parsing from user input results int he correct MenuCmd returned
fn menu_get_command_input_from_user_returns_cancel() {
let menu = Menu::new();
for (idx, cmd) in ["cancel", "Cancel", "c", "C"].iter().enumerate() {
let force = idx % 2 == 0;
let full_cmd = if force {
format!("{} -f {}\n", cmd, idx)
} else {
format!("{} {}\n", cmd, idx)
};
let result = menu.get_command_input_from_user(&full_cmd).unwrap();
assert!(matches!(result, MenuCmd::Cancel(_, _)));
if let MenuCmd::Cancel(canx_list, ret_force) = result {
if idx == 0 {
assert!(canx_list.is_empty());
} else {
assert_eq!(canx_list, vec![idx]);
}
assert_eq!(force, ret_force);
}
}
}
#[test]
/// ensure command parsing from user input results int he correct MenuCmd returned
fn menu_get_command_input_from_user_returns_add() {
let menu = Menu::new();
for cmd in ["add", "Addd", "a", "A", "None"] {
let test_url = "http://happyfuntimes.commmm";
let full_cmd = format!("{} {}\n", cmd, test_url);
if cmd != "None" {
let result = menu.get_command_input_from_user(&full_cmd).unwrap();
assert!(matches!(result, MenuCmd::Add(_)));
if let MenuCmd::Add(url) = result {
assert_eq!(url, test_url);
}
} else {
assert!(menu.get_command_input_from_user(&full_cmd).is_none());
};
}
}
#[test]
/// ensure spaces are trimmed and numbers are returned from split_to_nums
fn split_to_nums_is_correct() {

View File

@@ -12,12 +12,13 @@ use crate::{
},
extractor::{ExtractionTarget::RobotsTxt, ExtractorBuilder},
heuristics,
scan_manager::{FeroxResponses, ScanOrder, ScanStatus, PAUSE_SCAN},
scan_manager::{FeroxResponses, MenuCmdResult, ScanOrder, ScanStatus, PAUSE_SCAN},
statistics::{
StatError::Other,
StatField::{DirScanTimes, TotalExpected},
},
utils::fmt_err,
Command,
};
use super::requester::Requester;
@@ -137,14 +138,30 @@ impl FeroxScanner {
// for every word in the wordlist, check to see if PAUSE_SCAN is set to true
// when true; enter a busy loop that only exits by setting PAUSE_SCAN back
// to false
let num_cancelled = scanned_urls_clone.pause(true).await;
if num_cancelled > 0 {
handles_clone
.stats
.send(SubtractFromUsizeField(TotalExpected, num_cancelled))
.unwrap_or_else(|e| {
log::warn!("Could not update overall scan bar: {}", e)
});
match scanned_urls_clone.pause(true).await {
Some(MenuCmdResult::Url(url)) => {
// user wants to add a new url to be scanned, need to send
// it over to the event handler for processing
handles_clone
.send_scan_command(Command::ScanNewUrl(url))
.unwrap_or_else(|e| {
log::warn!("Could not add scan to scan queue: {}", e)
})
}
Some(MenuCmdResult::NumCancelled(num_canx)) => {
if num_canx > 0 {
handles_clone
.stats
.send(SubtractFromUsizeField(TotalExpected, num_canx))
.unwrap_or_else(|e| {
log::warn!(
"Could not update overall scan bar: {}",
e
)
});
}
}
_ => {}
}
}
requester_clone

View File

@@ -353,8 +353,13 @@ impl Requester {
}
// response came back without error, convert it to FeroxResponse
let ferox_response =
FeroxResponse::from(response, true, self.handles.config.output_level).await;
let ferox_response = FeroxResponse::from(
response,
&self.target_url,
true,
self.handles.config.output_level,
)
.await;
// do recursion if appropriate
if !self.handles.config.no_recursion {

View File

@@ -126,9 +126,6 @@ pub struct Stats {
/// tracker for total runtime
total_runtime: Mutex<Vec<f64>>,
/// tracker for the number of extensions the user specified
num_extensions: usize,
/// tracker for whether to use json during serialization or not
json: bool,
}
@@ -203,7 +200,7 @@ impl<'a> Deserialize<'a> for Stats {
where
D: Deserializer<'a>,
{
let stats = Self::new(0, false);
let stats = Self::new(false);
let map: HashMap<String, Value> = HashMap::deserialize(deserializer)?;
@@ -446,9 +443,8 @@ impl<'a> Deserialize<'a> for Stats {
impl Stats {
/// Small wrapper for default to set `kind` to "statistics" and `total_runtime` to have at least
/// one value
pub fn new(num_extensions: usize, is_json: bool) -> Self {
pub fn new(is_json: bool) -> Self {
Self {
num_extensions,
json: is_json,
kind: String::from("statistics"),
total_runtime: Mutex::new(vec![0.0]),
@@ -790,7 +786,7 @@ mod tests {
/// - errors
fn stats_increments_timeouts() {
let config = Configuration::new().unwrap();
let stats = Stats::new(config.extensions.len(), config.json);
let stats = Stats::new(config.json);
stats.add_error(StatError::Timeout);
stats.add_error(StatError::Timeout);
@@ -808,7 +804,7 @@ mod tests {
/// - responses_filtered
fn stats_increments_wildcards() {
let config = Configuration::new().unwrap();
let stats = Stats::new(config.extensions.len(), config.json);
let stats = Stats::new(config.json);
assert_eq!(stats.responses_filtered.load(Ordering::Relaxed), 0);
assert_eq!(stats.wildcards_filtered.load(Ordering::Relaxed), 0);
@@ -824,7 +820,7 @@ mod tests {
/// when Stats::update_usize_field receives StatField::ResponsesFiltered, it should increment
fn stats_increments_responses_filtered() {
let config = Configuration::new().unwrap();
let stats = Stats::new(config.extensions.len(), config.json);
let stats = Stats::new(config.json);
assert_eq!(stats.responses_filtered.load(Ordering::Relaxed), 0);
@@ -840,7 +836,7 @@ mod tests {
fn stats_merge_from_alters_correct_fields() {
let contents = r#"{"statistics":{"type":"statistics","timeouts":1,"requests":9207,"expected_per_scan":707,"total_expected":9191,"errors":3,"successes":720,"redirects":13,"client_errors":8474,"server_errors":2,"total_scans":13,"initial_targets":1,"links_extracted":51,"status_403s":3,"status_200s":720,"status_301s":12,"status_302s":1,"status_401s":4,"status_429s":2,"status_500s":5,"status_503s":9,"status_504s":6,"status_508s":7,"wildcards_filtered":707,"responses_filtered":707,"resources_discovered":27,"directory_scan_times":[2.211973078,1.989015505,1.898675839,3.9714468910000003,4.938152838,5.256073528,6.021986595,6.065740734,6.42633762,7.095142125,7.336982137,5.319785619,4.843649778],"total_runtime":[11.556575456000001],"url_format_errors":17,"redirection_errors":12,"connection_errors":21,"request_errors":4}}"#;
let config = Configuration::new().unwrap();
let stats = Stats::new(config.extensions.len(), config.json);
let stats = Stats::new(config.json);
let tfile = NamedTempFile::new().unwrap();
write(&tfile, contents).unwrap();
@@ -891,7 +887,7 @@ mod tests {
/// ensure update runtime overwrites the default 0th entry
fn update_runtime_works() {
let config = Configuration::new().unwrap();
let stats = Stats::new(config.extensions.len(), config.json);
let stats = Stats::new(config.json);
assert!((stats.total_runtime.lock().unwrap()[0] - 0.0).abs() < f64::EPSILON);
stats.update_runtime(20.2);
@@ -902,7 +898,7 @@ mod tests {
/// ensure status_403s returns the correct value
fn status_403s_returns_correct_value() {
let config = Configuration::new().unwrap();
let stats = Stats::new(config.extensions.len(), config.json);
let stats = Stats::new(config.json);
stats.status_403s.store(12, Ordering::Relaxed);
assert_eq!(stats.status_403s(), 12);
}
@@ -911,7 +907,7 @@ mod tests {
/// ensure status_403s returns the correct value
fn status_429s_returns_correct_value() {
let config = Configuration::new().unwrap();
let stats = Stats::new(config.extensions.len(), config.json);
let stats = Stats::new(config.json);
stats.status_429s.store(141, Ordering::Relaxed);
assert_eq!(stats.status_429s(), 141);
}

View File

@@ -18,10 +18,10 @@ macro_rules! atomic_increment {
#[macro_export]
macro_rules! atomic_load {
($metric:expr) => {
$metric.load(Ordering::Relaxed);
$metric.load(Ordering::Relaxed)
};
($metric:expr, $ordering:expr) => {
$metric.load($ordering);
$metric.load($ordering)
};
}

View File

@@ -41,7 +41,7 @@ async fn statistics_handler_exits() -> Result<()> {
/// Stats::save should write contents of Stats to disk
fn save_writes_stats_object_to_disk() {
let config = Configuration::new().unwrap();
let stats = Stats::new(config.extensions.len(), config.json);
let stats = Stats::new(config.json);
stats.add_request();
stats.add_request();

View File

@@ -7,7 +7,7 @@ use std::{convert::TryInto, fmt, sync::Arc};
#[derive(Debug)]
pub struct FeroxUrl {
/// string representation of the target url
target: String,
pub target: String,
/// Handles object for grabbing config values
handles: Arc<Handles>,