mirror of
https://github.com/bee-san/RustScan.git
synced 2026-11-07 09:46:04 -10:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b46f982b9b | ||
|
|
521f8bf720 | ||
|
|
ab1e69f1af | ||
|
|
29d85a77df | ||
|
|
a79a471111 | ||
|
|
f4192fd367 | ||
|
|
e9a4972e1f | ||
|
|
2f81acf45b | ||
|
|
8f8a51adf2 | ||
|
|
1dda392d1c |
No files matched your search
@@ -3,15 +3,15 @@ updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: "monthly"
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: "monthly"
|
||||
- package-ecosystem: cargo
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
interval: monthly
|
||||
open-pull-requests-limit: 10
|
||||
ignore:
|
||||
- dependency-name: anyhow
|
||||
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
run: python -m py_compile .github/scan-benchmark/scan_bench.py
|
||||
|
||||
- name: Setup Rust
|
||||
uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1.17.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@ecabd13d1c56bd1345c230e542e9144811ad706f # v2.0.0
|
||||
with:
|
||||
toolchain: stable
|
||||
# build.rs formats the generated payload table with rustfmt.
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup Rust
|
||||
uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1.17.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@ecabd13d1c56bd1345c230e542e9144811ad706f # v2.0.0
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
components: clippy, rustfmt
|
||||
|
||||
Generated
+23
-97
@@ -279,7 +279,7 @@ version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
|
||||
dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -387,7 +387,7 @@ dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -455,7 +455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -933,9 +933,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.181"
|
||||
version = "0.2.191"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "459427e2af2b9c839b132acb702a1c654d95e10f8c326bfc2ad11310e458b1c5"
|
||||
checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
@@ -977,9 +977,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.34"
|
||||
version = "0.4.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
|
||||
checksum = "5d15f86d12da44cd8d4ec3c73984537eb7f4cadcb9776a575a0a8a05ca11005c"
|
||||
|
||||
[[package]]
|
||||
name = "lru-cache"
|
||||
@@ -1382,7 +1382,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1445,7 +1445,7 @@ dependencies = [
|
||||
"text_placeholder",
|
||||
"tokio",
|
||||
"toml",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1535,9 +1535,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
||||
checksum = "4e7523beb55eece201a2356bee0bbca0d1ab466c14c07703b2e0ee6d42cb0c2c"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
@@ -1580,7 +1580,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1635,7 +1635,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1750,9 +1750,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "1.1.6+spec-1.1.0"
|
||||
version = "1.1.8+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a"
|
||||
checksum = "20489e00e4d8741d6be680764cc12e270655e375a20d1011e844a9c3379e678d"
|
||||
dependencies = [
|
||||
"indexmap 2.13.0",
|
||||
"serde_core",
|
||||
@@ -1765,27 +1765,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "1.1.1+spec-1.1.0"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
|
||||
checksum = "2b86d767906c6c42421dcba507eb9d203e779497710a47782a224bb871653053"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.1.3+spec-1.1.0"
|
||||
version = "1.1.5+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
|
||||
checksum = "baa693a8032d7e1cada7d0041e96126df243179ff061456783ac7f12bda4744c"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
version = "1.1.3+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
||||
checksum = "06bdbd8cfc056b8d2e2e85f29b56a3bdbecb527cef81eb39e3e7b98af4652770"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
@@ -2035,7 +2035,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2077,15 +2077,6 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
@@ -2119,30 +2110,13 @@ dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm 0.53.1",
|
||||
"windows_aarch64_msvc 0.53.1",
|
||||
"windows_i686_gnu 0.53.1",
|
||||
"windows_i686_gnullvm 0.53.1",
|
||||
"windows_i686_msvc 0.53.1",
|
||||
"windows_x86_64_gnu 0.53.1",
|
||||
"windows_x86_64_gnullvm 0.53.1",
|
||||
"windows_x86_64_msvc 0.53.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
@@ -2155,12 +2129,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
@@ -2173,12 +2141,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
@@ -2191,24 +2153,12 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
@@ -2221,12 +2171,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
@@ -2239,12 +2183,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
@@ -2257,12 +2195,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
@@ -2275,12 +2207,6 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "1.0.0"
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ once_cell = "1.21.4"
|
||||
libc = "0.2"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows-sys = { version = "0.59", features = ["Win32_Networking_WinSock"] }
|
||||
windows-sys = { version = "0.61", features = ["Win32_Networking_WinSock"] }
|
||||
|
||||
[dev-dependencies]
|
||||
parameterized = "2.0.0"
|
||||
|
||||
@@ -25,8 +25,11 @@ pub enum ScanOrder {
|
||||
/// - custom will read the ScriptConfig file and the available scripts in the predefined folders
|
||||
#[derive(Deserialize, Debug, ValueEnum, Clone, PartialEq, Eq, Copy)]
|
||||
pub enum ScriptsRequired {
|
||||
#[serde(alias = "none")]
|
||||
None,
|
||||
#[serde(alias = "default")]
|
||||
Default,
|
||||
#[serde(alias = "custom")]
|
||||
Custom,
|
||||
}
|
||||
|
||||
@@ -526,6 +529,55 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_config_script_mode(value: &str, expected: ScriptsRequired) {
|
||||
let config: Config = toml::from_str(&format!("scripts = \"{value}\""))
|
||||
.expect("documented script mode must deserialize");
|
||||
assert_eq!(config.scripts, Some(expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_scripts_lowercase_none() {
|
||||
assert_config_script_mode("none", ScriptsRequired::None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_scripts_lowercase_default() {
|
||||
assert_config_script_mode("default", ScriptsRequired::Default);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_scripts_lowercase_custom() {
|
||||
assert_config_script_mode("custom", ScriptsRequired::Custom);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_scripts_legacy_and_unknown_controls() {
|
||||
assert_config_script_mode("None", ScriptsRequired::None);
|
||||
assert_config_script_mode("Default", ScriptsRequired::Default);
|
||||
assert_config_script_mode("Custom", ScriptsRequired::Custom);
|
||||
for invalid in ["disabled", "unknown", ""] {
|
||||
assert!(toml::from_str::<Config>(&format!("scripts = \"{invalid}\"")).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_read_accepts_documented_lowercase_script_modes() {
|
||||
for (value, expected) in [
|
||||
("none", ScriptsRequired::None),
|
||||
("default", ScriptsRequired::Default),
|
||||
("custom", ScriptsRequired::Custom),
|
||||
] {
|
||||
let fixture = std::env::temp_dir().join(format!(
|
||||
"rustscan-config-scripts-{}-{value}.toml",
|
||||
std::process::id()
|
||||
));
|
||||
std::fs::write(&fixture, format!("scripts = \"{value}\"\n")).unwrap();
|
||||
let config = Config::read(Some(fixture.clone()));
|
||||
std::fs::remove_file(fixture).unwrap();
|
||||
assert_eq!(config.scripts, Some(expected));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_cli() {
|
||||
Opts::command().debug_assert();
|
||||
|
||||
+72
-15
@@ -128,20 +128,7 @@ fn main() {
|
||||
map.entry(socket.ip()).or_default().push(socket.port());
|
||||
}
|
||||
|
||||
for ip in ips {
|
||||
if ports_per_ip.contains_key(&ip) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we got here it means the IP was not found within the HashMap, this
|
||||
// means the scan couldn't find any open ports for it.
|
||||
|
||||
let x = format!("Looks like I didn't find any open ports for {:?}. This is usually caused by a high batch size.
|
||||
\n*I used {} batch size, consider lowering it with {} or a comfortable number for your system.
|
||||
\n Alternatively, increase the timeout if your ping is high. Rustscan -t 2000 for 2000 milliseconds (2s) timeout.\n",
|
||||
ip,
|
||||
batch_size,
|
||||
"'rustscan -b <batch_size> -a <ip address>'");
|
||||
if let Some(x) = no_open_ports_warning(&ips, &ports_per_ip, &closed_ports_per_ip, batch_size) {
|
||||
warning!(x, opts.greppable, opts.accessible);
|
||||
}
|
||||
|
||||
@@ -224,6 +211,35 @@ fn main() {
|
||||
info!("{}", benchmarks.summary());
|
||||
}
|
||||
|
||||
/// Builds the hint printed when the scan found no open ports.
|
||||
///
|
||||
/// A host without open ports is normal when another host answered (a subnet
|
||||
/// sweep for one port): replies got through at this batch size and timeout,
|
||||
/// so batch size and timeout are unlikely to be the cause. The hint is printed
|
||||
/// once, and only when no host answered with an open or, under `--closed`, a
|
||||
/// closed port.
|
||||
fn no_open_ports_warning(
|
||||
ips: &[IpAddr],
|
||||
ports_per_ip: &HashMap<IpAddr, Vec<u16>>,
|
||||
closed_ports_per_ip: &HashMap<IpAddr, Vec<u16>>,
|
||||
batch_size: usize,
|
||||
) -> Option<String> {
|
||||
if !ports_per_ip.is_empty() || !closed_ports_per_ip.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let target = match ips {
|
||||
[ip] => format!("for {ip:?}"),
|
||||
_ => format!("on any of the {} addresses", ips.len()),
|
||||
};
|
||||
Some(format!("Looks like I didn't find any open ports {}. This is usually caused by a high batch size.
|
||||
\n*I used {} batch size, consider lowering it with {} or a comfortable number for your system.
|
||||
\n Alternatively, increase the timeout if your ping is high. Rustscan -t 2000 for 2000 milliseconds (2s) timeout.\n",
|
||||
target,
|
||||
batch_size,
|
||||
"'rustscan -b <batch_size> -a <ip address>'"))
|
||||
}
|
||||
|
||||
/// Determines the actual batch size used by the scanner.
|
||||
///
|
||||
/// Unix systems may reduce the requested batch size according to the process
|
||||
@@ -373,7 +389,48 @@ mod tests {
|
||||
use super::effective_batch_size;
|
||||
#[cfg(unix)]
|
||||
use super::{adjust_ulimit_size, infer_batch_size};
|
||||
use super::{print_opening, Opts};
|
||||
use super::{no_open_ports_warning, print_opening, Opts};
|
||||
use std::collections::HashMap;
|
||||
use std::net::IpAddr;
|
||||
|
||||
fn hosts(n: u8) -> Vec<IpAddr> {
|
||||
(1..=n).map(|i| IpAddr::from([192, 0, 2, i])).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_open_ports_silent_when_another_host_answered() {
|
||||
// Sweeping a subnet for one port: one host has it open, the rest do not.
|
||||
let ips = hosts(254);
|
||||
let ports_per_ip = HashMap::from([(ips[0], vec![22])]);
|
||||
|
||||
assert!(no_open_ports_warning(&ips, &ports_per_ip, &HashMap::new(), 4500).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_open_ports_silent_when_a_host_answered_closed() {
|
||||
// With --closed, a refused connection also proves replies get through.
|
||||
let ips = hosts(254);
|
||||
let closed_ports_per_ip = HashMap::from([(ips[0], vec![443])]);
|
||||
|
||||
assert!(no_open_ports_warning(&ips, &HashMap::new(), &closed_ports_per_ip, 4500).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_open_ports_warns_once_when_nothing_found() {
|
||||
let warning = no_open_ports_warning(&hosts(254), &HashMap::new(), &HashMap::new(), 4500);
|
||||
|
||||
let warning = warning.expect("one hint when nothing was found");
|
||||
assert!(warning.contains("on any of the 254 addresses."));
|
||||
assert!(warning.contains("4500 batch size"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_open_ports_names_the_single_host() {
|
||||
let warning = no_open_ports_warning(&hosts(1), &HashMap::new(), &HashMap::new(), 4500);
|
||||
|
||||
let warning = warning.expect("one hint for the single host");
|
||||
assert!(warning.contains("for 192.0.2.1."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
|
||||
+121
-2
@@ -143,9 +143,78 @@ pub fn init_scripts(scripts: &ScriptsRequired) -> Result<Vec<ScriptFile>> {
|
||||
}
|
||||
}
|
||||
|
||||
verify_script_programs(&scripts_to_run)?;
|
||||
|
||||
Ok(scripts_to_run)
|
||||
}
|
||||
|
||||
/// Whether `path/program` is something the shell could run.
|
||||
#[cfg(not(windows))]
|
||||
fn program_exists_in(dir: &std::path::Path, program: &str) -> bool {
|
||||
dir.join(program).is_file()
|
||||
}
|
||||
|
||||
/// Whether `path/program` is something `cmd.exe` could run, which appends
|
||||
/// entries from `PATHEXT` to a bare name.
|
||||
#[cfg(windows)]
|
||||
fn program_exists_in(dir: &std::path::Path, program: &str) -> bool {
|
||||
if dir.join(program).is_file() {
|
||||
return true;
|
||||
}
|
||||
let extensions = std::env::var("PATHEXT").unwrap_or_default();
|
||||
extensions
|
||||
.split(';')
|
||||
.filter(|extension| !extension.is_empty())
|
||||
.any(|extension| dir.join(format!("{program}{extension}")).is_file())
|
||||
}
|
||||
|
||||
fn program_is_available(program: &str) -> bool {
|
||||
match std::env::var_os("PATH") {
|
||||
Some(path) => std::env::split_paths(&path).any(|dir| program_exists_in(&dir, program)),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check that the program each selected script starts with is available.
|
||||
///
|
||||
/// Scripts run through `sh -c`, so a missing binary surfaces only as a bare
|
||||
/// "Exit code = 127" once the scan has already finished. Report it up front
|
||||
/// instead, before a long scan has been wasted.
|
||||
fn verify_script_programs(scripts_to_run: &[ScriptFile]) -> Result<()> {
|
||||
for script in scripts_to_run {
|
||||
let call_format = match &script.call_format {
|
||||
Some(call_format) => call_format,
|
||||
None => continue,
|
||||
};
|
||||
let program = match call_format.split_whitespace().next() {
|
||||
Some(program) => program,
|
||||
None => continue,
|
||||
};
|
||||
// A leading assignment (`FOO=bar nmap ...`) or an absolute path is not
|
||||
// a bare program name, and `{{...}}` placeholders are only filled in
|
||||
// when the script actually runs, so leave those formats alone.
|
||||
if program.contains('=')
|
||||
|| program.contains('/')
|
||||
|| program.contains('$')
|
||||
|| program.contains("{{")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if !program_is_available(program) {
|
||||
return Err(anyhow!(
|
||||
"The script `{}` runs `{}`, which is not installed or not in PATH. \
|
||||
Install it, or skip scripts with `--scripts none`.",
|
||||
script
|
||||
.path
|
||||
.as_ref()
|
||||
.map_or_else(|| "default".to_string(), |path| path.display().to_string()),
|
||||
program
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn parse_scripts(scripts: Vec<PathBuf>) -> Vec<ScriptFile> {
|
||||
let mut parsed_scripts: Vec<ScriptFile> = Vec::with_capacity(scripts.len());
|
||||
for script in scripts {
|
||||
@@ -348,8 +417,7 @@ impl ScriptFile {
|
||||
if let Ok(file) = File::open(script) {
|
||||
for mut line in io::BufReader::new(file).lines().skip(1).flatten() {
|
||||
if line.starts_with('#') {
|
||||
line.retain(|c| c != '#');
|
||||
line = line.trim().to_string();
|
||||
line = line[1..].trim().to_string();
|
||||
line.push('\n');
|
||||
lines_buf.push_str(&line);
|
||||
} else {
|
||||
@@ -403,6 +471,21 @@ impl ScriptConfig {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn script_headers_preserve_literal_hash_characters() {
|
||||
let fixture =
|
||||
std::env::temp_dir().join(format!("rustscan-literal-hash-{}.txt", std::process::id()));
|
||||
fs::write(&fixture, "#!/usr/bin/env python3\n#tags = [\"C#\"]\n#call_format = \"fixture {{ip}} {{port}} https://example.invalid/#section\"\n\n").unwrap();
|
||||
let parsed = ScriptFile::new(fixture.clone());
|
||||
fs::remove_file(fixture).unwrap();
|
||||
let parsed = parsed.expect("valid TOML metadata");
|
||||
assert_eq!(parsed.tags, Some(vec!["C#".to_owned()]));
|
||||
assert_eq!(
|
||||
parsed.call_format.as_deref(),
|
||||
Some("fixture {{ip}} {{port}} https://example.invalid/#section")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn find_and_parse_scripts() {
|
||||
let scripts = find_scripts("fixtures/.rustscan_scripts".into()).unwrap();
|
||||
@@ -495,4 +578,40 @@ mod tests {
|
||||
|
||||
assert_eq!(script_dir_base, dirs::home_dir().unwrap());
|
||||
}
|
||||
|
||||
fn script_with_call_format(call_format: &str) -> ScriptFile {
|
||||
ScriptFile {
|
||||
path: Some(PathBuf::from("/tmp/script.txt")),
|
||||
tags: None,
|
||||
developer: None,
|
||||
port: None,
|
||||
ports_separator: None,
|
||||
call_format: Some(call_format.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_script_program_is_reported() {
|
||||
let scripts = vec![script_with_call_format("definitely-not-a-real-binary {{ip}}")];
|
||||
let err = verify_script_programs(&scripts).unwrap_err();
|
||||
assert!(err.to_string().contains("definitely-not-a-real-binary"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn available_script_program_is_accepted() {
|
||||
// `sh` is what the scripts are actually run through, so it is present
|
||||
// on every platform that can execute them.
|
||||
let scripts = vec![script_with_call_format("sh -c true {{ip}}")];
|
||||
assert!(verify_script_programs(&scripts).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn placeholder_and_assignment_prefixes_are_skipped() {
|
||||
let scripts = vec![
|
||||
script_with_call_format("{{script}} {{ip}} {{port}}"),
|
||||
script_with_call_format("FOO=bar definitely-not-a-real-binary"),
|
||||
script_with_call_format("/opt/custom/nmap {{ip}}"),
|
||||
];
|
||||
assert!(verify_script_programs(&scripts).is_ok());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user