#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use std::path::PathBuf;
use fleetreach_cli::config::Config;
use fleetreach_cli::orchestrate::{
scan_fleet, GhActionsScan, GoScan, HexScan, JuliaScan, MavenScan, NpmScan, NuGetScan,
PackagistScan, PyPiScan, RubyGemsScan, SwiftScan, Toolchain,
};
use fleetreach_core::semver::Version;
use fleetreach_core::{Occurrence, RepoId, ScanStatus};
use fleetreach_go::SandboxPolicy;
use fleetreach_scan::AdvisoryDb;
fn go() -> GoScan<'static> {
GoScan {
govulncheck: None,
sandbox: SandboxPolicy::Off,
vuln_db: None,
offline: false,
}
}
fn npm() -> NpmScan<'static> {
NpmScan { vuln_db: None }
}
fn pypi() -> PyPiScan<'static> {
PyPiScan { vuln_db: None }
}
fn rubygems() -> RubyGemsScan<'static> {
RubyGemsScan { vuln_db: None }
}
fn packagist() -> PackagistScan<'static> {
PackagistScan { vuln_db: None }
}
fn nuget() -> NuGetScan<'static> {
NuGetScan { vuln_db: None }
}
fn julia() -> JuliaScan<'static> {
JuliaScan { vuln_db: None }
}
fn swift() -> SwiftScan<'static> {
SwiftScan { vuln_db: None }
}
fn hex() -> HexScan<'static> {
HexScan { vuln_db: None }
}
fn ghactions() -> GhActionsScan<'static> {
GhActionsScan { vuln_db: None }
}
fn maven() -> MavenScan<'static> {
MavenScan { vuln_db: None }
}
fn cli_fixtures() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures")
}
fn advisory_db() -> AdvisoryDb {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../scan/tests/fixtures/advisory-db");
AdvisoryDb::open(&path).expect("open fixture advisory db")
}
const FLEET: &str = r#"
[[repo]]
id = "repo-vuln"
path = "repos/repo-vuln"
[[repo]]
id = "repo-warn"
path = "repos/repo-warn"
[[repo]]
id = "repo-empty"
path = "repos/repo-empty"
[[repo]]
id = "repo-glob"
path = "repos/repo-glob"
glob = true
glob_max_depth = 2
"#;
fn config() -> Config {
Config::from_str(FLEET, &cli_fixtures(), "fleet.toml").expect("valid fleet config")
}
fn status_of<'a>(data: &'a fleetreach_cli::ScanData, id: &str) -> &'a ScanStatus {
&data
.outcomes
.iter()
.find(|o| o.repo == RepoId(id.into()))
.unwrap_or_else(|| panic!("no outcome for {id}"))
.status
}
#[test]
fn each_repo_degrades_independently() {
let data = scan_fleet(
&advisory_db(),
&config(),
None,
None,
&go(),
&npm(),
&pypi(),
&rubygems(),
&packagist(),
&nuget(),
&julia(),
&swift(),
&hex(),
&ghactions(),
&maven(),
);
assert_eq!(data.outcomes.len(), 4);
assert_eq!(
status_of(&data, "repo-vuln"),
&ScanStatus::Scanned {
vulns: 1,
warnings: 0
}
);
assert_eq!(
status_of(&data, "repo-warn"),
&ScanStatus::Scanned {
vulns: 0,
warnings: 1
}
);
assert!(matches!(
status_of(&data, "repo-empty"),
ScanStatus::Errored { .. }
));
}
#[test]
fn glob_discovery_respects_the_depth_bound() {
let data = scan_fleet(
&advisory_db(),
&config(),
None,
None,
&go(),
&npm(),
&pypi(),
&rubygems(),
&packagist(),
&nuget(),
&julia(),
&swift(),
&hex(),
&ghactions(),
&maven(),
);
assert_eq!(
status_of(&data, "repo-glob"),
&ScanStatus::Scanned {
vulns: 1,
warnings: 0
}
);
}
#[test]
fn aggregates_findings_across_the_fleet() {
let data = scan_fleet(
&advisory_db(),
&config(),
None,
None,
&go(),
&npm(),
&pypi(),
&rubygems(),
&packagist(),
&nuget(),
&julia(),
&swift(),
&hex(),
&ghactions(),
&maven(),
);
assert_eq!(data.vulnerabilities.len(), 2);
assert_eq!(data.warnings.len(), 1);
assert!(data
.vulnerabilities
.iter()
.all(|v| v.occurrences.len() == 1));
}
#[test]
fn toolchain_advisory_joins_the_fleet_streams() {
let toolchain = Toolchain {
channel: "stable 1.40.0".into(),
version: Version::new(1, 40, 0),
};
let data = scan_fleet(
&advisory_db(),
&config(),
Some(&toolchain),
None,
&go(),
&npm(),
&pypi(),
&rubygems(),
&packagist(),
&nuget(),
&julia(),
&swift(),
&hex(),
&ghactions(),
&maven(),
);
assert_eq!(data.vulnerabilities.len(), 3);
let toolchain_vuln = data
.vulnerabilities
.iter()
.find(|v| v.advisory_id == "RUSTSEC-2099-0003")
.expect("toolchain advisory present");
assert!(matches!(
toolchain_vuln.occurrences[0],
Occurrence::Toolchain { .. }
));
}