diff --git a/benches/dynamic_bench.rs b/benches/dynamic_bench.rs
index 5c74a342..34fec934 100644
--- a/benches/dynamic_bench.rs
+++ b/benches/dynamic_bench.rs
@@ -129,7 +129,10 @@ fn bench_sandbox_run_payload(c: &mut Criterion) {
let spec = make_sqli_spec();
let harness = harness::build(&spec).expect("harness build");
let payloads = payloads_for(Cap::SQL_QUERY);
- let payload = payloads.iter().find(|p| !p.is_benign).expect("sqli payload");
+ let payload = payloads
+ .iter()
+ .find(|p| !p.is_benign)
+ .expect("sqli payload");
let opts = SandboxOptions {
timeout: std::time::Duration::from_secs(10),
..SandboxOptions::default()
@@ -192,10 +195,19 @@ fn bench_docker_exec_warm(c: &mut Criterion) {
let container = "nyx-bench-exec-warm";
let _ = std::process::Command::new("docker")
.args([
- "run", "-d", "--rm", "--name", container,
- "--cap-drop=ALL", "--security-opt", "no-new-privileges:true",
- "--network", "none",
- "python:3-slim", "sleep", "300",
+ "run",
+ "-d",
+ "--rm",
+ "--name",
+ container,
+ "--cap-drop=ALL",
+ "--security-opt",
+ "no-new-privileges:true",
+ "--network",
+ "none",
+ "python:3-slim",
+ "sleep",
+ "300",
])
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
@@ -239,7 +251,10 @@ fn bench_docker_payload_cost(c: &mut Criterion) {
let spec = make_sqli_spec();
let built = harness::build(&spec).expect("harness build");
let payloads = payloads_for(Cap::SQL_QUERY);
- let payload = payloads.iter().find(|p| !p.is_benign).expect("sqli payload");
+ let payload = payloads
+ .iter()
+ .find(|p| !p.is_benign)
+ .expect("sqli payload");
let opts = SandboxOptions {
timeout: std::time::Duration::from_secs(30),
backend: SandboxBackend::Docker,
diff --git a/build.rs b/build.rs
index 3e1efb4b..54959ac3 100644
--- a/build.rs
+++ b/build.rs
@@ -154,10 +154,12 @@ fn emit_seccomp_policy() {
.iter()
.find(|(n, _)| *n == cap_name.as_str())
.map(|(_, b)| *b)
- .unwrap_or_else(|| panic!(
- "seccomp_policy.toml references unknown Cap '{cap_name}' — \
+ .unwrap_or_else(|| {
+ panic!(
+ "seccomp_policy.toml references unknown Cap '{cap_name}' — \
add it to CAP_BIT_FOR_NAME in build.rs first"
- ));
+ )
+ });
out.push_str(&format!(" (0x{bit:08x}_u32, &[\n"));
for name in allow {
out.push_str(&format!(" \"{}\",\n", escape(name)));
@@ -335,7 +337,9 @@ fn emit_image_digests() {
out.push_str("// generated by build.rs from tools/image-builder/images.toml — do not edit\n\n");
// IMAGE_DIGESTS: only entries with a non-empty digest survive.
- out.push_str("pub static IMAGE_DIGESTS: phf::Map<&'static str, &'static str> = phf::phf_map! {\n");
+ out.push_str(
+ "pub static IMAGE_DIGESTS: phf::Map<&'static str, &'static str> = phf::phf_map! {\n",
+ );
for e in &entries {
if e.digest.is_empty() {
continue;
@@ -351,7 +355,9 @@ fn emit_image_digests() {
// IMAGE_BASES: every entry, digest stripped. Used by docker.rs when no
// digest is pinned yet so a `docker pull ` is still possible.
- out.push_str("pub static IMAGE_BASES: phf::Map<&'static str, &'static str> = phf::phf_map! {\n");
+ out.push_str(
+ "pub static IMAGE_BASES: phf::Map<&'static str, &'static str> = phf::phf_map! {\n",
+ );
for e in &entries {
out.push_str(&format!(
" \"{}\" => \"{}\",\n",
@@ -404,8 +410,12 @@ fn parse_image_catalogue(src: &str) -> Vec {
continue;
}
- let Some(slot) = current.as_mut() else { continue };
- let Some((key, value)) = line.split_once('=') else { continue };
+ let Some(slot) = current.as_mut() else {
+ continue;
+ };
+ let Some((key, value)) = line.split_once('=') else {
+ continue;
+ };
let key = key.trim();
let value = value.trim().trim_matches('"').trim_matches('\'');
match key {
diff --git a/src/auth_analysis/auth_markers.rs b/src/auth_analysis/auth_markers.rs
index d38e09b7..2fb66312 100644
--- a/src/auth_analysis/auth_markers.rs
+++ b/src/auth_analysis/auth_markers.rs
@@ -236,9 +236,18 @@ mod tests {
#[test]
fn flask_login_required_resolves_case_insensitively() {
- assert!(is_router_auth_marker(AuthFramework::Flask, "login_required"));
- assert!(is_router_auth_marker(AuthFramework::Flask, "Login_Required"));
- assert!(!is_router_auth_marker(AuthFramework::Flask, "something_else"));
+ assert!(is_router_auth_marker(
+ AuthFramework::Flask,
+ "login_required"
+ ));
+ assert!(is_router_auth_marker(
+ AuthFramework::Flask,
+ "Login_Required"
+ ));
+ assert!(!is_router_auth_marker(
+ AuthFramework::Flask,
+ "something_else"
+ ));
}
#[test]
diff --git a/src/baseline.rs b/src/baseline.rs
index b74bee5a..1bf8ceef 100644
--- a/src/baseline.rs
+++ b/src/baseline.rs
@@ -147,23 +147,20 @@ pub fn diags_to_baseline_entries(diags: &[Diag]) -> Vec {
/// `path`, and `rule_id` — no source code snippets or flow steps.
pub fn write_baseline(path: &Path, diags: &[Diag]) -> crate::errors::NyxResult<()> {
let entries = diags_to_baseline_entries(diags);
- let json = serde_json::to_string_pretty(&entries).map_err(|e| {
- crate::errors::NyxError::Msg(format!("baseline serialize error: {e}"))
- })?;
+ let json = serde_json::to_string_pretty(&entries)
+ .map_err(|e| crate::errors::NyxError::Msg(format!("baseline serialize error: {e}")))?;
if let Some(parent) = path.parent()
- && !parent.as_os_str().is_empty() {
- std::fs::create_dir_all(parent).map_err(|e| {
- crate::errors::NyxError::Msg(format!(
- "cannot create baseline dir {}: {e}",
- parent.display()
- ))
- })?;
- }
+ && !parent.as_os_str().is_empty()
+ {
+ std::fs::create_dir_all(parent).map_err(|e| {
+ crate::errors::NyxError::Msg(format!(
+ "cannot create baseline dir {}: {e}",
+ parent.display()
+ ))
+ })?;
+ }
std::fs::write(path, json).map_err(|e| {
- crate::errors::NyxError::Msg(format!(
- "cannot write baseline {}: {e}",
- path.display()
- ))
+ crate::errors::NyxError::Msg(format!("cannot write baseline {}: {e}", path.display()))
})
}
@@ -183,9 +180,7 @@ fn classify_transition(
Transition::FlippedNotConfirmed
}
// NotConfirmed → Confirmed: regression
- (Some(VerifyStatus::NotConfirmed), Some(VerifyStatus::Confirmed)) => {
- Transition::Regressed
- }
+ (Some(VerifyStatus::NotConfirmed), Some(VerifyStatus::Confirmed)) => Transition::Regressed,
// None / Inconclusive / Unsupported → Confirmed
(_, Some(VerifyStatus::Confirmed)) => Transition::FlippedConfirmed,
// Everything else: treat as unchanged (e.g. Confirmed → Inconclusive
@@ -380,9 +375,7 @@ pub fn format_diff_console(diff: &VerdictDiff) -> String {
}
Transition::FlippedConfirmed => {
non_unchanged += 1;
- lines.push(format!(
- " + {hash_str}: new Confirmed at {loc}"
- ));
+ lines.push(format!(" + {hash_str}: new Confirmed at {loc}"));
}
Transition::Unchanged => {}
}
@@ -402,7 +395,7 @@ pub fn format_diff_console(diff: &VerdictDiff) -> String {
#[cfg(test)]
mod tests {
use super::*;
- use crate::commands::scan::{compute_stable_hash, Diag};
+ use crate::commands::scan::{Diag, compute_stable_hash};
use crate::evidence::{Evidence, VerifyResult, VerifyStatus};
use crate::patterns::{FindingCategory, Severity};
@@ -471,7 +464,10 @@ mod tests {
)];
let diff = compute_verdict_diff(&[], ¤t);
assert_eq!(diff.entries[0].transition, Transition::New);
- assert_eq!(diff.entries[0].current_status, Some(VerifyStatus::Confirmed));
+ assert_eq!(
+ diff.entries[0].current_status,
+ Some(VerifyStatus::Confirmed)
+ );
}
#[test]
@@ -620,7 +616,10 @@ mod tests {
let tmp = tempfile::NamedTempFile::new().unwrap();
write_baseline(tmp.path(), &[d]).unwrap();
let content = std::fs::read_to_string(tmp.path()).unwrap();
- assert!(!content.contains("SECRET CODE"), "baseline must not contain source code");
+ assert!(
+ !content.contains("SECRET CODE"),
+ "baseline must not contain source code"
+ );
}
#[test]
diff --git a/src/callgraph.rs b/src/callgraph.rs
index 884b3ace..1b2ebaab 100644
--- a/src/callgraph.rs
+++ b/src/callgraph.rs
@@ -259,7 +259,6 @@ impl ClassMethodIndex {
.unwrap_or_default(),
}
}
-
}
// ── Type hierarchy index ────────────────────────────────────────────────
@@ -955,10 +954,9 @@ impl FileReachMap {
fn normalize<'a>(&self, path: &'a str) -> std::borrow::Cow<'a, str> {
match self.scan_root.as_deref() {
- Some(root) => std::borrow::Cow::Owned(crate::symbol::normalize_namespace(
- path,
- Some(root),
- )),
+ Some(root) => {
+ std::borrow::Cow::Owned(crate::symbol::normalize_namespace(path, Some(root)))
+ }
None => std::borrow::Cow::Borrowed(path),
}
}
@@ -2926,7 +2924,10 @@ mod tests {
let transitive = callers_transitive(&cg, &sink_key);
let caller_names: std::collections::HashSet =
transitive.iter().map(|k| k.name.clone()).collect();
- assert!(caller_names.contains("process"), "process should reach sink");
+ assert!(
+ caller_names.contains("process"),
+ "process should reach sink"
+ );
assert!(caller_names.contains("handle"), "handle should reach sink");
assert_eq!(transitive.len(), 2, "sink itself must be excluded");
diff --git a/src/chain/edges.rs b/src/chain/edges.rs
index cd0c8d92..cf2da89b 100644
--- a/src/chain/edges.rs
+++ b/src/chain/edges.rs
@@ -182,30 +182,28 @@ pub fn pick_chain_cap(bits: u32) -> Option {
while remaining != 0 {
let bit = 1u32 << remaining.trailing_zeros();
if let Some(cap) = Cap::from_bits(bit)
- && lookup_impact(cap, None).is_some() {
- return Some(cap);
- }
+ && lookup_impact(cap, None).is_some()
+ {
+ return Some(cap);
+ }
remaining &= !bit;
}
lowest_cap(bits)
}
-fn locate_reach(
- loc: &SourceLocation,
- surface: &SurfaceMap,
- reach: Option<&FileReachMap>,
-) -> Reach {
+fn locate_reach(loc: &SourceLocation, surface: &SurfaceMap, reach: Option<&FileReachMap>) -> Reach {
// Pass 1: file-local match (legacy behaviour, always applies).
for node in &surface.nodes {
if let SurfaceNode::EntryPoint(ep) = node
- && ep.handler_location.file == loc.file {
- return Reach::Reachable {
- location: ep.location.clone(),
- method: ep.method,
- route: ep.route.clone(),
- auth_required: ep.auth_required,
- };
- }
+ && ep.handler_location.file == loc.file
+ {
+ return Reach::Reachable {
+ location: ep.location.clone(),
+ method: ep.method,
+ route: ep.route.clone(),
+ auth_required: ep.auth_required,
+ };
+ }
}
// Pass 2: transitive caller match via the call graph. Only fires
// when `reach` is supplied — keeps the legacy file-local behaviour
@@ -213,14 +211,15 @@ fn locate_reach(
if let Some(reach) = reach {
for node in &surface.nodes {
if let SurfaceNode::EntryPoint(ep) = node
- && reach.reaches(&ep.handler_location.file, &loc.file) {
- return Reach::Reachable {
- location: ep.location.clone(),
- method: ep.method,
- route: ep.route.clone(),
- auth_required: ep.auth_required,
- };
- }
+ && reach.reaches(&ep.handler_location.file, &loc.file)
+ {
+ return Reach::Reachable {
+ location: ep.location.clone(),
+ method: ep.method,
+ route: ep.route.clone(),
+ auth_required: ep.auth_required,
+ };
+ }
}
}
Reach::Unreachable
diff --git a/src/chain/feasibility.rs b/src/chain/feasibility.rs
index 63da9be1..8c1599cd 100644
--- a/src/chain/feasibility.rs
+++ b/src/chain/feasibility.rs
@@ -69,7 +69,10 @@ impl Feasibility {
/// in the doc's table can fire. Phase 25's scoring pass uses this
/// flavour.
pub fn for_finding(diag: &Diag) -> Feasibility {
- let verdict = diag.evidence.as_ref().and_then(|e| e.dynamic_verdict.as_ref());
+ let verdict = diag
+ .evidence
+ .as_ref()
+ .and_then(|e| e.dynamic_verdict.as_ref());
Self::bucket_from_verdict(verdict, diag.confidence)
}
@@ -82,9 +85,7 @@ impl Feasibility {
) -> Feasibility {
match verdict.map(|v| v.status) {
Some(VerifyStatus::Confirmed) => Feasibility::Confirmed,
- Some(VerifyStatus::Inconclusive)
- if static_confidence == Some(Confidence::High) =>
- {
+ Some(VerifyStatus::Inconclusive) if static_confidence == Some(Confidence::High) => {
Feasibility::InconclusiveHighConf
}
_ => Feasibility::Unverified,
diff --git a/src/chain/finding.rs b/src/chain/finding.rs
index 9ad49e87..e8b1ccc1 100644
--- a/src/chain/finding.rs
+++ b/src/chain/finding.rs
@@ -210,23 +210,14 @@ mod tests {
#[test]
fn stable_hash_changes_with_member_order() {
- let a = ChainFinding::compute_stable_hash(
- &[member(1), member(2)],
- ImpactCategory::Rce,
- );
- let b = ChainFinding::compute_stable_hash(
- &[member(2), member(1)],
- ImpactCategory::Rce,
- );
+ let a = ChainFinding::compute_stable_hash(&[member(1), member(2)], ImpactCategory::Rce);
+ let b = ChainFinding::compute_stable_hash(&[member(2), member(1)], ImpactCategory::Rce);
assert_ne!(a, b);
}
#[test]
fn stable_hash_changes_with_impact() {
- let a = ChainFinding::compute_stable_hash(
- &[member(1), member(2)],
- ImpactCategory::Rce,
- );
+ let a = ChainFinding::compute_stable_hash(&[member(1), member(2)], ImpactCategory::Rce);
let b = ChainFinding::compute_stable_hash(
&[member(1), member(2)],
ImpactCategory::BrowserToLocalRce,
diff --git a/src/chain/impact.rs b/src/chain/impact.rs
index bf6c1f10..351e9653 100644
--- a/src/chain/impact.rs
+++ b/src/chain/impact.rs
@@ -250,9 +250,10 @@ pub fn lookup_impact(source: Cap, adjacent: Option) -> Option build_errors += 1,
@@ -400,7 +400,11 @@ fn run_chain_steps(
let mut prev_output: Option> = None;
let last_idx = built_steps.len().saturating_sub(1);
for (idx, (workdir, spec)) in built_steps.iter().enumerate() {
- let step_terminal = if idx == last_idx { Some(terminal) } else { None };
+ let step_terminal = if idx == last_idx {
+ Some(terminal)
+ } else {
+ None
+ };
let step = lang::compose_chain_step(spec.lang, prev_output.as_deref(), step_terminal);
let step_path = workdir.join(&step.filename);
@@ -459,7 +463,13 @@ fn run_chain_steps(
}
}
}
- (steps_run, sandbox_errors, steps_timeout, nonzero_exits, final_sink_hit)
+ (
+ steps_run,
+ sandbox_errors,
+ steps_timeout,
+ nonzero_exits,
+ final_sink_hit,
+ )
}
/// Phase 26 — Track G.3: drive composite dynamic re-verification for
@@ -472,7 +482,13 @@ pub fn reverify_chain(
surface: &SurfaceMap,
opts: &VerifyOptions,
) -> ChainReverifyResult {
- reverify_chain_with(chain, member_diags, surface, opts, &DefaultCompositeReverifier)
+ reverify_chain_with(
+ chain,
+ member_diags,
+ surface,
+ opts,
+ &DefaultCompositeReverifier,
+ )
}
/// Inject-the-reverifier flavour of [`reverify_chain`].
@@ -630,7 +646,10 @@ mod tests {
assert!(!result.was_downgraded());
assert_eq!(result.severity_after, ChainSeverity::Critical);
assert_eq!(chain.severity, ChainSeverity::Critical);
- assert_eq!(chain.dynamic_verdict.as_ref().unwrap().status, VerifyStatus::Confirmed);
+ assert_eq!(
+ chain.dynamic_verdict.as_ref().unwrap().status,
+ VerifyStatus::Confirmed
+ );
assert!(chain.reverify_reason.is_none());
}
@@ -690,7 +709,10 @@ mod tests {
);
assert!(results.is_empty());
for c in &chains {
- assert!(c.dynamic_verdict.is_none(), "no verdict attached when top_n=0");
+ assert!(
+ c.dynamic_verdict.is_none(),
+ "no verdict attached when top_n=0"
+ );
}
}
diff --git a/src/chain/score.rs b/src/chain/score.rs
index 5e64ed7e..bd310574 100644
--- a/src/chain/score.rs
+++ b/src/chain/score.rs
@@ -178,8 +178,13 @@ mod tests {
#[test]
fn category_weights_strictly_ordered() {
- assert!(category_weight(ImpactCategory::BrowserToLocalRce) > category_weight(ImpactCategory::Rce));
- assert!(category_weight(ImpactCategory::Rce) > category_weight(ImpactCategory::SessionHijack));
+ assert!(
+ category_weight(ImpactCategory::BrowserToLocalRce)
+ > category_weight(ImpactCategory::Rce)
+ );
+ assert!(
+ category_weight(ImpactCategory::Rce) > category_weight(ImpactCategory::SessionHijack)
+ );
assert!(
category_weight(ImpactCategory::SessionHijack)
> category_weight(ImpactCategory::InternalNetworkAccess)
diff --git a/src/chain/search.rs b/src/chain/search.rs
index 7f764115..9ab7fb22 100644
--- a/src/chain/search.rs
+++ b/src/chain/search.rs
@@ -120,8 +120,16 @@ pub fn find_chains_with_reach(
.filter(|e| edge_reaches_entry(e, entry, reach))
.collect();
candidates.sort_by(|a, b| {
- (a.finding.stable_hash, &a.finding.rule_id, &a.finding.location)
- .cmp(&(b.finding.stable_hash, &b.finding.rule_id, &b.finding.location))
+ (
+ a.finding.stable_hash,
+ &a.finding.rule_id,
+ &a.finding.location,
+ )
+ .cmp(&(
+ b.finding.stable_hash,
+ &b.finding.rule_id,
+ &b.finding.location,
+ ))
});
for sink in &sinks {
// Scope candidates to the sink: same-file match (legacy),
@@ -139,13 +147,9 @@ pub fn find_chains_with_reach(
})
.copied()
.collect();
- if let Some(chain) = compose_chain(
- entry,
- sink,
- &scoped,
- cfg.max_depth,
- local_listener_present,
- ) && chain.score >= cfg.min_score
+ if let Some(chain) =
+ compose_chain(entry, sink, &scoped, cfg.max_depth, local_listener_present)
+ && chain.score >= cfg.min_score
{
chains.push(chain);
}
@@ -201,15 +205,9 @@ fn is_loopback_label(s: &str) -> bool {
|| lower.contains("://localhost")
}
-fn edge_reaches_entry(
- edge: &ChainEdge,
- entry: &EntryPoint,
- reach: Option<&FileReachMap>,
-) -> bool {
+fn edge_reaches_entry(edge: &ChainEdge, entry: &EntryPoint, reach: Option<&FileReachMap>) -> bool {
let route_method_match = match &edge.reach {
- Reach::Reachable { route, method, .. } => {
- *route == entry.route && *method == entry.method
- }
+ Reach::Reachable { route, method, .. } => *route == entry.route && *method == entry.method,
Reach::Unreachable => return false,
};
if !route_method_match {
@@ -265,8 +263,7 @@ fn compose_chain(
let bound = scoped.len().min(max_depth);
let path: Vec<&ChainEdge> = scoped[..bound].to_vec();
let sink_cap = sole_cap(sink.cap_bits)?;
- let (impact, member_impacts) =
- resolve_impact(&path, sink_cap, entry, local_listener_present)?;
+ let (impact, member_impacts) = resolve_impact(&path, sink_cap, entry, local_listener_present)?;
let mut chain = build_chain(entry, sink, &path, impact, &member_impacts);
// SSRF + LocalListener refinement (Phase 24 deferred close): when
// the implied impact is `InternalNetworkAccess` AND the SurfaceMap
@@ -394,9 +391,7 @@ fn build_chain(
/// member edge has `Feasibility::Confirmed` the composite verdict
/// inherits that confirmation; otherwise `None` (Phase 26 will run a
/// real composite re-verification pass).
-fn composite_dynamic_verdict(
- _path: &[ChainEdge],
-) -> Option {
+fn composite_dynamic_verdict(_path: &[ChainEdge]) -> Option {
None
}
@@ -649,7 +644,9 @@ mod tests {
)
};
let mut surface_no_listener = SurfaceMap::new();
- surface_no_listener.nodes.push(entry("app.py", "/fetch", false));
+ surface_no_listener
+ .nodes
+ .push(entry("app.py", "/fetch", false));
surface_no_listener
.nodes
.push(sink("app.py", 20, "requests.get", Cap::SSRF));
@@ -662,7 +659,10 @@ mod tests {
},
);
assert_eq!(baseline.len(), 1);
- assert_eq!(baseline[0].implied_impact, ImpactCategory::InternalNetworkAccess);
+ assert_eq!(
+ baseline[0].implied_impact,
+ ImpactCategory::InternalNetworkAccess
+ );
let mut surface_with_listener = surface_no_listener.clone();
surface_with_listener
@@ -681,7 +681,10 @@ mod tests {
},
);
assert_eq!(boosted.len(), 1);
- assert_eq!(boosted[0].implied_impact, ImpactCategory::InternalNetworkAccess);
+ assert_eq!(
+ boosted[0].implied_impact,
+ ImpactCategory::InternalNetworkAccess
+ );
let ratio = boosted[0].score / baseline[0].score;
assert!(
(ratio - LOCAL_LISTENER_BOOST).abs() < 1e-9,
@@ -693,9 +696,7 @@ mod tests {
fn score_threshold_drops_low_score_chains() {
let mut surface = SurfaceMap::new();
surface.nodes.push(entry("app.py", "/r", false));
- surface
- .nodes
- .push(sink("app.py", 20, "open", Cap::FILE_IO));
+ surface.nodes.push(sink("app.py", 20, "open", Cap::FILE_IO));
let e = edge_with(
"app.py",
10,
@@ -724,12 +725,9 @@ mod tests {
surface.nodes.push(entry("routes.py", "/exec", false));
// Sink lives in a helper file the entry handler transitively
// reaches, not the entry file itself.
- surface.nodes.push(sink(
- "helper.py",
- 20,
- "os.system",
- Cap::CODE_EXEC,
- ));
+ surface
+ .nodes
+ .push(sink("helper.py", 20, "os.system", Cap::CODE_EXEC));
let e = edge_with(
"routes.py",
10,
@@ -798,15 +796,9 @@ mod tests {
surface.nodes.push(entry("a.js", "/run", false));
surface.nodes.push(entry("b.js", "/run", false));
surface.nodes.push(entry("c.py", "/run", false));
- surface
- .nodes
- .push(sink("a.js", 7, "eval", Cap::CODE_EXEC));
- surface
- .nodes
- .push(sink("b.js", 7, "eval", Cap::CODE_EXEC));
- surface
- .nodes
- .push(sink("c.py", 7, "eval", Cap::CODE_EXEC));
+ surface.nodes.push(sink("a.js", 7, "eval", Cap::CODE_EXEC));
+ surface.nodes.push(sink("b.js", 7, "eval", Cap::CODE_EXEC));
+ surface.nodes.push(sink("c.py", 7, "eval", Cap::CODE_EXEC));
let edges = vec![
edge_with(
"a.js",
@@ -845,7 +837,11 @@ mod tests {
let mut hashes: Vec = chains.iter().map(|c| c.stable_hash).collect();
hashes.sort();
hashes.dedup();
- assert_eq!(hashes.len(), 3, "surviving chains must have distinct hashes");
+ assert_eq!(
+ hashes.len(),
+ 3,
+ "surviving chains must have distinct hashes"
+ );
}
/// File-affinity gate on `edge_reaches_entry`: an entry only
@@ -858,12 +854,8 @@ mod tests {
let mut surface = SurfaceMap::new();
surface.nodes.push(entry("a.js", "/run", false));
surface.nodes.push(entry("b.js", "/run", false));
- surface
- .nodes
- .push(sink("a.js", 7, "eval", Cap::CODE_EXEC));
- surface
- .nodes
- .push(sink("b.js", 7, "eval", Cap::CODE_EXEC));
+ surface.nodes.push(sink("a.js", 7, "eval", Cap::CODE_EXEC));
+ surface.nodes.push(sink("b.js", 7, "eval", Cap::CODE_EXEC));
// Single finding lives in a.js only. Both entries match
// route+method but only entry@a.js shares the file.
let edges = vec![edge_with(
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index 599a8dd6..3babd6ee 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -389,7 +389,12 @@ pub fn handle_command(
)?;
}
#[cfg(feature = "dynamic")]
- Commands::VerifyFeedback { finding_id, wrong, right, upload } => {
+ Commands::VerifyFeedback {
+ finding_id,
+ wrong,
+ right,
+ upload,
+ } => {
handle_verify_feedback(&finding_id, wrong.as_deref(), right, upload)?;
}
#[cfg(not(feature = "dynamic"))]
@@ -477,8 +482,8 @@ fn handle_verify_feedback(
right: bool,
upload: bool,
) -> crate::errors::NyxResult<()> {
- use std::io::Write;
use std::fs::OpenOptions;
+ use std::io::Write;
let _ = upload; // Upload not yet implemented (reserved).
diff --git a/src/commands/scan.rs b/src/commands/scan.rs
index 6e508feb..bfdd07f4 100644
--- a/src/commands/scan.rs
+++ b/src/commands/scan.rs
@@ -370,7 +370,10 @@ fn load_verify_summaries(
}
};
let root_str = scan_root.to_string_lossy().into_owned();
- Some(Arc::new(crate::summary::merge_summaries(all, Some(&root_str))))
+ Some(Arc::new(crate::summary::merge_summaries(
+ all,
+ Some(&root_str),
+ )))
}
/// Build the whole-program [`crate::callgraph::CallGraph`] from a
@@ -446,60 +449,59 @@ pub fn handle(
let chain_reach_slot: std::sync::OnceLock =
std::sync::OnceLock::new();
- let (mut diags, surface_map): (Vec, crate::surface::SurfaceMap) = if index_mode
- == IndexMode::Off
- {
- scan_filesystem_with_observer(
- &scan_path,
- config,
- show_progress,
- None,
- None,
- None,
- Some(&preview_tier_seen),
- Some(&chain_reach_slot),
- )?
- } else {
- if index_mode == IndexMode::Rebuild || !db_path.exists() {
- tracing::debug!("Scanning filesystem index filesystem");
- crate::commands::index::build_index(
- &project_name,
+ let (mut diags, surface_map): (Vec, crate::surface::SurfaceMap) =
+ if index_mode == IndexMode::Off {
+ scan_filesystem_with_observer(
&scan_path,
- &db_path,
config,
show_progress,
- )?;
- }
+ None,
+ None,
+ None,
+ Some(&preview_tier_seen),
+ Some(&chain_reach_slot),
+ )?
+ } else {
+ if index_mode == IndexMode::Rebuild || !db_path.exists() {
+ tracing::debug!("Scanning filesystem index filesystem");
+ crate::commands::index::build_index(
+ &project_name,
+ &scan_path,
+ &db_path,
+ config,
+ show_progress,
+ )?;
+ }
- let pool = Indexer::init(&db_path)?;
- if config.database.vacuum_on_startup {
- let idx = Indexer::from_pool(&project_name, &pool)?;
- idx.vacuum()?;
- }
- // Indexed scan path: persist + return the SurfaceMap so the
- // Phase 25 chain composer can walk it. `scan_with_index_parallel_observer`
- // already builds and persists the map into the `surface_map`
- // SQLite table; reload it through the same pool so the indexed
- // chain emission matches the non-indexed branch.
- let scan_pool = Arc::clone(&pool);
- let diags = scan_with_index_parallel_observer(
- &project_name,
- scan_pool,
- config,
- show_progress,
- &scan_path,
- None,
- None,
- None,
- Some(&preview_tier_seen),
- Some(&chain_reach_slot),
- )?;
- let surface_map = {
- let idx = Indexer::from_pool(&project_name, &pool)?;
- idx.load_surface_map()?.unwrap_or_default()
+ let pool = Indexer::init(&db_path)?;
+ if config.database.vacuum_on_startup {
+ let idx = Indexer::from_pool(&project_name, &pool)?;
+ idx.vacuum()?;
+ }
+ // Indexed scan path: persist + return the SurfaceMap so the
+ // Phase 25 chain composer can walk it. `scan_with_index_parallel_observer`
+ // already builds and persists the map into the `surface_map`
+ // SQLite table; reload it through the same pool so the indexed
+ // chain emission matches the non-indexed branch.
+ let scan_pool = Arc::clone(&pool);
+ let diags = scan_with_index_parallel_observer(
+ &project_name,
+ scan_pool,
+ config,
+ show_progress,
+ &scan_path,
+ None,
+ None,
+ None,
+ Some(&preview_tier_seen),
+ Some(&chain_reach_slot),
+ )?;
+ let surface_map = {
+ let idx = Indexer::from_pool(&project_name, &pool)?;
+ idx.load_surface_map()?.unwrap_or_default()
+ };
+ (diags, surface_map)
};
- (diags, surface_map)
- };
// Print the Preview-tier banner to stderr once, after file enumeration
// completes and before the console output. Suppressed under --quiet and
@@ -646,8 +648,7 @@ pub fn handle(
// empty (legacy / AST-only paths that never built a call graph),
// the chain layer falls back to file-local reach.
let chain_reach = chain_reach_slot.get();
- let chain_edges =
- crate::chain::findings_to_edges_with_reach(&diags, &surface_map, chain_reach);
+ let chain_edges = crate::chain::findings_to_edges_with_reach(&diags, &surface_map, chain_reach);
let chain_search_cfg = crate::chain::ChainSearchConfig {
max_depth: config.chain.max_depth,
min_score: config.chain.min_score,
@@ -697,21 +698,15 @@ pub fn handle(
let diff_value = verdict_diff
.as_ref()
.map(|d| serde_json::to_value(d).unwrap_or(serde_json::Value::Null));
- let out = crate::output::build_findings_json(
- &diags_for_output,
- &chains,
- diff_value.as_ref(),
- );
+ let out =
+ crate::output::build_findings_json(&diags_for_output, &chains, diff_value.as_ref());
let json = serde_json::to_string(&out)
.map_err(|e| crate::errors::NyxError::Msg(e.to_string()))?;
println!("{json}");
}
OutputFormat::Sarif => {
- let sarif = crate::output::build_sarif_with_chains(
- &diags_for_output,
- &chains,
- &scan_path,
- );
+ let sarif =
+ crate::output::build_sarif_with_chains(&diags_for_output, &chains, &scan_path);
let json = serde_json::to_string_pretty(&sarif)
.map_err(|e| crate::errors::NyxError::Msg(e.to_string()))?;
println!("{json}");
@@ -725,12 +720,7 @@ pub fn handle(
tracing::debug!("Printing to console");
print!(
"{}",
- crate::fmt::render_console(
- &diags_for_output,
- &project_name,
- Some(&stats),
- &chains,
- )
+ crate::fmt::render_console(&diags_for_output, &project_name, Some(&stats), &chains,)
);
if let Some(ref diff) = verdict_diff {
println!("\nBaseline comparison:");
@@ -769,10 +759,7 @@ pub fn handle(
if let (Some(diff), Some(gate_name)) = (&verdict_diff, gate) {
if !crate::baseline::check_gate(diff, gate_name) {
if !suppress_status {
- eprintln!(
- "Gate '{}' violated. Exit code 2.",
- gate_name
- );
+ eprintln!("Gate '{}' violated. Exit code 2.", gate_name);
}
std::process::exit(2);
}
@@ -2235,9 +2222,8 @@ pub(crate) fn scan_filesystem_with_observer(
}
if let Some(out) = chain_reach_out {
- let _ = out.set(
- crate::callgraph::FileReachMap::build(&call_graph).with_scan_root(Some(root)),
- );
+ let _ =
+ out.set(crate::callgraph::FileReachMap::build(&call_graph).with_scan_root(Some(root)));
}
// ── Pass 2: re-run with cross-file global summaries ──────────────────
@@ -2311,15 +2297,14 @@ pub(crate) fn scan_filesystem_with_observer(
// `surface_map` SQLite table. The map is returned alongside the
// diagnostics so consumers (e.g. `nyx surface`) can avoid scanning
// twice.
- let surface_map = crate::surface::build::build_surface_map(
- &crate::surface::build::SurfaceBuildInputs {
+ let surface_map =
+ crate::surface::build::build_surface_map(&crate::surface::build::SurfaceBuildInputs {
files: &all_paths,
scan_root: Some(root),
global_summaries: &gs,
call_graph: &call_graph,
config: cfg,
- },
- );
+ });
if let Some(p) = progress {
p.record_pass2_ms(pass2_start.elapsed().as_millis() as u64);
}
@@ -3142,15 +3127,14 @@ pub fn scan_with_index_parallel_observer(
// view. Errors here are logged but not propagated — the surface
// map is an additive Phase F deliverable, not a scan gate.
{
- let surface_map = crate::surface::build::build_surface_map(
- &crate::surface::build::SurfaceBuildInputs {
+ let surface_map =
+ crate::surface::build::build_surface_map(&crate::surface::build::SurfaceBuildInputs {
files: &files,
scan_root: Some(scan_root),
global_summaries: &global_summaries,
call_graph: &call_graph,
config: cfg,
- },
- );
+ });
let mut idx = Indexer::from_pool(project, &pool)?;
if let Err(e) = idx.replace_surface_map(&surface_map) {
tracing::warn!("failed to persist surface_map: {e}");
diff --git a/src/commands/surface.rs b/src/commands/surface.rs
index 42faa759..04720504 100644
--- a/src/commands/surface.rs
+++ b/src/commands/surface.rs
@@ -100,12 +100,13 @@ pub fn load_or_build(
) -> NyxResult {
if let Ok((project, db_path)) = get_project_info(scan_root, database_dir)
&& db_path.exists()
- && let Ok(pool) = Indexer::init(&db_path)
- && let Ok(idx) = Indexer::from_pool(&project, &pool)
- && let Ok(Some(map)) = idx.load_surface_map()
- && !map.nodes.is_empty() {
- return Ok(map);
- }
+ && let Ok(pool) = Indexer::init(&db_path)
+ && let Ok(idx) = Indexer::from_pool(&project, &pool)
+ && let Ok(Some(map)) = idx.load_surface_map()
+ && !map.nodes.is_empty()
+ {
+ return Ok(map);
+ }
build_from_filesystem(scan_root, config)
}
@@ -151,11 +152,7 @@ fn build_full_from_filesystem(scan_root: &Path, config: &Config) -> NyxResult GlobalSummaries {
+fn build_summaries_inline(files: &[PathBuf], scan_root: &Path, config: &Config) -> GlobalSummaries {
let root_str = scan_root.to_string_lossy().into_owned();
let mg = config.module_graph.as_deref();
files
@@ -279,7 +276,8 @@ pub fn render_text(map: &SurfaceMap, scan_root: Option<&Path>) -> String {
}
for &i in indices {
match &map.nodes[i] {
- SurfaceNode::DataStore(_) | SurfaceNode::ExternalService(_)
+ SurfaceNode::DataStore(_)
+ | SurfaceNode::ExternalService(_)
| SurfaceNode::DangerousLocal(_) => {
if !entry_indices.is_empty() {
continue;
@@ -456,10 +454,18 @@ pub fn render_dot(map: &SurfaceMap) -> String {
escape_dot(&ep.handler_name),
),
"box",
- if ep.auth_required { "#3aa57c" } else { "#3072c4" },
+ if ep.auth_required {
+ "#3aa57c"
+ } else {
+ "#3072c4"
+ },
),
SurfaceNode::DataStore(ds) => (
- format!("DataStore ({})\\n{}", ds_kind_str(ds.kind), escape_dot(&ds.label)),
+ format!(
+ "DataStore ({})\\n{}",
+ ds_kind_str(ds.kind),
+ escape_dot(&ds.label)
+ ),
"cylinder",
"#b07a18",
),
@@ -543,9 +549,7 @@ fn render_svg(map: &SurfaceMap) -> NyxResult> {
mod tests {
use super::*;
use crate::entry_points::HttpMethod;
- use crate::surface::{
- EntryPoint, Framework, SourceLocation, SurfaceEdge, SurfaceNode,
- };
+ use crate::surface::{EntryPoint, Framework, SourceLocation, SurfaceEdge, SurfaceNode};
fn flask_fixture_map() -> SurfaceMap {
let mut map = SurfaceMap::new();
@@ -598,12 +602,13 @@ mod tests {
#[test]
fn text_render_groups_reaches_under_entry() {
let mut m = flask_fixture_map();
- m.nodes
- .push(SurfaceNode::DangerousLocal(crate::surface::DangerousLocal {
+ m.nodes.push(SurfaceNode::DangerousLocal(
+ crate::surface::DangerousLocal {
location: SourceLocation::new("app.py", 12, 1),
function_name: "eval".into(),
cap_bits: crate::labels::Cap::CODE_EXEC.bits(),
- }));
+ },
+ ));
// Build edge after canonicalize so indices are stable.
m.canonicalize();
let ep_idx = m
@@ -657,10 +662,7 @@ mod tests {
let canon = project_dir.canonicalize().unwrap();
let files = collect_files(&canon, &cfg).unwrap();
let summaries = build_summaries_inline(&files, &canon, &cfg);
- let names: Vec = summaries
- .iter()
- .map(|(k, _)| k.qualified_name())
- .collect();
+ let names: Vec = summaries.iter().map(|(k, _)| k.qualified_name()).collect();
assert!(
names.iter().any(|n| n.ends_with("run")),
"summaries should contain `run`, got {names:?}"
diff --git a/src/database.rs b/src/database.rs
index 90db6642..21e55611 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -1913,10 +1913,7 @@ pub mod index {
/// per project. The map is canonicalised before serialisation so
/// `replace_surface_map` + `load_surface_map` round-trip is
/// byte-identical for structurally identical maps.
- pub fn replace_surface_map(
- &mut self,
- map: &crate::surface::SurfaceMap,
- ) -> NyxResult<()> {
+ pub fn replace_surface_map(&mut self, map: &crate::surface::SurfaceMap) -> NyxResult<()> {
let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as i64;
let mut canon = map.clone();
let bytes = canon
diff --git a/src/dynamic/build_sandbox.rs b/src/dynamic/build_sandbox.rs
index 0c156e34..93c9f669 100644
--- a/src/dynamic/build_sandbox.rs
+++ b/src/dynamic/build_sandbox.rs
@@ -42,7 +42,11 @@ pub fn prepare_rust(spec: &HarnessSpec, workdir: &Path) -> Result Result Result<(), String> {
@@ -86,10 +93,14 @@ fn try_build_rust_binary(workdir: &Path, binary_dest: &Path) -> Result<(), Strin
.env("PATH", std::env::var("PATH").unwrap_or_default())
.env("HOME", std::env::var("HOME").unwrap_or_default())
// Inherit CARGO_HOME so the local registry cache is reused.
- .env("CARGO_HOME", std::env::var("CARGO_HOME").unwrap_or_else(|_| {
- dirs_next_cargo_home()
- }))
- .env("RUSTUP_HOME", std::env::var("RUSTUP_HOME").unwrap_or_default())
+ .env(
+ "CARGO_HOME",
+ std::env::var("CARGO_HOME").unwrap_or_else(|_| dirs_next_cargo_home()),
+ )
+ .env(
+ "RUSTUP_HOME",
+ std::env::var("RUSTUP_HOME").unwrap_or_default(),
+ )
.output()
.map_err(|e| format!("cargo build: {e}"))?;
@@ -101,8 +112,7 @@ fn try_build_rust_binary(workdir: &Path, binary_dest: &Path) -> Result<(), Strin
// Copy binary to cache location.
let compiled = workdir.join("target").join("release").join("nyx_harness");
if compiled.exists() {
- std::fs::copy(&compiled, binary_dest)
- .map_err(|e| format!("copy binary: {e}"))?;
+ std::fs::copy(&compiled, binary_dest).map_err(|e| format!("copy binary: {e}"))?;
}
Ok(())
@@ -137,7 +147,10 @@ fn compute_rust_lockfile_hash(workdir: &Path) -> String {
h.update(&content);
}
let out = h.finalize();
- format!("{:016x}", u64::from_le_bytes(out.as_bytes()[..8].try_into().unwrap()))
+ format!(
+ "{:016x}",
+ u64::from_le_bytes(out.as_bytes()[..8].try_into().unwrap())
+ )
}
/// Result of a successful build.
@@ -168,10 +181,7 @@ impl From for BuildError {
///
/// If a compatible cache entry exists, returns it immediately. Otherwise
/// builds in isolation and caches the result.
-pub fn prepare_python(
- spec: &HarnessSpec,
- workdir: &Path,
-) -> Result {
+pub fn prepare_python(spec: &HarnessSpec, workdir: &Path) -> Result {
let lockfile_hash = compute_lockfile_hash(workdir);
let cache_path = build_cache_path(&lockfile_hash, "python", &spec.toolchain_id)?;
@@ -217,11 +227,7 @@ pub fn prepare_python(
})
}
-fn try_build_venv(
- venv_path: &Path,
- workdir: &Path,
- spec: &HarnessSpec,
-) -> Result<(), String> {
+fn try_build_venv(venv_path: &Path, workdir: &Path, spec: &HarnessSpec) -> Result<(), String> {
// Find python binary.
let python = python_binary(spec);
@@ -262,10 +268,7 @@ fn try_build_venv(
fn python_binary(spec: &HarnessSpec) -> String {
// Try the pinned version first; fall back to python3.
- let ver = spec
- .toolchain_id
- .strip_prefix("python-")
- .unwrap_or("3");
+ let ver = spec.toolchain_id.strip_prefix("python-").unwrap_or("3");
let candidate = format!("python{ver}");
if which_exists(&candidate) {
return candidate;
@@ -290,7 +293,10 @@ fn compute_lockfile_hash(workdir: &Path) -> String {
}
}
let out = h.finalize();
- format!("{:016x}", u64::from_le_bytes(out.as_bytes()[..8].try_into().unwrap()))
+ format!(
+ "{:016x}",
+ u64::from_le_bytes(out.as_bytes()[..8].try_into().unwrap())
+ )
}
fn build_cache_path(
@@ -308,9 +314,7 @@ fn build_cache_path(
"cannot determine cache dir",
))
})?;
- dirs.cache_dir()
- .join("dynamic")
- .join("build-cache")
+ dirs.cache_dir().join("dynamic").join("build-cache")
};
let name = format!("{lockfile_hash}-{language}-{toolchain_id}");
@@ -366,7 +370,9 @@ pub fn prepare_node(spec: &HarnessSpec, workdir: &Path) -> Result 0 {
- std::thread::sleep(std::time::Duration::from_secs(BACKOFF[attempt as usize - 1]));
+ std::thread::sleep(std::time::Duration::from_secs(
+ BACKOFF[attempt as usize - 1],
+ ));
}
match try_npm_install(workdir) {
Ok(()) => {
@@ -389,7 +395,10 @@ pub fn prepare_node(spec: &HarnessSpec, workdir: &Path) -> Result Result<(), String> {
@@ -430,14 +439,22 @@ fn copy_dir_all(src: &Path, dst: &Path) -> std::io::Result<()> {
fn compute_node_lockfile_hash(workdir: &Path) -> String {
let mut h = Hasher::new();
- for fname in &["package.json", "package-lock.json", "yarn.lock", "pnpm-lock.yaml"] {
+ for fname in &[
+ "package.json",
+ "package-lock.json",
+ "yarn.lock",
+ "pnpm-lock.yaml",
+ ] {
if let Ok(content) = std::fs::read(workdir.join(fname)) {
h.update(fname.as_bytes());
h.update(&content);
}
}
let out = h.finalize();
- format!("{:016x}", u64::from_le_bytes(out.as_bytes()[..8].try_into().unwrap()))
+ format!(
+ "{:016x}",
+ u64::from_le_bytes(out.as_bytes()[..8].try_into().unwrap())
+ )
}
// ── Go build sandbox ──────────────────────────────────────────────────────────
@@ -470,7 +487,9 @@ pub fn prepare_go(spec: &HarnessSpec, workdir: &Path) -> Result 0 {
- std::thread::sleep(std::time::Duration::from_secs(BACKOFF[attempt as usize - 1]));
+ std::thread::sleep(std::time::Duration::from_secs(
+ BACKOFF[attempt as usize - 1],
+ ));
}
let _ = std::fs::remove_dir_all(&cache_path);
std::fs::create_dir_all(&cache_path)?;
@@ -490,23 +509,41 @@ pub fn prepare_go(spec: &HarnessSpec, workdir: &Path) -> Result Result<(), String> {
let go_bin = std::env::var("NYX_GO_BIN").unwrap_or_else(|_| "go".to_owned());
let output = Command::new(&go_bin)
- .args(["build", "-o", binary_dest.to_str().unwrap_or("nyx_harness"), "."])
+ .args([
+ "build",
+ "-o",
+ binary_dest.to_str().unwrap_or("nyx_harness"),
+ ".",
+ ])
.current_dir(workdir)
.env_clear()
.env("PATH", std::env::var("PATH").unwrap_or_default())
.env("HOME", std::env::var("HOME").unwrap_or_default())
- .env("GOPATH", std::env::var("GOPATH").unwrap_or_else(|_| {
- std::env::var("HOME").map(|h| format!("{h}/go")).unwrap_or_else(|_| "/tmp/go".to_owned())
- }))
- .env("GOMODCACHE", std::env::var("GOMODCACHE").unwrap_or_else(|_| {
- std::env::var("HOME").map(|h| format!("{h}/go/pkg/mod")).unwrap_or_else(|_| "/tmp/gomod".to_owned())
- }))
+ .env(
+ "GOPATH",
+ std::env::var("GOPATH").unwrap_or_else(|_| {
+ std::env::var("HOME")
+ .map(|h| format!("{h}/go"))
+ .unwrap_or_else(|_| "/tmp/go".to_owned())
+ }),
+ )
+ .env(
+ "GOMODCACHE",
+ std::env::var("GOMODCACHE").unwrap_or_else(|_| {
+ std::env::var("HOME")
+ .map(|h| format!("{h}/go/pkg/mod"))
+ .unwrap_or_else(|_| "/tmp/gomod".to_owned())
+ }),
+ )
.output()
.map_err(|e| format!("go build: {e}"))?;
@@ -529,7 +566,10 @@ fn compute_go_source_hash(workdir: &Path) -> String {
h.update(&content);
}
let out = h.finalize();
- format!("{:016x}", u64::from_le_bytes(out.as_bytes()[..8].try_into().unwrap()))
+ format!(
+ "{:016x}",
+ u64::from_le_bytes(out.as_bytes()[..8].try_into().unwrap())
+ )
}
// ── Java build sandbox ────────────────────────────────────────────────────────
@@ -592,7 +632,9 @@ pub fn prepare_java(spec: &HarnessSpec, workdir: &Path) -> Result 0 {
- std::thread::sleep(std::time::Duration::from_secs(BACKOFF[attempt as usize - 1]));
+ std::thread::sleep(std::time::Duration::from_secs(
+ BACKOFF[attempt as usize - 1],
+ ));
}
match try_compile_java(workdir, &cache_path, target_release) {
Ok(()) => {
@@ -622,7 +664,10 @@ pub fn prepare_java(spec: &HarnessSpec, workdir: &Path) -> Result Option {
}
}
-fn try_compile_java(workdir: &Path, cache_path: &Path, target_release: Option) -> Result<(), String> {
+fn try_compile_java(
+ workdir: &Path,
+ cache_path: &Path,
+ target_release: Option,
+) -> Result<(), String> {
let javac = std::env::var("NYX_JAVAC_BIN").unwrap_or_else(|_| "javac".to_owned());
// If the harness emitter shipped a `pom.xml`, stage Maven-resolved
@@ -792,9 +841,10 @@ fn collect_class_files(root: &Path) -> Vec {
if path.is_dir() {
stack.push(path);
} else if path.extension().map(|e| e == "class").unwrap_or(false)
- && let Ok(rel) = path.strip_prefix(root) {
- out.push(rel.to_path_buf());
- }
+ && let Ok(rel) = path.strip_prefix(root)
+ {
+ out.push(rel.to_path_buf());
+ }
}
}
out.sort();
@@ -826,7 +876,10 @@ fn compute_java_source_hash(workdir: &Path, target_release: Option) -> Stri
h.update(b":release=host");
}
let out = h.finalize();
- format!("{:016x}", u64::from_le_bytes(out.as_bytes()[..8].try_into().unwrap()))
+ format!(
+ "{:016x}",
+ u64::from_le_bytes(out.as_bytes()[..8].try_into().unwrap())
+ )
}
// ── PHP build sandbox ─────────────────────────────────────────────────────────
@@ -869,7 +922,9 @@ pub fn prepare_php(spec: &HarnessSpec, workdir: &Path) -> Result 0 {
- std::thread::sleep(std::time::Duration::from_secs(BACKOFF[attempt as usize - 1]));
+ std::thread::sleep(std::time::Duration::from_secs(
+ BACKOFF[attempt as usize - 1],
+ ));
}
match try_composer_install(workdir) {
Ok(()) => {
@@ -892,7 +947,10 @@ pub fn prepare_php(spec: &HarnessSpec, workdir: &Path) -> Result Result<(), String> {
@@ -922,7 +980,10 @@ fn compute_php_lockfile_hash(workdir: &Path) -> String {
}
}
let out = h.finalize();
- format!("{:016x}", u64::from_le_bytes(out.as_bytes()[..8].try_into().unwrap()))
+ format!(
+ "{:016x}",
+ u64::from_le_bytes(out.as_bytes()[..8].try_into().unwrap())
+ )
}
// ── C build sandbox ───────────────────────────────────────────────────────────
@@ -959,7 +1020,9 @@ pub fn prepare_c(
for attempt in 0..MAX_ATTEMPTS {
if attempt > 0 {
- std::thread::sleep(std::time::Duration::from_secs(BACKOFF[attempt as usize - 1]));
+ std::thread::sleep(std::time::Duration::from_secs(
+ BACKOFF[attempt as usize - 1],
+ ));
}
let _ = std::fs::remove_dir_all(&cache_path);
std::fs::create_dir_all(&cache_path)?;
@@ -979,7 +1042,10 @@ pub fn prepare_c(
}
}
- Err(BuildError::BuildFailed { stderr: last_err, attempts: MAX_ATTEMPTS })
+ Err(BuildError::BuildFailed {
+ stderr: last_err,
+ attempts: MAX_ATTEMPTS,
+ })
}
fn try_build_c_binary(workdir: &Path, binary_dest: &Path, static_link: bool) -> Result<(), String> {
@@ -1032,7 +1098,12 @@ pub(crate) fn static_link_env_override() -> bool {
)
}
-fn run_cc(cc_bin: &str, workdir: &Path, binary_dest: &Path, leading_flags: &[&str]) -> Result<(), String> {
+fn run_cc(
+ cc_bin: &str,
+ workdir: &Path,
+ binary_dest: &Path,
+ leading_flags: &[&str],
+) -> Result<(), String> {
let binary_str = binary_dest.to_str().unwrap_or("nyx_harness");
let mut args: Vec<&str> = leading_flags.to_vec();
args.extend(["-o", binary_str, "main.c"]);
@@ -1067,7 +1138,10 @@ fn compute_c_source_hash(workdir: &Path, static_link: bool) -> String {
h.update(b"static");
}
let out = h.finalize();
- format!("{:016x}", u64::from_le_bytes(out.as_bytes()[..8].try_into().unwrap()))
+ format!(
+ "{:016x}",
+ u64::from_le_bytes(out.as_bytes()[..8].try_into().unwrap())
+ )
}
// ── C++ build sandbox ─────────────────────────────────────────────────────────
@@ -1093,7 +1167,9 @@ pub fn prepare_cpp(spec: &HarnessSpec, workdir: &Path) -> Result 0 {
- std::thread::sleep(std::time::Duration::from_secs(BACKOFF[attempt as usize - 1]));
+ std::thread::sleep(std::time::Duration::from_secs(
+ BACKOFF[attempt as usize - 1],
+ ));
}
let _ = std::fs::remove_dir_all(&cache_path);
std::fs::create_dir_all(&cache_path)?;
@@ -1113,7 +1189,10 @@ pub fn prepare_cpp(spec: &HarnessSpec, workdir: &Path) -> Result Result<(), String> {
@@ -1122,7 +1201,14 @@ fn try_build_cpp_binary(workdir: &Path, binary_dest: &Path) -> Result<(), String
"c++".to_owned()
});
let output = Command::new(&cxx_bin)
- .args(["-O0", "-g", "-std=c++17", "-o", binary_dest.to_str().unwrap_or("nyx_harness"), "main.cpp"])
+ .args([
+ "-O0",
+ "-g",
+ "-std=c++17",
+ "-o",
+ binary_dest.to_str().unwrap_or("nyx_harness"),
+ "main.cpp",
+ ])
.current_dir(workdir)
.env_clear()
.env("PATH", std::env::var("PATH").unwrap_or_default())
@@ -1145,7 +1231,10 @@ fn compute_cpp_source_hash(workdir: &Path) -> String {
}
}
let out = h.finalize();
- format!("{:016x}", u64::from_le_bytes(out.as_bytes()[..8].try_into().unwrap()))
+ format!(
+ "{:016x}",
+ u64::from_le_bytes(out.as_bytes()[..8].try_into().unwrap())
+ )
}
// ── Uniform per-language build dispatch (Phase 26 — composite chains) ────────
@@ -1251,10 +1340,14 @@ fn start_isolated_build_container(
network_none: bool,
) -> bool {
let mut args: Vec<&str> = vec![
- "run", "-d", "--rm",
- "--name", name,
+ "run",
+ "-d",
+ "--rm",
+ "--name",
+ name,
"--cap-drop=ALL",
- "--security-opt", "no-new-privileges:true",
+ "--security-opt",
+ "no-new-privileges:true",
];
if network_none {
args.extend_from_slice(&["--network", "none"]);
@@ -1319,16 +1412,22 @@ pub fn prepare_rust_in_docker(workdir: &Path) -> Result<(), String> {
return Err("failed to start rust:slim build container; image may not be available".into());
}
- let _guard = BuildContainerGuard { docker: docker.clone(), name: container.clone() };
+ let _guard = BuildContainerGuard {
+ docker: docker.clone(),
+ name: container.clone(),
+ };
copy_workdir_to_build_container(&docker, workdir, &container, "/build");
// CARGO_NET_OFFLINE prevents any registry contact; std lib is pre-built in the image.
let _ = std::process::Command::new(&docker)
.args([
"exec",
- "-e", "CARGO_NET_OFFLINE=true",
+ "-e",
+ "CARGO_NET_OFFLINE=true",
&container,
- "sh", "-c", "cd /build && cargo build --release 2>&1",
+ "sh",
+ "-c",
+ "cd /build && cargo build --release 2>&1",
])
.output();
@@ -1347,10 +1446,15 @@ pub fn prepare_node_in_docker(workdir: &Path) -> Result<(), String> {
let container = build_container_id("nodebuild", workdir);
if !start_isolated_build_container(&docker, &container, "node:20-slim", true) {
- return Err("failed to start node:20-slim build container; image may not be available".into());
+ return Err(
+ "failed to start node:20-slim build container; image may not be available".into(),
+ );
}
- let _guard = BuildContainerGuard { docker: docker.clone(), name: container.clone() };
+ let _guard = BuildContainerGuard {
+ docker: docker.clone(),
+ name: container.clone(),
+ };
copy_workdir_to_build_container(&docker, workdir, &container, "/build");
// npm install may fail if the registry is unreachable (--network none), but the
@@ -1359,7 +1463,8 @@ pub fn prepare_node_in_docker(workdir: &Path) -> Result<(), String> {
.args([
"exec",
&container,
- "sh", "-c",
+ "sh",
+ "-c",
"cd /build && npm install --no-save --no-audit --no-fund 2>&1",
])
.output();
@@ -1379,20 +1484,29 @@ pub fn prepare_go_in_docker(workdir: &Path) -> Result<(), String> {
let container = build_container_id("gobuild", workdir);
if !start_isolated_build_container(&docker, &container, "golang:1.21-slim", true) {
- return Err("failed to start golang:1.21-slim build container; image may not be available".into());
+ return Err(
+ "failed to start golang:1.21-slim build container; image may not be available".into(),
+ );
}
- let _guard = BuildContainerGuard { docker: docker.clone(), name: container.clone() };
+ let _guard = BuildContainerGuard {
+ docker: docker.clone(),
+ name: container.clone(),
+ };
copy_workdir_to_build_container(&docker, workdir, &container, "/build");
// GOPROXY=off prevents module downloads; std library is pre-compiled in the image.
let _ = std::process::Command::new(&docker)
.args([
"exec",
- "-e", "GOPROXY=off",
- "-e", "GONOSUMDB=*",
+ "-e",
+ "GOPROXY=off",
+ "-e",
+ "GONOSUMDB=*",
&container,
- "sh", "-c", "cd /build && go build ./... 2>&1",
+ "sh",
+ "-c",
+ "cd /build && go build ./... 2>&1",
])
.output();
@@ -1413,26 +1527,26 @@ pub fn prepare_java_in_docker(workdir: &Path) -> Result<(), String> {
// Bridge network: Maven must download exec-maven-plugin from Maven Central.
// Filesystem isolation still holds: /tmp inside the container is private.
- if !start_isolated_build_container(
- &docker,
- &container,
- "maven:3.9-eclipse-temurin-21",
- false,
- ) {
+ if !start_isolated_build_container(&docker, &container, "maven:3.9-eclipse-temurin-21", false) {
return Err(
"failed to start maven:3.9-eclipse-temurin-21 build container; image may not be available"
.into(),
);
}
- let _guard = BuildContainerGuard { docker: docker.clone(), name: container.clone() };
+ let _guard = BuildContainerGuard {
+ docker: docker.clone(),
+ name: container.clone(),
+ };
copy_workdir_to_build_container(&docker, workdir, &container, "/build");
let _ = std::process::Command::new(&docker)
.args([
"exec",
&container,
- "sh", "-c", "cd /build && mvn --no-transfer-progress validate 2>&1",
+ "sh",
+ "-c",
+ "cd /build && mvn --no-transfer-progress validate 2>&1",
])
.output();
@@ -1451,10 +1565,15 @@ pub fn prepare_php_in_docker(workdir: &Path) -> Result<(), String> {
let container = build_container_id("phpbuild", workdir);
if !start_isolated_build_container(&docker, &container, "composer:2", true) {
- return Err("failed to start composer:2 build container; image may not be available".into());
+ return Err(
+ "failed to start composer:2 build container; image may not be available".into(),
+ );
}
- let _guard = BuildContainerGuard { docker: docker.clone(), name: container.clone() };
+ let _guard = BuildContainerGuard {
+ docker: docker.clone(),
+ name: container.clone(),
+ };
copy_workdir_to_build_container(&docker, workdir, &container, "/build");
// Empty require{} means no packages to fetch; post-install-cmd still fires.
@@ -1462,7 +1581,8 @@ pub fn prepare_php_in_docker(workdir: &Path) -> Result<(), String> {
.args([
"exec",
&container,
- "sh", "-c",
+ "sh",
+ "-c",
"cd /build && composer install --no-dev --no-interaction --prefer-dist 2>&1",
])
.output();
@@ -1519,11 +1639,7 @@ mod tests {
#[test]
fn java_source_hash_differs_across_target_release() {
let dir = tempfile::TempDir::new().unwrap();
- std::fs::write(
- dir.path().join("Vuln.java"),
- "public class Vuln {}\n",
- )
- .unwrap();
+ std::fs::write(dir.path().join("Vuln.java"), "public class Vuln {}\n").unwrap();
let h_none = compute_java_source_hash(dir.path(), None);
let h17 = compute_java_source_hash(dir.path(), Some(17));
let h21 = compute_java_source_hash(dir.path(), Some(21));
@@ -1568,7 +1684,10 @@ mod tests {
copy_dir_all(src.path(), dst.path()).unwrap();
assert_eq!(std::fs::read(dst.path().join("a.txt")).unwrap(), b"hello");
- assert_eq!(std::fs::read(dst.path().join("sub").join("b.txt")).unwrap(), b"world");
+ assert_eq!(
+ std::fs::read(dst.path().join("sub").join("b.txt")).unwrap(),
+ b"world"
+ );
}
#[test]
@@ -1760,7 +1879,11 @@ mod tests {
let result = dispatch_prepare(&spec, dir.path(), ProcessHardeningProfile::Standard)
.expect("TypeScript dispatch must succeed on a workdir with no package.json");
- assert_eq!(result.lang, Lang::TypeScript, "lang field must echo the spec's");
+ assert_eq!(
+ result.lang,
+ Lang::TypeScript,
+ "lang field must echo the spec's"
+ );
assert!(
!result.cache_hit,
"first dispatch on a fresh cache must be a cache miss; got {result:?}",
diff --git a/src/dynamic/corpus.rs b/src/dynamic/corpus.rs
index 6b7620b8..476b6163 100644
--- a/src/dynamic/corpus.rs
+++ b/src/dynamic/corpus.rs
@@ -67,9 +67,9 @@ mod xss;
mod xxe;
pub use registry::{
- audit_marker_collisions, benign_payload_for, benign_payload_for_lang, materialise_bytes,
- payloads_for, payloads_for_lang, resolve_benign_control, resolve_benign_control_lang,
- CORPUS, CORPUS_UNSUPPORTED_LANG_NEUTRAL,
+ CORPUS, CORPUS_UNSUPPORTED_LANG_NEUTRAL, audit_marker_collisions, benign_payload_for,
+ benign_payload_for_lang, materialise_bytes, payloads_for, payloads_for_lang,
+ resolve_benign_control, resolve_benign_control_lang,
};
/// Re-exported canonical [`Oracle`] type.
diff --git a/src/dynamic/corpus/audit.rs b/src/dynamic/corpus/audit.rs
index 39401394..ce413d6b 100644
--- a/src/dynamic/corpus/audit.rs
+++ b/src/dynamic/corpus/audit.rs
@@ -19,8 +19,8 @@
//! The runtime `corpus_registry::audit` test mirrors both checks so
//! failure surfaces in `cargo test` output, not just `cargo build`.
-use super::registry::{CORPUS, CORPUS_UNSUPPORTED_LANG_NEUTRAL};
use super::CuratedPayload;
+use super::registry::{CORPUS, CORPUS_UNSUPPORTED_LANG_NEUTRAL};
use crate::labels::Cap;
/// Byte-level equality for `&'static str` usable in const eval.
@@ -121,9 +121,7 @@ pub fn audit_benign_controls_runtime() -> Result<(), String> {
}
match p.benign_control {
Some(r) => {
- let found = slice
- .iter()
- .any(|q| q.is_benign && q.label == r.label);
+ let found = slice.iter().any(|q| q.is_benign && q.label == r.label);
if !found {
return Err(format!(
"({:?}, {:?}) vuln payload {:?} references missing \
@@ -180,17 +178,18 @@ pub fn audit_benign_label_uniqueness_runtime() -> Result<(), String> {
continue;
}
if let Some(prev_lang) = bucket.insert(p.label, lang)
- && prev_lang != lang {
- return Err(format!(
- "benign label {:?} for cap {:#x} is registered in both \
+ && prev_lang != lang
+ {
+ return Err(format!(
+ "benign label {:?} for cap {:#x} is registered in both \
{:?} and {:?} — lang-agnostic resolve_benign_control \
could match the wrong language",
- p.label,
- cap.bits(),
- prev_lang,
- lang,
- ));
- }
+ p.label,
+ cap.bits(),
+ prev_lang,
+ lang,
+ ));
+ }
}
}
Ok(())
@@ -206,7 +205,6 @@ mod corpus_registry {
fn audit() {
audit_benign_controls_runtime().expect("benign_control audit failed");
audit_cap_coverage_runtime().expect("cap coverage audit failed");
- audit_benign_label_uniqueness_runtime()
- .expect("benign label uniqueness audit failed");
+ audit_benign_label_uniqueness_runtime().expect("benign label uniqueness audit failed");
}
}
diff --git a/src/dynamic/corpus/cmdi/c.rs b/src/dynamic/corpus/cmdi/c.rs
index aadeccd5..0abf7f37 100644
--- a/src/dynamic/corpus/cmdi/c.rs
+++ b/src/dynamic/corpus/cmdi/c.rs
@@ -19,7 +19,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
],
oob_nonce_slot: false,
probe_predicates: &[],
- benign_control: Some(PayloadRef { label: "cmdi-benign-c" }),
+ benign_control: Some(PayloadRef {
+ label: "cmdi-benign-c",
+ }),
no_benign_control_rationale: None,
},
CuratedPayload {
diff --git a/src/dynamic/corpus/cmdi/cpp.rs b/src/dynamic/corpus/cmdi/cpp.rs
index 462be343..0dca6aeb 100644
--- a/src/dynamic/corpus/cmdi/cpp.rs
+++ b/src/dynamic/corpus/cmdi/cpp.rs
@@ -22,7 +22,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
],
oob_nonce_slot: false,
probe_predicates: &[],
- benign_control: Some(PayloadRef { label: "cmdi-benign-cpp" }),
+ benign_control: Some(PayloadRef {
+ label: "cmdi-benign-cpp",
+ }),
no_benign_control_rationale: None,
},
CuratedPayload {
diff --git a/src/dynamic/corpus/cmdi/go.rs b/src/dynamic/corpus/cmdi/go.rs
index d2ea660a..cfb0fad0 100644
--- a/src/dynamic/corpus/cmdi/go.rs
+++ b/src/dynamic/corpus/cmdi/go.rs
@@ -19,7 +19,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
],
oob_nonce_slot: false,
probe_predicates: &[],
- benign_control: Some(PayloadRef { label: "cmdi-benign-go" }),
+ benign_control: Some(PayloadRef {
+ label: "cmdi-benign-go",
+ }),
no_benign_control_rationale: None,
},
CuratedPayload {
diff --git a/src/dynamic/corpus/cmdi/java.rs b/src/dynamic/corpus/cmdi/java.rs
index e6991e62..62d44630 100644
--- a/src/dynamic/corpus/cmdi/java.rs
+++ b/src/dynamic/corpus/cmdi/java.rs
@@ -17,7 +17,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
],
oob_nonce_slot: false,
probe_predicates: &[],
- benign_control: Some(PayloadRef { label: "cmdi-benign-java" }),
+ benign_control: Some(PayloadRef {
+ label: "cmdi-benign-java",
+ }),
no_benign_control_rationale: None,
},
CuratedPayload {
diff --git a/src/dynamic/corpus/cmdi/javascript.rs b/src/dynamic/corpus/cmdi/javascript.rs
index c7d20b0a..6539f46f 100644
--- a/src/dynamic/corpus/cmdi/javascript.rs
+++ b/src/dynamic/corpus/cmdi/javascript.rs
@@ -17,7 +17,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
],
oob_nonce_slot: false,
probe_predicates: &[],
- benign_control: Some(PayloadRef { label: "cmdi-benign-javascript" }),
+ benign_control: Some(PayloadRef {
+ label: "cmdi-benign-javascript",
+ }),
no_benign_control_rationale: None,
},
CuratedPayload {
diff --git a/src/dynamic/corpus/cmdi/php.rs b/src/dynamic/corpus/cmdi/php.rs
index 071150f6..8b2a560e 100644
--- a/src/dynamic/corpus/cmdi/php.rs
+++ b/src/dynamic/corpus/cmdi/php.rs
@@ -17,7 +17,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
],
oob_nonce_slot: false,
probe_predicates: &[],
- benign_control: Some(PayloadRef { label: "cmdi-benign-php" }),
+ benign_control: Some(PayloadRef {
+ label: "cmdi-benign-php",
+ }),
no_benign_control_rationale: None,
},
CuratedPayload {
diff --git a/src/dynamic/corpus/cmdi/python.rs b/src/dynamic/corpus/cmdi/python.rs
index bdb99ffe..29bb2145 100644
--- a/src/dynamic/corpus/cmdi/python.rs
+++ b/src/dynamic/corpus/cmdi/python.rs
@@ -22,7 +22,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
],
oob_nonce_slot: false,
probe_predicates: &[],
- benign_control: Some(PayloadRef { label: "cmdi-benign-python" }),
+ benign_control: Some(PayloadRef {
+ label: "cmdi-benign-python",
+ }),
no_benign_control_rationale: None,
},
CuratedPayload {
diff --git a/src/dynamic/corpus/cmdi/ruby.rs b/src/dynamic/corpus/cmdi/ruby.rs
index bf1440c5..71eaa155 100644
--- a/src/dynamic/corpus/cmdi/ruby.rs
+++ b/src/dynamic/corpus/cmdi/ruby.rs
@@ -18,7 +18,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
],
oob_nonce_slot: false,
probe_predicates: &[],
- benign_control: Some(PayloadRef { label: "cmdi-benign-ruby" }),
+ benign_control: Some(PayloadRef {
+ label: "cmdi-benign-ruby",
+ }),
no_benign_control_rationale: None,
},
CuratedPayload {
diff --git a/src/dynamic/corpus/cmdi/rust.rs b/src/dynamic/corpus/cmdi/rust.rs
index f8bbb52c..b37129db 100644
--- a/src/dynamic/corpus/cmdi/rust.rs
+++ b/src/dynamic/corpus/cmdi/rust.rs
@@ -22,7 +22,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
],
oob_nonce_slot: false,
probe_predicates: &[],
- benign_control: Some(PayloadRef { label: "cmdi-benign" }),
+ benign_control: Some(PayloadRef {
+ label: "cmdi-benign",
+ }),
no_benign_control_rationale: None,
},
// Benign control: plain text that should never produce the cmdi marker.
diff --git a/src/dynamic/corpus/cmdi/typescript.rs b/src/dynamic/corpus/cmdi/typescript.rs
index 3245614d..7591b4e6 100644
--- a/src/dynamic/corpus/cmdi/typescript.rs
+++ b/src/dynamic/corpus/cmdi/typescript.rs
@@ -17,7 +17,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
],
oob_nonce_slot: false,
probe_predicates: &[],
- benign_control: Some(PayloadRef { label: "cmdi-benign-typescript" }),
+ benign_control: Some(PayloadRef {
+ label: "cmdi-benign-typescript",
+ }),
no_benign_control_rationale: None,
},
CuratedPayload {
diff --git a/src/dynamic/corpus/crypto/go.rs b/src/dynamic/corpus/crypto/go.rs
index 0b498440..99045d7d 100644
--- a/src/dynamic/corpus/crypto/go.rs
+++ b/src/dynamic/corpus/crypto/go.rs
@@ -11,7 +11,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"NYX_CRYPTO_WEAK",
label: "crypto-go-weak-random",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
+ predicates: &[ProbePredicate::WeakKeyEntropy {
+ max_bits: WEAK_BITS,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -19,7 +21,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/crypto/go/vuln.go"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
+ probe_predicates: &[ProbePredicate::WeakKeyEntropy {
+ max_bits: WEAK_BITS,
+ }],
benign_control: Some(PayloadRef {
label: "crypto-go-benign",
}),
@@ -29,7 +33,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"NYX_CRYPTO_STRONG",
label: "crypto-go-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
+ predicates: &[ProbePredicate::WeakKeyEntropy {
+ max_bits: WEAK_BITS,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/crypto/java.rs b/src/dynamic/corpus/crypto/java.rs
index 3276d5c8..952b705d 100644
--- a/src/dynamic/corpus/crypto/java.rs
+++ b/src/dynamic/corpus/crypto/java.rs
@@ -22,7 +22,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"NYX_CRYPTO_WEAK",
label: "crypto-java-weak-random",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
+ predicates: &[ProbePredicate::WeakKeyEntropy {
+ max_bits: WEAK_BITS,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -30,7 +32,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/crypto/java/vuln.java"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
+ probe_predicates: &[ProbePredicate::WeakKeyEntropy {
+ max_bits: WEAK_BITS,
+ }],
benign_control: Some(PayloadRef {
label: "crypto-java-benign",
}),
@@ -40,7 +44,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"NYX_CRYPTO_STRONG",
label: "crypto-java-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
+ predicates: &[ProbePredicate::WeakKeyEntropy {
+ max_bits: WEAK_BITS,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/crypto/php.rs b/src/dynamic/corpus/crypto/php.rs
index fc6818fb..148622fb 100644
--- a/src/dynamic/corpus/crypto/php.rs
+++ b/src/dynamic/corpus/crypto/php.rs
@@ -10,7 +10,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"NYX_CRYPTO_WEAK",
label: "crypto-php-weak-random",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
+ predicates: &[ProbePredicate::WeakKeyEntropy {
+ max_bits: WEAK_BITS,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -18,7 +20,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/crypto/php/vuln.php"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
+ probe_predicates: &[ProbePredicate::WeakKeyEntropy {
+ max_bits: WEAK_BITS,
+ }],
benign_control: Some(PayloadRef {
label: "crypto-php-benign",
}),
@@ -28,7 +32,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"NYX_CRYPTO_STRONG",
label: "crypto-php-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
+ predicates: &[ProbePredicate::WeakKeyEntropy {
+ max_bits: WEAK_BITS,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/crypto/python.rs b/src/dynamic/corpus/crypto/python.rs
index 8b0915ed..8f1d89d0 100644
--- a/src/dynamic/corpus/crypto/python.rs
+++ b/src/dynamic/corpus/crypto/python.rs
@@ -20,7 +20,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"NYX_CRYPTO_WEAK",
label: "crypto-python-weak-random",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
+ predicates: &[ProbePredicate::WeakKeyEntropy {
+ max_bits: WEAK_BITS,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -28,7 +30,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/crypto/python/vuln.py"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
+ probe_predicates: &[ProbePredicate::WeakKeyEntropy {
+ max_bits: WEAK_BITS,
+ }],
benign_control: Some(PayloadRef {
label: "crypto-python-benign",
}),
@@ -38,7 +42,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"NYX_CRYPTO_STRONG",
label: "crypto-python-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
+ predicates: &[ProbePredicate::WeakKeyEntropy {
+ max_bits: WEAK_BITS,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/crypto/rust.rs b/src/dynamic/corpus/crypto/rust.rs
index 3895fcd7..24acfea7 100644
--- a/src/dynamic/corpus/crypto/rust.rs
+++ b/src/dynamic/corpus/crypto/rust.rs
@@ -11,7 +11,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"NYX_CRYPTO_WEAK",
label: "crypto-rust-weak-random",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
+ predicates: &[ProbePredicate::WeakKeyEntropy {
+ max_bits: WEAK_BITS,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -19,7 +21,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/crypto/rust/vuln.rs"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
+ probe_predicates: &[ProbePredicate::WeakKeyEntropy {
+ max_bits: WEAK_BITS,
+ }],
benign_control: Some(PayloadRef {
label: "crypto-rust-benign",
}),
@@ -29,7 +33,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"NYX_CRYPTO_STRONG",
label: "crypto-rust-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::WeakKeyEntropy { max_bits: WEAK_BITS }],
+ predicates: &[ProbePredicate::WeakKeyEntropy {
+ max_bits: WEAK_BITS,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/data_exfil/go.rs b/src/dynamic/corpus/data_exfil/go.rs
index d3afeb47..5fec1fff 100644
--- a/src/dynamic/corpus/data_exfil/go.rs
+++ b/src/dynamic/corpus/data_exfil/go.rs
@@ -10,7 +10,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"attacker.test",
label: "data-exfil-go-external",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -18,7 +20,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/data_exfil/go/vuln.go"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ probe_predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
benign_control: Some(PayloadRef {
label: "data-exfil-go-benign",
}),
@@ -28,7 +32,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"127.0.0.1",
label: "data-exfil-go-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/data_exfil/java.rs b/src/dynamic/corpus/data_exfil/java.rs
index 8b6af8db..43f5da96 100644
--- a/src/dynamic/corpus/data_exfil/java.rs
+++ b/src/dynamic/corpus/data_exfil/java.rs
@@ -10,7 +10,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"attacker.test",
label: "data-exfil-java-external",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -18,7 +20,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/data_exfil/java/vuln.java"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ probe_predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
benign_control: Some(PayloadRef {
label: "data-exfil-java-benign",
}),
@@ -28,7 +32,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"127.0.0.1",
label: "data-exfil-java-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/data_exfil/js.rs b/src/dynamic/corpus/data_exfil/js.rs
index 47c47a79..80c104ab 100644
--- a/src/dynamic/corpus/data_exfil/js.rs
+++ b/src/dynamic/corpus/data_exfil/js.rs
@@ -10,7 +10,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"attacker.test",
label: "data-exfil-js-external",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -18,7 +20,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/data_exfil/js/vuln.js"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ probe_predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
benign_control: Some(PayloadRef {
label: "data-exfil-js-benign",
}),
@@ -28,7 +32,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"127.0.0.1",
label: "data-exfil-js-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/data_exfil/php.rs b/src/dynamic/corpus/data_exfil/php.rs
index a1895826..32c89475 100644
--- a/src/dynamic/corpus/data_exfil/php.rs
+++ b/src/dynamic/corpus/data_exfil/php.rs
@@ -10,7 +10,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"attacker.test",
label: "data-exfil-php-external",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -18,7 +20,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/data_exfil/php/vuln.php"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ probe_predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
benign_control: Some(PayloadRef {
label: "data-exfil-php-benign",
}),
@@ -28,7 +32,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"127.0.0.1",
label: "data-exfil-php-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/data_exfil/python.rs b/src/dynamic/corpus/data_exfil/python.rs
index 827e15e1..16405f30 100644
--- a/src/dynamic/corpus/data_exfil/python.rs
+++ b/src/dynamic/corpus/data_exfil/python.rs
@@ -10,7 +10,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"attacker.test",
label: "data-exfil-python-external",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -18,7 +20,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/data_exfil/python/vuln.py"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ probe_predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
benign_control: Some(PayloadRef {
label: "data-exfil-python-benign",
}),
@@ -28,7 +32,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"127.0.0.1",
label: "data-exfil-python-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/data_exfil/ruby.rs b/src/dynamic/corpus/data_exfil/ruby.rs
index 9526cb49..e1e3c1b2 100644
--- a/src/dynamic/corpus/data_exfil/ruby.rs
+++ b/src/dynamic/corpus/data_exfil/ruby.rs
@@ -10,7 +10,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"attacker.test",
label: "data-exfil-ruby-external",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -18,7 +20,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/data_exfil/ruby/vuln.rb"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ probe_predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
benign_control: Some(PayloadRef {
label: "data-exfil-ruby-benign",
}),
@@ -28,7 +32,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"127.0.0.1",
label: "data-exfil-ruby-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/data_exfil/rust.rs b/src/dynamic/corpus/data_exfil/rust.rs
index 6bdb2e77..eb09a508 100644
--- a/src/dynamic/corpus/data_exfil/rust.rs
+++ b/src/dynamic/corpus/data_exfil/rust.rs
@@ -10,7 +10,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"attacker.test",
label: "data-exfil-rust-external",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -18,7 +20,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/data_exfil/rust/vuln.rs"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ probe_predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
benign_control: Some(PayloadRef {
label: "data-exfil-rust-benign",
}),
@@ -28,7 +32,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"127.0.0.1",
label: "data-exfil-rust-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::OutboundHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::OutboundHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/deserialize/java.rs b/src/dynamic/corpus/deserialize/java.rs
index 8ee9931b..53dc5622 100644
--- a/src/dynamic/corpus/deserialize/java.rs
+++ b/src/dynamic/corpus/deserialize/java.rs
@@ -29,9 +29,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 7,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/deserialize/java/Vuln.java",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/deserialize/java/Vuln.java"],
oob_nonce_slot: false,
probe_predicates: &[ProbePredicate::DeserializeGadgetInvoked {
require_invoked: true,
@@ -55,9 +53,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 7,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/deserialize/java/Benign.java",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/deserialize/java/Benign.java"],
oob_nonce_slot: false,
probe_predicates: &[],
benign_control: None,
diff --git a/src/dynamic/corpus/deserialize/php.rs b/src/dynamic/corpus/deserialize/php.rs
index 14d1c706..866fe2f9 100644
--- a/src/dynamic/corpus/deserialize/php.rs
+++ b/src/dynamic/corpus/deserialize/php.rs
@@ -26,9 +26,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 7,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/deserialize/php/vuln.php",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/deserialize/php/vuln.php"],
oob_nonce_slot: false,
probe_predicates: &[ProbePredicate::DeserializeGadgetInvoked {
require_invoked: true,
@@ -53,9 +51,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 7,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/deserialize/php/benign.php",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/deserialize/php/benign.php"],
oob_nonce_slot: false,
probe_predicates: &[],
benign_control: None,
diff --git a/src/dynamic/corpus/deserialize/python.rs b/src/dynamic/corpus/deserialize/python.rs
index 2c4f3d57..79be82bb 100644
--- a/src/dynamic/corpus/deserialize/python.rs
+++ b/src/dynamic/corpus/deserialize/python.rs
@@ -25,9 +25,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 7,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/deserialize/python/vuln.py",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/deserialize/python/vuln.py"],
oob_nonce_slot: false,
probe_predicates: &[ProbePredicate::DeserializeGadgetInvoked {
require_invoked: true,
@@ -49,9 +47,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 7,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/deserialize/python/benign.py",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/deserialize/python/benign.py"],
oob_nonce_slot: false,
probe_predicates: &[],
benign_control: None,
diff --git a/src/dynamic/corpus/deserialize/ruby.rs b/src/dynamic/corpus/deserialize/ruby.rs
index 9889a510..55743c7b 100644
--- a/src/dynamic/corpus/deserialize/ruby.rs
+++ b/src/dynamic/corpus/deserialize/ruby.rs
@@ -26,9 +26,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 7,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/deserialize/ruby/vuln.rb",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/deserialize/ruby/vuln.rb"],
oob_nonce_slot: false,
probe_predicates: &[ProbePredicate::DeserializeGadgetInvoked {
require_invoked: true,
@@ -50,9 +48,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 7,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/deserialize/ruby/benign.rb",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/deserialize/ruby/benign.rb"],
oob_nonce_slot: false,
probe_predicates: &[],
benign_control: None,
diff --git a/src/dynamic/corpus/fmt_string/c.rs b/src/dynamic/corpus/fmt_string/c.rs
index bba50e38..47ae303b 100644
--- a/src/dynamic/corpus/fmt_string/c.rs
+++ b/src/dynamic/corpus/fmt_string/c.rs
@@ -32,7 +32,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
],
oob_nonce_slot: false,
probe_predicates: &[],
- benign_control: Some(PayloadRef { label: "fmt-string-benign" }),
+ benign_control: Some(PayloadRef {
+ label: "fmt-string-benign",
+ }),
no_benign_control_rationale: None,
},
CuratedPayload {
diff --git a/src/dynamic/corpus/open_redirect/go.rs b/src/dynamic/corpus/open_redirect/go.rs
index 28b1edcf..62019259 100644
--- a/src/dynamic/corpus/open_redirect/go.rs
+++ b/src/dynamic/corpus/open_redirect/go.rs
@@ -21,7 +21,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"https://attacker.test/",
label: "open-redirect-go-absolute",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -29,7 +31,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/open_redirect/go/vuln.go"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ probe_predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
benign_control: Some(PayloadRef {
label: "open-redirect-go-benign",
}),
@@ -39,7 +43,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"/dashboard",
label: "open-redirect-go-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/open_redirect/java.rs b/src/dynamic/corpus/open_redirect/java.rs
index c9c468be..33753ecb 100644
--- a/src/dynamic/corpus/open_redirect/java.rs
+++ b/src/dynamic/corpus/open_redirect/java.rs
@@ -26,7 +26,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"https://attacker.test/",
label: "open-redirect-java-absolute",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -34,7 +36,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/open_redirect/java/Vuln.java"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ probe_predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
benign_control: Some(PayloadRef {
label: "open-redirect-java-benign",
}),
@@ -44,7 +48,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"/dashboard",
label: "open-redirect-java-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/open_redirect/js.rs b/src/dynamic/corpus/open_redirect/js.rs
index 8d13ac43..984d9254 100644
--- a/src/dynamic/corpus/open_redirect/js.rs
+++ b/src/dynamic/corpus/open_redirect/js.rs
@@ -20,7 +20,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"https://attacker.test/",
label: "open-redirect-js-absolute",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -28,7 +30,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/open_redirect/js/vuln.js"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ probe_predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
benign_control: Some(PayloadRef {
label: "open-redirect-js-benign",
}),
@@ -38,7 +42,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"/dashboard",
label: "open-redirect-js-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/open_redirect/php.rs b/src/dynamic/corpus/open_redirect/php.rs
index 504d65aa..bdba4239 100644
--- a/src/dynamic/corpus/open_redirect/php.rs
+++ b/src/dynamic/corpus/open_redirect/php.rs
@@ -22,7 +22,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"https://attacker.test/",
label: "open-redirect-php-absolute",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -30,7 +32,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/open_redirect/php/vuln.php"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ probe_predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
benign_control: Some(PayloadRef {
label: "open-redirect-php-benign",
}),
@@ -40,7 +44,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"/dashboard",
label: "open-redirect-php-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/open_redirect/python.rs b/src/dynamic/corpus/open_redirect/python.rs
index ecd8ae4c..ee61581b 100644
--- a/src/dynamic/corpus/open_redirect/python.rs
+++ b/src/dynamic/corpus/open_redirect/python.rs
@@ -21,7 +21,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"https://attacker.test/",
label: "open-redirect-python-absolute",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -29,7 +31,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/open_redirect/python/vuln.py"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ probe_predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
benign_control: Some(PayloadRef {
label: "open-redirect-python-benign",
}),
@@ -39,7 +43,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"/dashboard",
label: "open-redirect-python-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/open_redirect/ruby.rs b/src/dynamic/corpus/open_redirect/ruby.rs
index 5a504bcb..6b19acd5 100644
--- a/src/dynamic/corpus/open_redirect/ruby.rs
+++ b/src/dynamic/corpus/open_redirect/ruby.rs
@@ -20,7 +20,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"https://attacker.test/",
label: "open-redirect-ruby-absolute",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -28,7 +30,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/open_redirect/ruby/vuln.rb"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ probe_predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
benign_control: Some(PayloadRef {
label: "open-redirect-ruby-benign",
}),
@@ -38,7 +42,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"/dashboard",
label: "open-redirect-ruby-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/open_redirect/rust.rs b/src/dynamic/corpus/open_redirect/rust.rs
index 4f649596..d8a47599 100644
--- a/src/dynamic/corpus/open_redirect/rust.rs
+++ b/src/dynamic/corpus/open_redirect/rust.rs
@@ -20,7 +20,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"https://attacker.test/",
label: "open-redirect-rust-absolute",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: false,
provenance: PayloadProvenance::Curated,
@@ -28,7 +30,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
deprecated_at_corpus_version: None,
fixture_paths: &["tests/dynamic_fixtures/open_redirect/rust/vuln.rs"],
oob_nonce_slot: false,
- probe_predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ probe_predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
benign_control: Some(PayloadRef {
label: "open-redirect-rust-benign",
}),
@@ -38,7 +42,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
bytes: b"/dashboard",
label: "open-redirect-rust-benign",
oracle: Oracle::SinkProbe {
- predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: ALLOWLIST }],
+ predicates: &[ProbePredicate::RedirectHostNotIn {
+ allowlist: ALLOWLIST,
+ }],
},
is_benign: true,
provenance: PayloadProvenance::Curated,
diff --git a/src/dynamic/corpus/path_trav/rust.rs b/src/dynamic/corpus/path_trav/rust.rs
index 81feb067..ce08d50a 100644
--- a/src/dynamic/corpus/path_trav/rust.rs
+++ b/src/dynamic/corpus/path_trav/rust.rs
@@ -21,7 +21,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
],
oob_nonce_slot: false,
probe_predicates: &[],
- benign_control: Some(PayloadRef { label: "path-traversal-benign" }),
+ benign_control: Some(PayloadRef {
+ label: "path-traversal-benign",
+ }),
no_benign_control_rationale: None,
},
CuratedPayload {
diff --git a/src/dynamic/corpus/registry.rs b/src/dynamic/corpus/registry.rs
index 1b10da25..9f00f2b1 100644
--- a/src/dynamic/corpus/registry.rs
+++ b/src/dynamic/corpus/registry.rs
@@ -23,12 +23,12 @@
use std::collections::HashMap;
use std::sync::OnceLock;
+use super::{CapCorpus, CuratedPayload, Oracle};
use super::{
cmdi, crypto, data_exfil, deserialize, fmt_string, header_injection, json_parse, ldap,
open_redirect, path_trav, prototype_pollution, sqli, ssrf, ssti, unauthorized_id, xpath, xss,
xxe,
};
-use super::{CapCorpus, CuratedPayload, Oracle};
use crate::dynamic::oracle::ProbePredicate;
use crate::labels::Cap;
use crate::symbol::Lang;
@@ -93,7 +93,11 @@ const ENTRIES: &[(Cap, Lang, &[CuratedPayload])] = &[
(Cap::HTML_ESCAPE, Lang::Rust, xss::rust::PAYLOADS),
(Cap::FMT_STRING, Lang::C, fmt_string::c::PAYLOADS),
(Cap::DESERIALIZE, Lang::Java, deserialize::java::PAYLOADS),
- (Cap::DESERIALIZE, Lang::Python, deserialize::python::PAYLOADS),
+ (
+ Cap::DESERIALIZE,
+ Lang::Python,
+ deserialize::python::PAYLOADS,
+ ),
(Cap::DESERIALIZE, Lang::Php, deserialize::php::PAYLOADS),
(Cap::DESERIALIZE, Lang::Ruby, deserialize::ruby::PAYLOADS),
(Cap::SSTI, Lang::Python, ssti::python_jinja2::PAYLOADS),
@@ -113,20 +117,68 @@ const ENTRIES: &[(Cap, Lang, &[CuratedPayload])] = &[
(Cap::XPATH_INJECTION, Lang::Python, xpath::python::PAYLOADS),
(Cap::XPATH_INJECTION, Lang::Php, xpath::php::PAYLOADS),
(Cap::XPATH_INJECTION, Lang::JavaScript, xpath::js::PAYLOADS),
- (Cap::HEADER_INJECTION, Lang::Java, header_injection::java::PAYLOADS),
- (Cap::HEADER_INJECTION, Lang::Python, header_injection::python::PAYLOADS),
- (Cap::HEADER_INJECTION, Lang::Php, header_injection::php::PAYLOADS),
- (Cap::HEADER_INJECTION, Lang::Ruby, header_injection::ruby::PAYLOADS),
- (Cap::HEADER_INJECTION, Lang::JavaScript, header_injection::js::PAYLOADS),
- (Cap::HEADER_INJECTION, Lang::Go, header_injection::go::PAYLOADS),
- (Cap::HEADER_INJECTION, Lang::Rust, header_injection::rust::PAYLOADS),
- (Cap::OPEN_REDIRECT, Lang::Java, open_redirect::java::PAYLOADS),
- (Cap::OPEN_REDIRECT, Lang::Python, open_redirect::python::PAYLOADS),
+ (
+ Cap::HEADER_INJECTION,
+ Lang::Java,
+ header_injection::java::PAYLOADS,
+ ),
+ (
+ Cap::HEADER_INJECTION,
+ Lang::Python,
+ header_injection::python::PAYLOADS,
+ ),
+ (
+ Cap::HEADER_INJECTION,
+ Lang::Php,
+ header_injection::php::PAYLOADS,
+ ),
+ (
+ Cap::HEADER_INJECTION,
+ Lang::Ruby,
+ header_injection::ruby::PAYLOADS,
+ ),
+ (
+ Cap::HEADER_INJECTION,
+ Lang::JavaScript,
+ header_injection::js::PAYLOADS,
+ ),
+ (
+ Cap::HEADER_INJECTION,
+ Lang::Go,
+ header_injection::go::PAYLOADS,
+ ),
+ (
+ Cap::HEADER_INJECTION,
+ Lang::Rust,
+ header_injection::rust::PAYLOADS,
+ ),
+ (
+ Cap::OPEN_REDIRECT,
+ Lang::Java,
+ open_redirect::java::PAYLOADS,
+ ),
+ (
+ Cap::OPEN_REDIRECT,
+ Lang::Python,
+ open_redirect::python::PAYLOADS,
+ ),
(Cap::OPEN_REDIRECT, Lang::Php, open_redirect::php::PAYLOADS),
- (Cap::OPEN_REDIRECT, Lang::Ruby, open_redirect::ruby::PAYLOADS),
- (Cap::OPEN_REDIRECT, Lang::JavaScript, open_redirect::js::PAYLOADS),
+ (
+ Cap::OPEN_REDIRECT,
+ Lang::Ruby,
+ open_redirect::ruby::PAYLOADS,
+ ),
+ (
+ Cap::OPEN_REDIRECT,
+ Lang::JavaScript,
+ open_redirect::js::PAYLOADS,
+ ),
(Cap::OPEN_REDIRECT, Lang::Go, open_redirect::go::PAYLOADS),
- (Cap::OPEN_REDIRECT, Lang::Rust, open_redirect::rust::PAYLOADS),
+ (
+ Cap::OPEN_REDIRECT,
+ Lang::Rust,
+ open_redirect::rust::PAYLOADS,
+ ),
(
Cap::PROTOTYPE_POLLUTION,
Lang::JavaScript,
@@ -142,16 +194,48 @@ const ENTRIES: &[(Cap, Lang, &[CuratedPayload])] = &[
(Cap::CRYPTO, Lang::Php, crypto::php::PAYLOADS),
(Cap::CRYPTO, Lang::Go, crypto::go::PAYLOADS),
(Cap::CRYPTO, Lang::Rust, crypto::rust::PAYLOADS),
- (Cap::JSON_PARSE, Lang::JavaScript, json_parse::javascript::PAYLOADS),
+ (
+ Cap::JSON_PARSE,
+ Lang::JavaScript,
+ json_parse::javascript::PAYLOADS,
+ ),
(Cap::JSON_PARSE, Lang::Python, json_parse::python::PAYLOADS),
(Cap::JSON_PARSE, Lang::Ruby, json_parse::ruby::PAYLOADS),
- (Cap::UNAUTHORIZED_ID, Lang::Python, unauthorized_id::python::PAYLOADS),
- (Cap::UNAUTHORIZED_ID, Lang::Ruby, unauthorized_id::ruby::PAYLOADS),
- (Cap::UNAUTHORIZED_ID, Lang::Java, unauthorized_id::java::PAYLOADS),
- (Cap::UNAUTHORIZED_ID, Lang::Php, unauthorized_id::php::PAYLOADS),
- (Cap::UNAUTHORIZED_ID, Lang::JavaScript, unauthorized_id::js::PAYLOADS),
- (Cap::UNAUTHORIZED_ID, Lang::Go, unauthorized_id::go::PAYLOADS),
- (Cap::UNAUTHORIZED_ID, Lang::Rust, unauthorized_id::rust::PAYLOADS),
+ (
+ Cap::UNAUTHORIZED_ID,
+ Lang::Python,
+ unauthorized_id::python::PAYLOADS,
+ ),
+ (
+ Cap::UNAUTHORIZED_ID,
+ Lang::Ruby,
+ unauthorized_id::ruby::PAYLOADS,
+ ),
+ (
+ Cap::UNAUTHORIZED_ID,
+ Lang::Java,
+ unauthorized_id::java::PAYLOADS,
+ ),
+ (
+ Cap::UNAUTHORIZED_ID,
+ Lang::Php,
+ unauthorized_id::php::PAYLOADS,
+ ),
+ (
+ Cap::UNAUTHORIZED_ID,
+ Lang::JavaScript,
+ unauthorized_id::js::PAYLOADS,
+ ),
+ (
+ Cap::UNAUTHORIZED_ID,
+ Lang::Go,
+ unauthorized_id::go::PAYLOADS,
+ ),
+ (
+ Cap::UNAUTHORIZED_ID,
+ Lang::Rust,
+ unauthorized_id::rust::PAYLOADS,
+ ),
(Cap::DATA_EXFIL, Lang::Python, data_exfil::python::PAYLOADS),
(Cap::DATA_EXFIL, Lang::Ruby, data_exfil::ruby::PAYLOADS),
(Cap::DATA_EXFIL, Lang::Java, data_exfil::java::PAYLOADS),
@@ -355,7 +439,7 @@ pub fn audit_marker_collisions() -> Vec<(&'static str, &'static str, &'static st
#[cfg(test)]
mod tests {
use super::*;
- use crate::dynamic::corpus::{benign_payload_for, CORPUS_VERSION};
+ use crate::dynamic::corpus::{CORPUS_VERSION, benign_payload_for};
#[test]
fn supported_caps_have_payloads() {
@@ -404,8 +488,14 @@ mod tests {
#[test]
fn phase_11_caps_pair_benign_controls_per_lang() {
let cases: &[(Cap, &[Lang])] = &[
- (Cap::CRYPTO, &[Lang::Java, Lang::Python, Lang::Php, Lang::Go, Lang::Rust]),
- (Cap::JSON_PARSE, &[Lang::JavaScript, Lang::Python, Lang::Ruby]),
+ (
+ Cap::CRYPTO,
+ &[Lang::Java, Lang::Python, Lang::Php, Lang::Go, Lang::Rust],
+ ),
+ (
+ Cap::JSON_PARSE,
+ &[Lang::JavaScript, Lang::Python, Lang::Ruby],
+ ),
(
Cap::UNAUTHORIZED_ID,
&[
@@ -434,10 +524,7 @@ mod tests {
for (cap, langs) in cases {
for lang in *langs {
let slice = payloads_for_lang(*cap, *lang);
- assert!(
- !slice.is_empty(),
- "({cap:?}, {lang:?}) must have payloads",
- );
+ assert!(!slice.is_empty(), "({cap:?}, {lang:?}) must have payloads",);
let vuln = slice
.iter()
.find(|p| !p.is_benign)
@@ -596,7 +683,10 @@ mod tests {
#[test]
fn ssrf_has_oob_nonce_slot() {
let has_oob = payloads_for(Cap::SSRF).iter().any(|p| p.oob_nonce_slot);
- assert!(has_oob, "SSRF corpus must include an OOB-nonce-slot payload");
+ assert!(
+ has_oob,
+ "SSRF corpus must include an OOB-nonce-slot payload"
+ );
}
#[test]
@@ -617,8 +707,7 @@ mod tests {
.find(|p| p.oob_nonce_slot)
.expect("must have OOB payload");
let url = "http://127.0.0.1:54321/mynonce";
- let bytes =
- materialise_bytes(p, Some(url)).expect("OOB payload materialises with URL");
+ let bytes = materialise_bytes(p, Some(url)).expect("OOB payload materialises with URL");
assert_eq!(&*bytes, url.as_bytes());
}
@@ -637,7 +726,11 @@ mod tests {
(Cap::SQL_QUERY, "sqli-tautology", "sqli-benign"),
(Cap::SQL_QUERY, "sqli-union-nyx", "sqli-benign"),
(Cap::CODE_EXEC, "cmdi-echo-marker", "cmdi-benign"),
- (Cap::FILE_IO, "path-traversal-passwd", "path-traversal-benign"),
+ (
+ Cap::FILE_IO,
+ "path-traversal-passwd",
+ "path-traversal-benign",
+ ),
(Cap::SSRF, "ssrf-file-scheme", "ssrf-benign"),
(Cap::HTML_ESCAPE, "xss-script-marker", "xss-benign-text"),
];
@@ -723,7 +816,10 @@ mod tests {
let mut entries_by_cap: HashMap> =
HashMap::new();
for &(cap, lang, slice) in CORPUS.entries {
- entries_by_cap.entry(cap.bits()).or_default().push((lang, slice));
+ entries_by_cap
+ .entry(cap.bits())
+ .or_default()
+ .push((lang, slice));
}
for (cap_bits, langs) in &entries_by_cap {
if langs.len() != 1 {
@@ -899,9 +995,8 @@ mod tests {
.iter()
.find(|p| !p.is_benign)
.expect("each lang must have an LDAP vuln payload");
- let resolved =
- super::resolve_benign_control_lang(vuln, Cap::LDAP_INJECTION, lang)
- .expect("lang-aware benign control must resolve");
+ let resolved = super::resolve_benign_control_lang(vuln, Cap::LDAP_INJECTION, lang)
+ .expect("lang-aware benign control must resolve");
assert!(resolved.is_benign);
}
}
@@ -941,9 +1036,8 @@ mod tests {
.iter()
.find(|p| !p.is_benign)
.expect("each lang must have an XPath vuln payload");
- let resolved =
- super::resolve_benign_control_lang(vuln, Cap::XPATH_INJECTION, lang)
- .expect("lang-aware benign control must resolve");
+ let resolved = super::resolve_benign_control_lang(vuln, Cap::XPATH_INJECTION, lang)
+ .expect("lang-aware benign control must resolve");
assert!(resolved.is_benign);
}
}
@@ -992,9 +1086,8 @@ mod tests {
.iter()
.find(|p| !p.is_benign)
.expect("each lang must have a HEADER_INJECTION vuln payload");
- let resolved =
- super::resolve_benign_control_lang(vuln, Cap::HEADER_INJECTION, lang)
- .expect("lang-aware benign control must resolve");
+ let resolved = super::resolve_benign_control_lang(vuln, Cap::HEADER_INJECTION, lang)
+ .expect("lang-aware benign control must resolve");
assert!(resolved.is_benign);
}
}
@@ -1036,9 +1129,8 @@ mod tests {
.iter()
.find(|p| !p.is_benign)
.expect("each lang must have a PROTOTYPE_POLLUTION vuln payload");
- let resolved =
- super::resolve_benign_control_lang(vuln, Cap::PROTOTYPE_POLLUTION, lang)
- .expect("lang-aware benign control must resolve");
+ let resolved = super::resolve_benign_control_lang(vuln, Cap::PROTOTYPE_POLLUTION, lang)
+ .expect("lang-aware benign control must resolve");
assert!(resolved.is_benign);
}
}
diff --git a/src/dynamic/corpus/sqli/rust.rs b/src/dynamic/corpus/sqli/rust.rs
index b8c09ff4..9b25ae4e 100644
--- a/src/dynamic/corpus/sqli/rust.rs
+++ b/src/dynamic/corpus/sqli/rust.rs
@@ -18,7 +18,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
fixture_paths: &["tests/benchmark/corpus/rust/sqli/sqli_rusqlite_format.rs"],
oob_nonce_slot: false,
probe_predicates: &[],
- benign_control: Some(PayloadRef { label: "sqli-benign" }),
+ benign_control: Some(PayloadRef {
+ label: "sqli-benign",
+ }),
no_benign_control_rationale: None,
},
CuratedPayload {
@@ -32,7 +34,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
fixture_paths: &["tests/benchmark/corpus/rust/sqli/sqli_rusqlite_format.rs"],
oob_nonce_slot: false,
probe_predicates: &[],
- benign_control: Some(PayloadRef { label: "sqli-benign" }),
+ benign_control: Some(PayloadRef {
+ label: "sqli-benign",
+ }),
no_benign_control_rationale: None,
},
// Benign control: ordinary value that should never produce the SQL marker.
diff --git a/src/dynamic/corpus/ssrf/rust.rs b/src/dynamic/corpus/ssrf/rust.rs
index a5acd0ff..5dc800c1 100644
--- a/src/dynamic/corpus/ssrf/rust.rs
+++ b/src/dynamic/corpus/ssrf/rust.rs
@@ -26,7 +26,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
fixture_paths: &["tests/benchmark/corpus/rust/ssrf/ssrf_reqwest.rs"],
oob_nonce_slot: false,
probe_predicates: &[],
- benign_control: Some(PayloadRef { label: "ssrf-benign" }),
+ benign_control: Some(PayloadRef {
+ label: "ssrf-benign",
+ }),
no_benign_control_rationale: None,
},
CuratedPayload {
diff --git a/src/dynamic/corpus/ssti/java_thymeleaf.rs b/src/dynamic/corpus/ssti/java_thymeleaf.rs
index 29c3a799..80c215ad 100644
--- a/src/dynamic/corpus/ssti/java_thymeleaf.rs
+++ b/src/dynamic/corpus/ssti/java_thymeleaf.rs
@@ -19,9 +19,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 8,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/ssti/java_thymeleaf/vuln.java",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/ssti/java_thymeleaf/vuln.java"],
oob_nonce_slot: false,
probe_predicates: &[ProbePredicate::TemplateEvalEqual { expected: 49 }],
benign_control: Some(PayloadRef {
@@ -39,9 +37,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 8,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/ssti/java_thymeleaf/benign.java",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/ssti/java_thymeleaf/benign.java"],
oob_nonce_slot: false,
probe_predicates: &[],
benign_control: None,
diff --git a/src/dynamic/corpus/ssti/js_handlebars.rs b/src/dynamic/corpus/ssti/js_handlebars.rs
index bfb35c01..db1b0e3b 100644
--- a/src/dynamic/corpus/ssti/js_handlebars.rs
+++ b/src/dynamic/corpus/ssti/js_handlebars.rs
@@ -25,9 +25,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 8,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/ssti/js_handlebars/vuln.js",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/ssti/js_handlebars/vuln.js"],
oob_nonce_slot: false,
probe_predicates: &[ProbePredicate::TemplateEvalEqual { expected: 49 }],
benign_control: Some(PayloadRef {
@@ -45,9 +43,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 8,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/ssti/js_handlebars/benign.js",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/ssti/js_handlebars/benign.js"],
oob_nonce_slot: false,
probe_predicates: &[],
benign_control: None,
diff --git a/src/dynamic/corpus/ssti/php_twig.rs b/src/dynamic/corpus/ssti/php_twig.rs
index 8f5666d8..289f9bea 100644
--- a/src/dynamic/corpus/ssti/php_twig.rs
+++ b/src/dynamic/corpus/ssti/php_twig.rs
@@ -19,9 +19,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 8,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/ssti/php_twig/vuln.php",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/ssti/php_twig/vuln.php"],
oob_nonce_slot: false,
probe_predicates: &[ProbePredicate::TemplateEvalEqual { expected: 49 }],
benign_control: Some(PayloadRef {
@@ -39,9 +37,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 8,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/ssti/php_twig/benign.php",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/ssti/php_twig/benign.php"],
oob_nonce_slot: false,
probe_predicates: &[],
benign_control: None,
diff --git a/src/dynamic/corpus/ssti/python_jinja2.rs b/src/dynamic/corpus/ssti/python_jinja2.rs
index 439d1491..9c50cb79 100644
--- a/src/dynamic/corpus/ssti/python_jinja2.rs
+++ b/src/dynamic/corpus/ssti/python_jinja2.rs
@@ -26,9 +26,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 8,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/ssti/python_jinja2/vuln.py",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/ssti/python_jinja2/vuln.py"],
oob_nonce_slot: false,
probe_predicates: &[ProbePredicate::TemplateEvalEqual { expected: 49 }],
benign_control: Some(PayloadRef {
@@ -46,9 +44,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 8,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/ssti/python_jinja2/benign.py",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/ssti/python_jinja2/benign.py"],
oob_nonce_slot: false,
probe_predicates: &[],
benign_control: None,
diff --git a/src/dynamic/corpus/ssti/ruby_erb.rs b/src/dynamic/corpus/ssti/ruby_erb.rs
index 1e8a4576..e8049ec2 100644
--- a/src/dynamic/corpus/ssti/ruby_erb.rs
+++ b/src/dynamic/corpus/ssti/ruby_erb.rs
@@ -19,9 +19,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 8,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/ssti/ruby_erb/vuln.rb",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/ssti/ruby_erb/vuln.rb"],
oob_nonce_slot: false,
probe_predicates: &[ProbePredicate::TemplateEvalEqual { expected: 49 }],
benign_control: Some(PayloadRef {
@@ -39,9 +37,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 8,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/ssti/ruby_erb/benign.rb",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/ssti/ruby_erb/benign.rb"],
oob_nonce_slot: false,
probe_predicates: &[],
benign_control: None,
diff --git a/src/dynamic/corpus/xss/rust.rs b/src/dynamic/corpus/xss/rust.rs
index e39917a8..7ff2396c 100644
--- a/src/dynamic/corpus/xss/rust.rs
+++ b/src/dynamic/corpus/xss/rust.rs
@@ -18,7 +18,9 @@ pub const PAYLOADS: &[CuratedPayload] = &[
fixture_paths: &["tests/benchmark/corpus/rust/xss/axum_html/main.rs"],
oob_nonce_slot: false,
probe_predicates: &[],
- benign_control: Some(PayloadRef { label: "xss-benign-text" }),
+ benign_control: Some(PayloadRef {
+ label: "xss-benign-text",
+ }),
no_benign_control_rationale: None,
},
CuratedPayload {
diff --git a/src/dynamic/corpus/xxe/go.rs b/src/dynamic/corpus/xxe/go.rs
index 44c4deb8..60a77f79 100644
--- a/src/dynamic/corpus/xxe/go.rs
+++ b/src/dynamic/corpus/xxe/go.rs
@@ -29,9 +29,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 15,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/xxe/go/vuln.go",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/xxe/go/vuln.go"],
oob_nonce_slot: true,
probe_predicates: &[],
benign_control: None,
@@ -57,9 +55,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 9,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/xxe/go/vuln.go",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/xxe/go/vuln.go"],
oob_nonce_slot: false,
probe_predicates: &[ProbePredicate::XxeEntityExpanded {
require_expanded: true,
@@ -82,9 +78,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 9,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/xxe/go/benign.go",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/xxe/go/benign.go"],
oob_nonce_slot: false,
probe_predicates: &[],
benign_control: None,
diff --git a/src/dynamic/corpus/xxe/java.rs b/src/dynamic/corpus/xxe/java.rs
index 885b8aaf..70436e5f 100644
--- a/src/dynamic/corpus/xxe/java.rs
+++ b/src/dynamic/corpus/xxe/java.rs
@@ -31,9 +31,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 15,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/xxe/java/Vuln.java",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/xxe/java/Vuln.java"],
oob_nonce_slot: true,
probe_predicates: &[],
benign_control: None,
@@ -59,9 +57,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 9,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/xxe/java/Vuln.java",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/xxe/java/Vuln.java"],
oob_nonce_slot: false,
probe_predicates: &[ProbePredicate::XxeEntityExpanded {
require_expanded: true,
@@ -84,9 +80,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 9,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/xxe/java/Benign.java",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/xxe/java/Benign.java"],
oob_nonce_slot: false,
probe_predicates: &[],
benign_control: None,
diff --git a/src/dynamic/corpus/xxe/php.rs b/src/dynamic/corpus/xxe/php.rs
index 6d62fa4a..d0df682a 100644
--- a/src/dynamic/corpus/xxe/php.rs
+++ b/src/dynamic/corpus/xxe/php.rs
@@ -29,9 +29,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 15,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/xxe/php/vuln.php",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/xxe/php/vuln.php"],
oob_nonce_slot: true,
probe_predicates: &[],
benign_control: None,
@@ -57,9 +55,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 9,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/xxe/php/vuln.php",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/xxe/php/vuln.php"],
oob_nonce_slot: false,
probe_predicates: &[ProbePredicate::XxeEntityExpanded {
require_expanded: true,
@@ -82,9 +78,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 9,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/xxe/php/benign.php",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/xxe/php/benign.php"],
oob_nonce_slot: false,
probe_predicates: &[],
benign_control: None,
diff --git a/src/dynamic/corpus/xxe/python.rs b/src/dynamic/corpus/xxe/python.rs
index 7eb1163b..da04b00a 100644
--- a/src/dynamic/corpus/xxe/python.rs
+++ b/src/dynamic/corpus/xxe/python.rs
@@ -39,9 +39,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 15,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/xxe/python/vuln.py",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/xxe/python/vuln.py"],
oob_nonce_slot: true,
probe_predicates: &[],
benign_control: None,
@@ -68,9 +66,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 9,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/xxe/python/vuln.py",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/xxe/python/vuln.py"],
oob_nonce_slot: false,
probe_predicates: &[ProbePredicate::XxeEntityExpanded {
require_expanded: true,
@@ -93,9 +89,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 9,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/xxe/python/benign.py",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/xxe/python/benign.py"],
oob_nonce_slot: false,
probe_predicates: &[],
benign_control: None,
diff --git a/src/dynamic/corpus/xxe/ruby.rs b/src/dynamic/corpus/xxe/ruby.rs
index 6dd09497..6cc3ee87 100644
--- a/src/dynamic/corpus/xxe/ruby.rs
+++ b/src/dynamic/corpus/xxe/ruby.rs
@@ -28,9 +28,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 15,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/xxe/ruby/vuln.rb",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/xxe/ruby/vuln.rb"],
oob_nonce_slot: true,
probe_predicates: &[],
benign_control: None,
@@ -56,9 +54,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 9,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/xxe/ruby/vuln.rb",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/xxe/ruby/vuln.rb"],
oob_nonce_slot: false,
probe_predicates: &[ProbePredicate::XxeEntityExpanded {
require_expanded: true,
@@ -81,9 +77,7 @@ pub const PAYLOADS: &[CuratedPayload] = &[
provenance: PayloadProvenance::Curated,
since_corpus_version: 9,
deprecated_at_corpus_version: None,
- fixture_paths: &[
- "tests/dynamic_fixtures/xxe/ruby/benign.rb",
- ],
+ fixture_paths: &["tests/dynamic_fixtures/xxe/ruby/benign.rb"],
oob_nonce_slot: false,
probe_predicates: &[],
benign_control: None,
diff --git a/src/dynamic/differential.rs b/src/dynamic/differential.rs
index 3861bd73..04b4cd96 100644
--- a/src/dynamic/differential.rs
+++ b/src/dynamic/differential.rs
@@ -113,7 +113,10 @@ mod tests {
#[test]
fn rule_a_both_fire_is_collision() {
- assert_eq!(evaluate(true, true), DifferentialVerdict::OracleCollisionSuspected);
+ assert_eq!(
+ evaluate(true, true),
+ DifferentialVerdict::OracleCollisionSuspected
+ );
}
#[test]
@@ -128,7 +131,10 @@ mod tests {
#[test]
fn rule_d_only_benign_fires_is_reversed() {
- assert_eq!(evaluate(false, true), DifferentialVerdict::ReversedDifferential);
+ assert_eq!(
+ evaluate(false, true),
+ DifferentialVerdict::ReversedDifferential
+ );
}
#[test]
diff --git a/src/dynamic/environment.rs b/src/dynamic/environment.rs
index 9761d707..98c42903 100644
--- a/src/dynamic/environment.rs
+++ b/src/dynamic/environment.rs
@@ -33,12 +33,12 @@
//! source file. The 10 MiB ceiling protects against runaway full-tree
//! copy regressions called out in the Phase 09 acceptance.
-use crate::callgraph::{callers_of, CallGraph};
+use crate::callgraph::{CallGraph, callers_of};
use crate::dynamic::spec::HarnessSpec;
use crate::dynamic::toolchain::{self, ToolchainResolution};
use crate::summary::GlobalSummaries;
use crate::symbol::{FuncKey, Lang};
-use crate::utils::project::{detect_frameworks, DetectedFramework};
+use crate::utils::project::{DetectedFramework, detect_frameworks};
use std::collections::HashSet;
use std::io;
use std::path::{Path, PathBuf};
@@ -139,7 +139,12 @@ pub fn extract_env_var_references(entry_file: &Path, lang: Lang) -> Vec
],
Lang::JavaScript | Lang::TypeScript => &["process.env.", "process.env["],
Lang::Java => &["System.getenv(", "getenv("],
- Lang::Rust => &["std::env::var(", "env::var(", "env::var_os(", "std::env::var_os("],
+ Lang::Rust => &[
+ "std::env::var(",
+ "env::var(",
+ "env::var_os(",
+ "std::env::var_os(",
+ ],
Lang::Go => &["os.Getenv(", "os.LookupEnv("],
Lang::Php => &["getenv(", "$_ENV[", "$_SERVER["],
Lang::Ruby => &["ENV[", "ENV.fetch(", "ENV.fetch "],
@@ -161,9 +166,12 @@ pub fn extract_env_var_references(entry_file: &Path, lang: Lang) -> Vec
_ => extract_quoted_arg(tail),
};
if let Some(name) = name
- && !name.is_empty() && is_env_var_name(&name) && seen.insert(name.clone()) {
- out.push(name);
- }
+ && !name.is_empty()
+ && is_env_var_name(&name)
+ && seen.insert(name.clone())
+ {
+ out.push(name);
+ }
}
}
out
@@ -199,7 +207,9 @@ fn extract_quoted_arg(s: &str) -> Option {
if i >= bytes.len() {
return None;
}
- std::str::from_utf8(&bytes[start..i]).ok().map(|s| s.to_owned())
+ std::str::from_utf8(&bytes[start..i])
+ .ok()
+ .map(|s| s.to_owned())
}
/// Extract a bare identifier (e.g. `FOO` in `process.env.FOO`). Stops at
@@ -241,11 +251,7 @@ fn is_env_var_name(s: &str) -> bool {
///
/// Returned in deterministic source-order so two runs against the same
/// inputs produce byte-identical env layouts.
-pub fn build_secret_bag(
- entry_file: &Path,
- lang: Lang,
- spec_hash: &str,
-) -> Vec<(String, String)> {
+pub fn build_secret_bag(entry_file: &Path, lang: Lang, spec_hash: &str) -> Vec<(String, String)> {
let mut out: Vec<(String, String)> = Vec::new();
for name in extract_env_var_references(entry_file, lang) {
let val = derive_secret(spec_hash, &name);
@@ -288,9 +294,33 @@ const CONFIG_FILE_CANDIDATES: &[&str] = &[
/// user's pinned dependency set. Order is significant only insofar as
/// the first match wins for [`CapturedDeps::lockfile_origin`].
const MANIFEST_FILES_BY_LANG: &[(Lang, &[&str])] = &[
- (Lang::Python, &["requirements.txt", "pyproject.toml", "Pipfile", "Pipfile.lock"]),
- (Lang::JavaScript, &["package.json", "package-lock.json", "yarn.lock", "pnpm-lock.yaml"]),
- (Lang::TypeScript, &["package.json", "package-lock.json", "yarn.lock", "tsconfig.json"]),
+ (
+ Lang::Python,
+ &[
+ "requirements.txt",
+ "pyproject.toml",
+ "Pipfile",
+ "Pipfile.lock",
+ ],
+ ),
+ (
+ Lang::JavaScript,
+ &[
+ "package.json",
+ "package-lock.json",
+ "yarn.lock",
+ "pnpm-lock.yaml",
+ ],
+ ),
+ (
+ Lang::TypeScript,
+ &[
+ "package.json",
+ "package-lock.json",
+ "yarn.lock",
+ "tsconfig.json",
+ ],
+ ),
(Lang::Rust, &["Cargo.toml", "Cargo.lock"]),
(Lang::Go, &["go.mod", "go.sum"]),
(Lang::Java, &["pom.xml", "build.gradle", "build.gradle.kts"]),
@@ -470,7 +500,8 @@ pub fn capture_project_dependencies_with_context(
let manifests = collect_manifest_files(spec.lang, project_root);
let lockfile = manifests.first().cloned();
- let source_closure = compute_source_closure(&entry_file, project_root, spec, summaries, callgraph);
+ let source_closure =
+ compute_source_closure(&entry_file, project_root, spec, summaries, callgraph);
CapturedDeps {
project_root: project_root.to_path_buf(),
@@ -575,13 +606,8 @@ pub fn stage_workdir_full(
Some(r) => r,
None => continue,
};
- running_bytes = copy_into_workdir(
- manifest,
- workdir,
- &rel,
- running_bytes,
- &mut staged_sources,
- )?;
+ running_bytes =
+ copy_into_workdir(manifest, workdir, &rel, running_bytes, &mut staged_sources)?;
if lockfile_in_workdir.is_none() {
lockfile_in_workdir = Some(workdir.join(&rel));
}
@@ -596,8 +622,7 @@ pub fn stage_workdir_full(
Some(r) => r,
None => PathBuf::from(cfg.file_name().unwrap_or_default()),
};
- running_bytes =
- copy_into_workdir(cfg, workdir, &rel, running_bytes, &mut staged_sources)?;
+ running_bytes = copy_into_workdir(cfg, workdir, &rel, running_bytes, &mut staged_sources)?;
}
// Phase 11 — Track D.4: populate the per-spec secret bag for every
@@ -642,14 +667,12 @@ fn copy_into_workdir(
};
let size = metadata.len();
if running_bytes.saturating_add(size) > MAX_WORKDIR_BYTES {
- return Err(io::Error::other(
- format!(
- "staged workdir would exceed {} bytes (next file `{}` = {} bytes)",
- MAX_WORKDIR_BYTES,
- rel.display(),
- size
- ),
- ));
+ return Err(io::Error::other(format!(
+ "staged workdir would exceed {} bytes (next file `{}` = {} bytes)",
+ MAX_WORKDIR_BYTES,
+ rel.display(),
+ size
+ )));
}
let dest = workdir.join(rel);
if let Some(parent) = dest.parent() {
@@ -669,8 +692,14 @@ fn resolve_under_root(project_root: &Path, entry_file: &str) -> PathBuf {
}
fn rel_under_root(path: &Path, root: &Path) -> Option {
- let abs_path = path.canonicalize().ok().unwrap_or_else(|| path.to_path_buf());
- let abs_root = root.canonicalize().ok().unwrap_or_else(|| root.to_path_buf());
+ let abs_path = path
+ .canonicalize()
+ .ok()
+ .unwrap_or_else(|| path.to_path_buf());
+ let abs_root = root
+ .canonicalize()
+ .ok()
+ .unwrap_or_else(|| root.to_path_buf());
abs_path
.strip_prefix(&abs_root)
.ok()
@@ -729,9 +758,11 @@ fn collect_config_files(entry_file: &Path, project_root: &Path) -> Vec
let mut v = Vec::new();
v.push(project_root.to_path_buf());
if let Some(parent) = entry_file.parent()
- && parent != project_root && parent.starts_with(project_root) {
- v.push(parent.to_path_buf());
- }
+ && parent != project_root
+ && parent.starts_with(project_root)
+ {
+ v.push(parent.to_path_buf());
+ }
v
};
for dir in &dirs {
@@ -1253,7 +1284,11 @@ import './local-thing';
"from flask import Flask, request\nimport os\nimport requests\n",
)
.unwrap();
- fs::write(root.join("requirements.txt"), "Flask==2.3.0\nrequests>=2.28\n").unwrap();
+ fs::write(
+ root.join("requirements.txt"),
+ "Flask==2.3.0\nrequests>=2.28\n",
+ )
+ .unwrap();
let spec = fake_spec("app.py", Lang::Python);
let captured = capture_project_dependencies(root, &spec);
assert!(captured.direct_deps.contains(&"flask".to_owned()));
diff --git a/src/dynamic/framework/adapters/go_chi.rs b/src/dynamic/framework/adapters/go_chi.rs
index 85cc43bb..c9203743 100644
--- a/src/dynamic/framework/adapters/go_chi.rs
+++ b/src/dynamic/framework/adapters/go_chi.rs
@@ -119,8 +119,10 @@ mod tests {
fn skips_when_chi_not_imported() {
let src: &[u8] = b"package main\nfunc Show() {}\n";
let tree = parse(src);
- assert!(GoChiAdapter
- .detect(&summary("Show"), tree.root_node(), src)
- .is_none());
+ assert!(
+ GoChiAdapter
+ .detect(&summary("Show"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/go_echo.rs b/src/dynamic/framework/adapters/go_echo.rs
index 55db4023..717c1737 100644
--- a/src/dynamic/framework/adapters/go_echo.rs
+++ b/src/dynamic/framework/adapters/go_echo.rs
@@ -120,8 +120,10 @@ mod tests {
fn skips_when_echo_not_imported() {
let src: &[u8] = b"package main\nfunc Show() {}\n";
let tree = parse(src);
- assert!(GoEchoAdapter
- .detect(&summary("Show"), tree.root_node(), src)
- .is_none());
+ assert!(
+ GoEchoAdapter
+ .detect(&summary("Show"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/go_fiber.rs b/src/dynamic/framework/adapters/go_fiber.rs
index 2a114d29..6c9dcbfd 100644
--- a/src/dynamic/framework/adapters/go_fiber.rs
+++ b/src/dynamic/framework/adapters/go_fiber.rs
@@ -126,8 +126,10 @@ mod tests {
fn skips_when_fiber_not_imported() {
let src: &[u8] = b"package main\nfunc Show() {}\n";
let tree = parse(src);
- assert!(GoFiberAdapter
- .detect(&summary("Show"), tree.root_node(), src)
- .is_none());
+ assert!(
+ GoFiberAdapter
+ .detect(&summary("Show"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/go_gin.rs b/src/dynamic/framework/adapters/go_gin.rs
index 7114c2b1..daad0b96 100644
--- a/src/dynamic/framework/adapters/go_gin.rs
+++ b/src/dynamic/framework/adapters/go_gin.rs
@@ -124,9 +124,11 @@ mod tests {
fn skips_when_gin_not_imported() {
let src: &[u8] = b"package main\nfunc Show(id string) {}\n";
let tree = parse(src);
- assert!(GoGinAdapter
- .detect(&summary("Show"), tree.root_node(), src)
- .is_none());
+ assert!(
+ GoGinAdapter
+ .detect(&summary("Show"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -134,9 +136,11 @@ mod tests {
let src: &[u8] =
b"package main\nimport \"github.com/gin-gonic/gin\"\nfunc init() { r := gin.Default(); r.GET(\"/users\", Show) }\nfunc Helper(x string) {}\n";
let tree = parse(src);
- assert!(GoGinAdapter
- .detect(&summary("Helper"), tree.root_node(), src)
- .is_none());
+ assert!(
+ GoGinAdapter
+ .detect(&summary("Helper"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
diff --git a/src/dynamic/framework/adapters/go_routes.rs b/src/dynamic/framework/adapters/go_routes.rs
index afc85e93..d43725c2 100644
--- a/src/dynamic/framework/adapters/go_routes.rs
+++ b/src/dynamic/framework/adapters/go_routes.rs
@@ -83,22 +83,13 @@ fn contains_any(haystack: &[u8], needles: &[&[u8]]) -> bool {
/// Find a top-level `function_declaration` or a `method_declaration`
/// whose name equals `target`. Returns the matching node.
-pub fn find_go_function<'a>(
- root: Node<'a>,
- bytes: &'a [u8],
- target: &str,
-) -> Option> {
+pub fn find_go_function<'a>(root: Node<'a>, bytes: &'a [u8], target: &str) -> Option> {
let mut hit: Option> = None;
walk_go(root, bytes, target, &mut hit);
hit
}
-fn walk_go<'a>(
- node: Node<'a>,
- bytes: &'a [u8],
- target: &str,
- out: &mut Option>,
-) {
+fn walk_go<'a>(node: Node<'a>, bytes: &'a [u8], target: &str, out: &mut Option>) {
if out.is_some() {
return;
}
@@ -136,9 +127,10 @@ pub fn go_formal_names(func: Node<'_>, bytes: &[u8]) -> Vec {
let mut pc = p.walk();
for c in p.named_children(&mut pc) {
if c.kind() == "identifier"
- && let Ok(text) = c.utf8_text(bytes) {
- out.push(text.to_owned());
- }
+ && let Ok(text) = c.utf8_text(bytes)
+ {
+ out.push(text.to_owned());
+ }
}
}
out
@@ -428,8 +420,7 @@ mod tests {
let src: &[u8] =
b"package main\nfunc init() { r := gin.New(); r.GET(\"/u/:id\", Show) }\nfunc Show(c interface{}) {}\n";
let tree = parse(src);
- let (method, path) =
- find_route_for_callee(tree.root_node(), src, "Show").expect("hit");
+ let (method, path) = find_route_for_callee(tree.root_node(), src, "Show").expect("hit");
assert_eq!(method, HttpMethod::GET);
assert_eq!(path, "/u/:id");
}
@@ -439,8 +430,7 @@ mod tests {
let src: &[u8] =
b"package main\nfunc init() { r := chi.NewRouter(); r.Get(\"/x\", controllers.Show) }\n";
let tree = parse(src);
- let (method, path) =
- find_route_for_callee(tree.root_node(), src, "Show").expect("hit");
+ let (method, path) = find_route_for_callee(tree.root_node(), src, "Show").expect("hit");
assert_eq!(method, HttpMethod::GET);
assert_eq!(path, "/x");
}
diff --git a/src/dynamic/framework/adapters/header_go.rs b/src/dynamic/framework/adapters/header_go.rs
index 1a0d530b..92e41641 100644
--- a/src/dynamic/framework/adapters/header_go.rs
+++ b/src/dynamic/framework/adapters/header_go.rs
@@ -133,9 +133,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("Set")],
..Default::default()
};
- assert!(HeaderGoAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ HeaderGoAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -146,9 +148,11 @@ mod tests {
name: "Add".into(),
..Default::default()
};
- assert!(HeaderGoAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ HeaderGoAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -174,9 +178,11 @@ mod tests {
}],
..Default::default()
};
- assert!(HeaderGoAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ HeaderGoAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -195,9 +201,11 @@ mod tests {
}],
..Default::default()
};
- assert!(HeaderGoAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ HeaderGoAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -213,8 +221,10 @@ mod tests {
],
..Default::default()
};
- assert!(HeaderGoAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ HeaderGoAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/header_java.rs b/src/dynamic/framework/adapters/header_java.rs
index 124b6b04..6021e685 100644
--- a/src/dynamic/framework/adapters/header_java.rs
+++ b/src/dynamic/framework/adapters/header_java.rs
@@ -17,7 +17,15 @@ const ADAPTER_NAME: &str = "header-java";
fn callee_is_header_setter(name: &str) -> bool {
let last = name.rsplit_once('.').map(|(_, s)| s).unwrap_or(name);
- matches!(last, "setHeader" | "addHeader" | "setDateHeader" | "addDateHeader" | "setIntHeader" | "addIntHeader")
+ matches!(
+ last,
+ "setHeader"
+ | "addHeader"
+ | "setDateHeader"
+ | "addDateHeader"
+ | "setIntHeader"
+ | "addIntHeader"
+ )
}
fn source_imports_servlet(file_bytes: &[u8]) -> bool {
@@ -110,9 +118,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("setHeader")],
..Default::default()
};
- assert!(HeaderJavaAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ HeaderJavaAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -123,9 +133,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(HeaderJavaAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ HeaderJavaAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -143,8 +155,10 @@ mod tests {
],
..Default::default()
};
- assert!(HeaderJavaAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ HeaderJavaAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/header_js.rs b/src/dynamic/framework/adapters/header_js.rs
index 52587f73..962c16a6 100644
--- a/src/dynamic/framework/adapters/header_js.rs
+++ b/src/dynamic/framework/adapters/header_js.rs
@@ -18,7 +18,10 @@ const ADAPTER_NAME: &str = "header-js";
fn callee_is_header_setter(name: &str) -> bool {
let last = name.rsplit_once('.').map(|(_, s)| s).unwrap_or(name);
- matches!(last, "setHeader" | "header" | "set" | "writeHead" | "append")
+ matches!(
+ last,
+ "setHeader" | "header" | "set" | "writeHead" | "append"
+ )
}
fn source_uses_node_http(file_bytes: &[u8]) -> bool {
@@ -115,9 +118,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("setHeader")],
..Default::default()
};
- assert!(HeaderJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ HeaderJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -128,9 +133,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(HeaderJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ HeaderJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -146,8 +153,10 @@ mod tests {
],
..Default::default()
};
- assert!(HeaderJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ HeaderJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/header_php.rs b/src/dynamic/framework/adapters/header_php.rs
index 454997ac..8b2a4230 100644
--- a/src/dynamic/framework/adapters/header_php.rs
+++ b/src/dynamic/framework/adapters/header_php.rs
@@ -106,9 +106,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("header")],
..Default::default()
};
- assert!(HeaderPhpAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ HeaderPhpAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -119,15 +121,16 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(HeaderPhpAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ HeaderPhpAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
fn skips_when_value_url_encoded() {
- let src: &[u8] =
- b" bool {
matches!(
last,
"__setitem__" | "set_header" | "setdefault" | "add_header" | "append"
- ) || matches!(name, "Response.headers.__setitem__" | "make_response" | "Response.headers.add")
+ ) || matches!(
+ name,
+ "Response.headers.__setitem__" | "make_response" | "Response.headers.add"
+ )
}
fn source_imports_python_web(file_bytes: &[u8]) -> bool {
@@ -116,9 +119,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("__setitem__")],
..Default::default()
};
- assert!(HeaderPythonAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ HeaderPythonAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -129,9 +134,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(HeaderPythonAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ HeaderPythonAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -149,8 +156,10 @@ mod tests {
],
..Default::default()
};
- assert!(HeaderPythonAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ HeaderPythonAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/header_ruby.rs b/src/dynamic/framework/adapters/header_ruby.rs
index 879c193f..f6df08c4 100644
--- a/src/dynamic/framework/adapters/header_ruby.rs
+++ b/src/dynamic/framework/adapters/header_ruby.rs
@@ -132,9 +132,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("set_header")],
..Default::default()
};
- assert!(HeaderRubyAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ HeaderRubyAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -145,9 +147,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(HeaderRubyAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ HeaderRubyAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -168,9 +172,11 @@ mod tests {
}],
..Default::default()
};
- assert!(HeaderRubyAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ HeaderRubyAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -188,9 +194,11 @@ mod tests {
}],
..Default::default()
};
- assert!(HeaderRubyAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ HeaderRubyAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -207,8 +215,10 @@ mod tests {
],
..Default::default()
};
- assert!(HeaderRubyAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ HeaderRubyAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/header_rust.rs b/src/dynamic/framework/adapters/header_rust.rs
index dae818d4..09023ff7 100644
--- a/src/dynamic/framework/adapters/header_rust.rs
+++ b/src/dynamic/framework/adapters/header_rust.rs
@@ -132,9 +132,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("insert")],
..Default::default()
};
- assert!(HeaderRustAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ HeaderRustAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -145,9 +147,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(HeaderRustAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ HeaderRustAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -173,9 +177,11 @@ mod tests {
}],
..Default::default()
};
- assert!(HeaderRustAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ HeaderRustAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -193,9 +199,11 @@ mod tests {
}],
..Default::default()
};
- assert!(HeaderRustAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ HeaderRustAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -215,8 +223,10 @@ mod tests {
],
..Default::default()
};
- assert!(HeaderRustAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ HeaderRustAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/java_deserialize.rs b/src/dynamic/framework/adapters/java_deserialize.rs
index 95fd4983..29992f94 100644
--- a/src/dynamic/framework/adapters/java_deserialize.rs
+++ b/src/dynamic/framework/adapters/java_deserialize.rs
@@ -90,8 +90,10 @@ mod tests {
name: "run".into(),
..Default::default()
};
- assert!(JavaDeserializeAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ JavaDeserializeAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/java_micronaut.rs b/src/dynamic/framework/adapters/java_micronaut.rs
index 5ea787c7..d097f490 100644
--- a/src/dynamic/framework/adapters/java_micronaut.rs
+++ b/src/dynamic/framework/adapters/java_micronaut.rs
@@ -45,10 +45,7 @@ fn class_path_prefix(class: Node<'_>, bytes: &[u8]) -> Option {
hit
}
-fn method_verb_and_path(
- method: Node<'_>,
- bytes: &[u8],
-) -> Option<(HttpMethod, String)> {
+fn method_verb_and_path(method: Node<'_>, bytes: &[u8]) -> Option<(HttpMethod, String)> {
let mut hit: Option<(HttpMethod, String)> = None;
iter_annotations(method, bytes, |ann, name| {
if hit.is_some() {
@@ -155,17 +152,21 @@ mod tests {
fn skips_non_micronaut_file() {
let src: &[u8] = b"@Controller\npublic class C {\n @GetMapping(\"/x\")\n public String x() { return \"\"; }\n}\n";
let tree = parse(src);
- assert!(JavaMicronautAdapter
- .detect(&summary("x"), tree.root_node(), src)
- .is_none());
+ assert!(
+ JavaMicronautAdapter
+ .detect(&summary("x"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
fn skips_method_without_micronaut_verb() {
let src: &[u8] = b"import io.micronaut.http.annotation.Controller;\n@Controller(\"/api\")\npublic class V {\n public String helper() { return \"\"; }\n}\n";
let tree = parse(src);
- assert!(JavaMicronautAdapter
- .detect(&summary("helper"), tree.root_node(), src)
- .is_none());
+ assert!(
+ JavaMicronautAdapter
+ .detect(&summary("helper"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/java_quarkus.rs b/src/dynamic/framework/adapters/java_quarkus.rs
index 1321ed3d..75a2805c 100644
--- a/src/dynamic/framework/adapters/java_quarkus.rs
+++ b/src/dynamic/framework/adapters/java_quarkus.rs
@@ -39,17 +39,15 @@ fn class_path_prefix(class: Node<'_>, bytes: &[u8]) -> String {
let mut prefix = String::new();
iter_annotations(class, bytes, |ann, name| {
if name == "Path"
- && let Some(p) = annotation_string_arg(ann, bytes) {
- prefix = p;
- }
+ && let Some(p) = annotation_string_arg(ann, bytes)
+ {
+ prefix = p;
+ }
});
prefix
}
-fn method_verb_and_path(
- method: Node<'_>,
- bytes: &[u8],
-) -> Option<(HttpMethod, String)> {
+fn method_verb_and_path(method: Node<'_>, bytes: &[u8]) -> Option<(HttpMethod, String)> {
let mut verb: Option = None;
let mut path = String::new();
iter_annotations(method, bytes, |ann, name| {
@@ -57,9 +55,10 @@ fn method_verb_and_path(
verb = Some(v);
}
if name == "Path"
- && let Some(p) = annotation_string_arg(ann, bytes) {
- path = p;
- }
+ && let Some(p) = annotation_string_arg(ann, bytes)
+ {
+ path = p;
+ }
});
Some((verb?, path))
}
@@ -157,17 +156,21 @@ mod tests {
fn skips_non_quarkus_file() {
let src: &[u8] = b"@RestController\npublic class C {\n @GetMapping(\"/x\")\n public String x() { return \"\"; }\n}\n";
let tree = parse(src);
- assert!(JavaQuarkusAdapter
- .detect(&summary("x"), tree.root_node(), src)
- .is_none());
+ assert!(
+ JavaQuarkusAdapter
+ .detect(&summary("x"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
fn skips_method_without_verb_annotation() {
let src: &[u8] = b"import jakarta.ws.rs.Path;\n@Path(\"/api\")\npublic class V {\n public String helper() { return \"\"; }\n}\n";
let tree = parse(src);
- assert!(JavaQuarkusAdapter
- .detect(&summary("helper"), tree.root_node(), src)
- .is_none());
+ assert!(
+ JavaQuarkusAdapter
+ .detect(&summary("helper"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/java_routes.rs b/src/dynamic/framework/adapters/java_routes.rs
index 08963efc..eed1da73 100644
--- a/src/dynamic/framework/adapters/java_routes.rs
+++ b/src/dynamic/framework/adapters/java_routes.rs
@@ -77,11 +77,7 @@ pub fn source_imports_micronaut(bytes: &[u8]) -> bool {
pub fn source_imports_servlet(bytes: &[u8]) -> bool {
let has_canonical = contains_any(
bytes,
- &[
- b"javax.servlet",
- b"jakarta.servlet",
- b"extends HttpServlet",
- ],
+ &[b"javax.servlet", b"jakarta.servlet", b"extends HttpServlet"],
);
if has_canonical {
return true;
@@ -113,12 +109,7 @@ pub fn find_class_with_method<'a>(
hit
}
-fn walk<'a>(
- node: Node<'a>,
- bytes: &[u8],
- target: &str,
- out: &mut Option<(Node<'a>, Node<'a>)>,
-) {
+fn walk<'a>(node: Node<'a>, bytes: &[u8], target: &str, out: &mut Option<(Node<'a>, Node<'a>)>) {
if out.is_some() {
return;
}
@@ -126,21 +117,22 @@ fn walk<'a>(
&& let Some(body) = node
.child_by_field_name("body")
.or_else(|| named_child_of_kind(node, "class_body"))
- {
- let mut cur = body.walk();
- for member in body.children(&mut cur) {
- if member.kind() != "method_declaration" {
- continue;
- }
- if let Some(name) = member
- .child_by_field_name("name")
- .and_then(|n| n.utf8_text(bytes).ok())
- && name == target {
- *out = Some((node, member));
- return;
- }
+ {
+ let mut cur = body.walk();
+ for member in body.children(&mut cur) {
+ if member.kind() != "method_declaration" {
+ continue;
+ }
+ if let Some(name) = member
+ .child_by_field_name("name")
+ .and_then(|n| n.utf8_text(bytes).ok())
+ && name == target
+ {
+ *out = Some((node, member));
+ return;
}
}
+ }
let mut cur = node.walk();
for child in node.children(&mut cur) {
walk(child, bytes, target, out);
@@ -173,7 +165,10 @@ pub fn annotation_string_arg(ann: Node<'_>, bytes: &[u8]) -> Option {
// Try `value = "…"` / `path = "…"` first so the keyword form is
// not accidentally captured by the bare-string scan.
for key in ["value", "path"] {
- if let Some(start) = raw.find(&format!("{key} = ")).or_else(|| raw.find(&format!("{key}="))) {
+ if let Some(start) = raw
+ .find(&format!("{key} = "))
+ .or_else(|| raw.find(&format!("{key}=")))
+ {
let after = &raw[start..];
if let Some(open) = after.find('"') {
let rest = &after[open + 1..];
@@ -300,16 +295,17 @@ pub fn extract_path_placeholders(path: &str) -> Vec {
let mut i = 0;
while i < bytes.len() {
if bytes[i] == b'{'
- && let Some(end) = bytes[i + 1..].iter().position(|&b| b == b'}') {
- let inner = &path[i + 1..i + 1 + end];
- let inner_name = inner.split(':').next().unwrap_or(inner).trim();
- let name = inner_name.strip_prefix('*').unwrap_or(inner_name);
- if !name.is_empty() && !out.iter().any(|n| n == name) {
- out.push(name.to_owned());
- }
- i += end + 2;
- continue;
+ && let Some(end) = bytes[i + 1..].iter().position(|&b| b == b'}')
+ {
+ let inner = &path[i + 1..i + 1 + end];
+ let inner_name = inner.split(':').next().unwrap_or(inner).trim();
+ let name = inner_name.strip_prefix('*').unwrap_or(inner_name);
+ if !name.is_empty() && !out.iter().any(|n| n == name) {
+ out.push(name.to_owned());
}
+ i += end + 2;
+ continue;
+ }
i += 1;
}
out
@@ -469,8 +465,7 @@ mod tests {
#[test]
fn class_extends_detects_servlet() {
- let src: &[u8] =
- b"public class V extends HttpServlet { public void doGet() {} }\n";
+ let src: &[u8] = b"public class V extends HttpServlet { public void doGet() {} }\n";
let tree = parse(src);
let (class, _) = find_class_with_method(tree.root_node(), src, "doGet").unwrap();
assert!(class_extends(class, src, "HttpServlet"));
diff --git a/src/dynamic/framework/adapters/java_servlet.rs b/src/dynamic/framework/adapters/java_servlet.rs
index 1fb92df6..0c2dfdc5 100644
--- a/src/dynamic/framework/adapters/java_servlet.rs
+++ b/src/dynamic/framework/adapters/java_servlet.rs
@@ -126,10 +126,12 @@ mod tests {
let route = binding.route.unwrap();
assert_eq!(route.method, HttpMethod::GET);
assert_eq!(route.path, "/admin");
- assert!(binding
- .request_params
- .iter()
- .all(|p| matches!(p.source, ParamSource::Implicit)));
+ assert!(
+ binding
+ .request_params
+ .iter()
+ .all(|p| matches!(p.source, ParamSource::Implicit))
+ );
}
#[test]
@@ -157,19 +159,24 @@ mod tests {
#[test]
fn skips_when_method_name_is_not_a_servlet_verb() {
- let src: &[u8] = b"public class V extends HttpServlet { public void run(HttpServletRequest req) {} }\n";
+ let src: &[u8] =
+ b"public class V extends HttpServlet { public void run(HttpServletRequest req) {} }\n";
let tree = parse(src);
- assert!(JavaServletAdapter
- .detect(&summary("run"), tree.root_node(), src)
- .is_none());
+ assert!(
+ JavaServletAdapter
+ .detect(&summary("run"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
fn skips_when_no_servlet_signature_markers() {
let src: &[u8] = b"public class V {\n public void doGet(String x) {}\n}\n";
let tree = parse(src);
- assert!(JavaServletAdapter
- .detect(&summary("doGet"), tree.root_node(), src)
- .is_none());
+ assert!(
+ JavaServletAdapter
+ .detect(&summary("doGet"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/java_spring.rs b/src/dynamic/framework/adapters/java_spring.rs
index bf71c05c..d66f7809 100644
--- a/src/dynamic/framework/adapters/java_spring.rs
+++ b/src/dynamic/framework/adapters/java_spring.rs
@@ -49,17 +49,15 @@ fn class_route_prefix(class: Node<'_>, bytes: &[u8]) -> String {
let mut prefix = String::new();
iter_annotations(class, bytes, |ann, name| {
if name == "RequestMapping"
- && let Some(p) = annotation_string_arg(ann, bytes) {
- prefix = p;
- }
+ && let Some(p) = annotation_string_arg(ann, bytes)
+ {
+ prefix = p;
+ }
});
prefix
}
-fn method_route(
- method: Node<'_>,
- bytes: &[u8],
-) -> Option<(HttpMethod, String)> {
+fn method_route(method: Node<'_>, bytes: &[u8]) -> Option<(HttpMethod, String)> {
let mut hit: Option<(HttpMethod, String)> = None;
iter_annotations(method, bytes, |ann, name| {
if hit.is_some() {
@@ -100,7 +98,10 @@ impl FrameworkAdapter for JavaSpringAdapter {
// Quarkus / JAX-RS files often re-use `@Path` but the brief
// routes those through `java-quarkus`; skip when the file
// looks like Quarkus and is not also a Spring controller.
- if source_imports_quarkus(file_bytes) && !file_bytes.windows(15).any(|w| w == b"@RestController") && !file_bytes.windows(11).any(|w| w == b"@Controller") {
+ if source_imports_quarkus(file_bytes)
+ && !file_bytes.windows(15).any(|w| w == b"@RestController")
+ && !file_bytes.windows(11).any(|w| w == b"@Controller")
+ {
return None;
}
let (class, method) = find_class_with_method(ast, file_bytes, &summary.name)?;
@@ -210,26 +211,32 @@ mod tests {
let src: &[u8] =
b"@RequestMapping(\"/api\")\npublic class C {\n @GetMapping(\"/x\")\n public String x() { return \"\"; }\n}\n";
let tree = parse(src);
- assert!(JavaSpringAdapter
- .detect(&summary("x"), tree.root_node(), src)
- .is_none());
+ assert!(
+ JavaSpringAdapter
+ .detect(&summary("x"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
fn skips_quarkus_file() {
let src: &[u8] = b"import io.quarkus.runtime.Quarkus;\nimport jakarta.ws.rs.GET;\nimport jakarta.ws.rs.Path;\n@Path(\"/run\")\npublic class Q {\n @GET\n public String run() { return \"\"; }\n}\n";
let tree = parse(src);
- assert!(JavaSpringAdapter
- .detect(&summary("run"), tree.root_node(), src)
- .is_none());
+ assert!(
+ JavaSpringAdapter
+ .detect(&summary("run"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
fn skips_plain_function() {
let src: &[u8] = b"public class C { public int add(int a, int b) { return a + b; } }\n";
let tree = parse(src);
- assert!(JavaSpringAdapter
- .detect(&summary("add"), tree.root_node(), src)
- .is_none());
+ assert!(
+ JavaSpringAdapter
+ .detect(&summary("add"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/java_thymeleaf.rs b/src/dynamic/framework/adapters/java_thymeleaf.rs
index 8494a673..51133187 100644
--- a/src/dynamic/framework/adapters/java_thymeleaf.rs
+++ b/src/dynamic/framework/adapters/java_thymeleaf.rs
@@ -123,9 +123,11 @@ mod tests {
let src: &[u8] = b"import org.thymeleaf.TemplateEngine;\npublic class V { public static String run(String body) { TemplateEngine e = new TemplateEngine(); return e.process(body, null); } }\n";
let tree = parse_java(src);
let summary = summary_for("run", &["body"], &[0]);
- assert!(JavaThymeleafAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ JavaThymeleafAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -137,9 +139,11 @@ mod tests {
name: "run".into(),
..Default::default()
};
- assert!(JavaThymeleafAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ JavaThymeleafAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -149,9 +153,11 @@ mod tests {
let src: &[u8] = b"// org.thymeleaf.TemplateEngine is great\npublic class V { public static String run(String body) { TemplateEngine e = new TemplateEngine(); return e.process(\"static\", null); } }\n";
let tree = parse_java(src);
let summary = summary_for("run", &["body"], &[0]);
- assert!(JavaThymeleafAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ JavaThymeleafAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -159,8 +165,10 @@ mod tests {
let src: &[u8] = b"import org.thymeleaf.TemplateEngine;\npublic class V { public static String run(String body) { TemplateEngine e = new TemplateEngine(); return e.process(body, null); } }\n";
let tree = parse_java(src);
let summary = summary_for("run", &["body"], &[]);
- assert!(JavaThymeleafAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ JavaThymeleafAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/js_express.rs b/src/dynamic/framework/adapters/js_express.rs
index 9d7c04e4..a3643d54 100644
--- a/src/dynamic/framework/adapters/js_express.rs
+++ b/src/dynamic/framework/adapters/js_express.rs
@@ -107,10 +107,18 @@ mod tests {
let route = binding.route.as_ref().unwrap();
assert_eq!(route.method, HttpMethod::GET);
assert_eq!(route.path, "/users/:id");
- assert!(binding.request_params.iter().any(|p| p.name == "req"
- && matches!(p.source, ParamSource::Implicit)));
- assert!(binding.request_params.iter().any(|p| p.name == "res"
- && matches!(p.source, ParamSource::Implicit)));
+ assert!(
+ binding
+ .request_params
+ .iter()
+ .any(|p| p.name == "req" && matches!(p.source, ParamSource::Implicit))
+ );
+ assert!(
+ binding
+ .request_params
+ .iter()
+ .any(|p| p.name == "res" && matches!(p.source, ParamSource::Implicit))
+ );
}
#[test]
@@ -147,9 +155,11 @@ mod tests {
function handler(ctx) { ctx.body = 'ok'; }\n\
app.get('/x', handler);\n";
let tree = parse_js(src);
- assert!(JsExpressAdapter
- .detect(&summary("handler"), tree.root_node(), src)
- .is_none());
+ assert!(
+ JsExpressAdapter
+ .detect(&summary("handler"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -159,8 +169,10 @@ mod tests {
function other(req, res) { res.send('x'); }\n\
app.get('/x', other);\n";
let tree = parse_js(src);
- assert!(JsExpressAdapter
- .detect(&summary("missing"), tree.root_node(), src)
- .is_none());
+ assert!(
+ JsExpressAdapter
+ .detect(&summary("missing"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/js_fastify.rs b/src/dynamic/framework/adapters/js_fastify.rs
index 5f04d2bc..3889fec7 100644
--- a/src/dynamic/framework/adapters/js_fastify.rs
+++ b/src/dynamic/framework/adapters/js_fastify.rs
@@ -148,8 +148,10 @@ mod tests {
function h(req, res) {}\n\
app.get('/x', h);\n";
let tree = parse_js(src);
- assert!(JsFastifyAdapter
- .detect(&summary("h"), tree.root_node(), src)
- .is_none());
+ assert!(
+ JsFastifyAdapter
+ .detect(&summary("h"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/js_handlebars.rs b/src/dynamic/framework/adapters/js_handlebars.rs
index 84faa6f0..750419e1 100644
--- a/src/dynamic/framework/adapters/js_handlebars.rs
+++ b/src/dynamic/framework/adapters/js_handlebars.rs
@@ -139,9 +139,11 @@ mod tests {
let src: &[u8] = b"const Handlebars = require('handlebars');\nfunction render(body) {\n return Handlebars.compile(body)({});\n}\n";
let tree = parse_js(src);
let summary = summary_for("render", &["body"], &[0]);
- assert!(JsHandlebarsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ JsHandlebarsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -152,9 +154,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(JsHandlebarsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ JsHandlebarsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -162,9 +166,11 @@ mod tests {
let src: &[u8] = b"// uses Handlebars\nfunction render(body) {\n return Handlebars.compile(\"static\")({});\n}\n";
let tree = parse_js(src);
let summary = summary_for("render", &["body"], &[0]);
- assert!(JsHandlebarsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ JsHandlebarsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -172,8 +178,10 @@ mod tests {
let src: &[u8] = b"const Handlebars = require('handlebars');\nfunction render(body) {\n return Handlebars.compile(body)({});\n}\n";
let tree = parse_js(src);
let summary = summary_for("render", &["body"], &[]);
- assert!(JsHandlebarsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ JsHandlebarsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/js_koa.rs b/src/dynamic/framework/adapters/js_koa.rs
index 3a6d2a0e..5be0d332 100644
--- a/src/dynamic/framework/adapters/js_koa.rs
+++ b/src/dynamic/framework/adapters/js_koa.rs
@@ -39,22 +39,13 @@ fn receiver_looks_like_koa(name: &str) -> bool {
/// that reference `target`. Returns the matched call node so callers
/// can stamp a middleware-shape binding when the verb-based dispatch
/// fails to fire.
-fn find_use_middleware<'a>(
- root: Node<'a>,
- bytes: &[u8],
- target: &str,
-) -> Option> {
+fn find_use_middleware<'a>(root: Node<'a>, bytes: &[u8], target: &str) -> Option> {
let mut hit: Option> = None;
walk_for_use(root, bytes, target, &mut hit);
hit
}
-fn walk_for_use<'a>(
- node: Node<'a>,
- bytes: &[u8],
- target: &str,
- out: &mut Option>,
-) {
+fn walk_for_use<'a>(node: Node<'a>, bytes: &[u8], target: &str, out: &mut Option>) {
if out.is_some() {
return;
}
@@ -108,8 +99,7 @@ impl FrameworkAdapter for JsKoaAdapter {
.unwrap_or_default();
bind_path_params(&formals, path)
};
- if let Some((method, path)) =
- find_route_registration(ast, file_bytes, &summary.name, &recv)
+ if let Some((method, path)) = find_route_registration(ast, file_bytes, &summary.name, &recv)
{
let request_params = formals_for(&path);
return Some(FrameworkBinding {
@@ -180,8 +170,12 @@ mod tests {
let route = binding.route.as_ref().unwrap();
assert_eq!(route.method, HttpMethod::GET);
assert_eq!(route.path, "/users/:id");
- assert!(binding.request_params.iter().any(|p| p.name == "ctx"
- && matches!(p.source, ParamSource::Implicit)));
+ assert!(
+ binding
+ .request_params
+ .iter()
+ .any(|p| p.name == "ctx" && matches!(p.source, ParamSource::Implicit))
+ );
}
#[test]
@@ -205,8 +199,10 @@ mod tests {
function h(req, res) {}\n\
router.get('/x', h);\n";
let tree = parse_js(src);
- assert!(JsKoaAdapter
- .detect(&summary("h"), tree.root_node(), src)
- .is_none());
+ assert!(
+ JsKoaAdapter
+ .detect(&summary("h"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/js_nest.rs b/src/dynamic/framework/adapters/js_nest.rs
index bc5ced6f..8ac608d6 100644
--- a/src/dynamic/framework/adapters/js_nest.rs
+++ b/src/dynamic/framework/adapters/js_nest.rs
@@ -84,8 +84,7 @@ fn detect_nest(
if !source_imports_nest(file_bytes) {
return None;
}
- let (class_node, method_node) =
- find_class_method(ast, file_bytes, &summary.name)?;
+ let (class_node, method_node) = find_class_method(ast, file_bytes, &summary.name)?;
let prefix = class_controller_prefix(class_node, file_bytes)?;
let (method, sub_path) = method_verb_and_path(method_node, file_bytes)?;
let full_path = join_paths(&prefix, &sub_path);
@@ -213,10 +212,7 @@ fn class_controller_prefix(class_node: Node<'_>, bytes: &[u8]) -> Option
/// with one of the Nest verb decorators (`@Get`, `@Post`, ...). The
/// `sub_path` is `""` when the decorator carries no argument
/// (`@Get()` mounts at the controller prefix root).
-fn method_verb_and_path(
- method_node: Node<'_>,
- bytes: &[u8],
-) -> Option<(HttpMethod, String)> {
+fn method_verb_and_path(method_node: Node<'_>, bytes: &[u8]) -> Option<(HttpMethod, String)> {
const VERBS: &[&str] = &[
"Get", "Head", "Post", "Put", "Patch", "Delete", "Options", "All",
];
@@ -461,8 +457,7 @@ mod tests {
fn parse_ts(src: &[u8]) -> tree_sitter::Tree {
let mut parser = tree_sitter::Parser::new();
- let lang =
- tree_sitter::Language::from(tree_sitter_typescript::LANGUAGE_TYPESCRIPT);
+ let lang = tree_sitter::Language::from(tree_sitter_typescript::LANGUAGE_TYPESCRIPT);
parser.set_language(&lang).unwrap();
parser.parse(src, None).unwrap()
}
@@ -562,8 +557,10 @@ mod tests {
compute(x: number) { return x + 1; }\n\
}\n";
let tree = parse_ts(src);
- assert!(TsNestAdapter
- .detect(&summary("compute", "typescript"), tree.root_node(), src)
- .is_none());
+ assert!(
+ TsNestAdapter
+ .detect(&summary("compute", "typescript"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/js_routes.rs b/src/dynamic/framework/adapters/js_routes.rs
index 15d829d6..bc1e003f 100644
--- a/src/dynamic/framework/adapters/js_routes.rs
+++ b/src/dynamic/framework/adapters/js_routes.rs
@@ -140,22 +140,13 @@ pub fn strip_quotes(raw: &str) -> &str {
/// arrow function whose binding name equals `target`. Returns the
/// `formal_parameters` (or `formal_parameter` for shorthand arrows)
/// node so callers can enumerate parameter names.
-pub fn find_function_params<'a>(
- root: Node<'a>,
- bytes: &[u8],
- target: &str,
-) -> Option> {
+pub fn find_function_params<'a>(root: Node<'a>, bytes: &[u8], target: &str) -> Option> {
let mut hit: Option> = None;
walk_for_params(root, bytes, target, &mut hit);
hit
}
-fn walk_for_params<'a>(
- node: Node<'a>,
- bytes: &[u8],
- target: &str,
- out: &mut Option>,
-) {
+fn walk_for_params<'a>(node: Node<'a>, bytes: &[u8], target: &str, out: &mut Option>) {
if out.is_some() {
return;
}
@@ -311,15 +302,7 @@ pub fn bind_path_params(formals: &[String], path: &str) -> Vec {
fn is_implicit_formal(name: &str) -> bool {
matches!(
name,
- "req"
- | "request"
- | "res"
- | "response"
- | "reply"
- | "ctx"
- | "context"
- | "next"
- | "done"
+ "req" | "request" | "res" | "response" | "reply" | "ctx" | "context" | "next" | "done"
)
}
@@ -349,9 +332,7 @@ pub fn extract_path_placeholders(path: &str) -> Vec {
b':' => {
let start = i + 1;
let mut j = start;
- while j < bytes.len()
- && (bytes[j].is_ascii_alphanumeric() || bytes[j] == b'_')
- {
+ while j < bytes.len() && (bytes[j].is_ascii_alphanumeric() || bytes[j] == b'_') {
j += 1;
}
if j > start {
@@ -456,10 +437,11 @@ fn walk_for_registration<'a>(
&& receiver_accepts(last_segment(object_text))
&& let Some(args) = node.child_by_field_name("arguments")
&& call_args_reference_target(args, bytes, target)
- && let Some(path) = first_string_arg(args, bytes) {
- *out = Some((method, path));
- return;
- }
+ && let Some(path) = first_string_arg(args, bytes)
+ {
+ *out = Some((method, path));
+ return;
+ }
// Fastify options-object: `fastify.route({ method, url, handler })`.
if prop_text == "route"
&& receiver_accepts(last_segment(object_text))
@@ -507,11 +489,7 @@ pub fn first_string_arg(args: Node<'_>, bytes: &[u8]) -> Option {
/// Parse a Fastify options-object call `fastify.route({ method, url,
/// handler })` returning the bound `(method, url)` when the
/// `handler:` property references `target`.
-fn parse_options_route(
- args: Node<'_>,
- bytes: &[u8],
- target: &str,
-) -> Option<(HttpMethod, String)> {
+fn parse_options_route(args: Node<'_>, bytes: &[u8], target: &str) -> Option<(HttpMethod, String)> {
let mut cur = args.walk();
for c in args.named_children(&mut cur) {
if c.kind() != "object" {
@@ -525,7 +503,9 @@ fn parse_options_route(
if pair.kind() != "pair" {
continue;
}
- let Some(key) = pair.child_by_field_name("key").and_then(|n| n.utf8_text(bytes).ok())
+ let Some(key) = pair
+ .child_by_field_name("key")
+ .and_then(|n| n.utf8_text(bytes).ok())
else {
continue;
};
diff --git a/src/dynamic/framework/adapters/kafka_java.rs b/src/dynamic/framework/adapters/kafka_java.rs
index 849e396b..7a206d87 100644
--- a/src/dynamic/framework/adapters/kafka_java.rs
+++ b/src/dynamic/framework/adapters/kafka_java.rs
@@ -35,7 +35,12 @@ fn source_imports_kafka(file_bytes: &[u8]) -> bool {
fn extract_topic(file_bytes: &[u8]) -> String {
let text = std::str::from_utf8(file_bytes).unwrap_or("");
- for needle in ["topics = \"", "topics=\"", "topics = {\"", "subscribe(Arrays.asList(\""] {
+ for needle in [
+ "topics = \"",
+ "topics=\"",
+ "topics = {\"",
+ "subscribe(Arrays.asList(\"",
+ ] {
if let Some(idx) = text.find(needle) {
let after = &text[idx + needle.len()..];
if let Some(end) = after.find('"') {
diff --git a/src/dynamic/framework/adapters/kafka_python.rs b/src/dynamic/framework/adapters/kafka_python.rs
index c1c98b15..8a91db70 100644
--- a/src/dynamic/framework/adapters/kafka_python.rs
+++ b/src/dynamic/framework/adapters/kafka_python.rs
@@ -129,8 +129,10 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(KafkaPythonAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ KafkaPythonAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/ldap_php.rs b/src/dynamic/framework/adapters/ldap_php.rs
index b732ccbc..50915bce 100644
--- a/src/dynamic/framework/adapters/ldap_php.rs
+++ b/src/dynamic/framework/adapters/ldap_php.rs
@@ -173,9 +173,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("ldap_search")],
..Default::default()
};
- assert!(LdapPhpAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ LdapPhpAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -186,9 +188,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(LdapPhpAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ LdapPhpAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -203,8 +207,10 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("ldap_search")],
..Default::default()
};
- assert!(LdapPhpAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ LdapPhpAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/ldap_python.rs b/src/dynamic/framework/adapters/ldap_python.rs
index 2d194989..9ed9fb2f 100644
--- a/src/dynamic/framework/adapters/ldap_python.rs
+++ b/src/dynamic/framework/adapters/ldap_python.rs
@@ -168,9 +168,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("search_s")],
..Default::default()
};
- assert!(LdapPythonAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ LdapPythonAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -181,9 +183,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(LdapPythonAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ LdapPythonAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -198,8 +202,10 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("search_s")],
..Default::default()
};
- assert!(LdapPythonAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ LdapPythonAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/ldap_spring.rs b/src/dynamic/framework/adapters/ldap_spring.rs
index 5d48ac8b..504b3b00 100644
--- a/src/dynamic/framework/adapters/ldap_spring.rs
+++ b/src/dynamic/framework/adapters/ldap_spring.rs
@@ -205,9 +205,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(LdapSpringAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ LdapSpringAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -225,8 +227,10 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("search")],
..Default::default()
};
- assert!(LdapSpringAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ LdapSpringAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/middleware_django.rs b/src/dynamic/framework/adapters/middleware_django.rs
index c84f6fbd..12b727d2 100644
--- a/src/dynamic/framework/adapters/middleware_django.rs
+++ b/src/dynamic/framework/adapters/middleware_django.rs
@@ -17,11 +17,7 @@ fn callee_is_django_middleware(name: &str) -> bool {
let last = name.rsplit_once('.').map(|(_, s)| s).unwrap_or(name);
matches!(
last,
- "process_request"
- | "process_response"
- | "process_view"
- | "process_exception"
- | "__call__"
+ "process_request" | "process_response" | "process_view" | "process_exception" | "__call__"
)
}
diff --git a/src/dynamic/framework/adapters/middleware_express.rs b/src/dynamic/framework/adapters/middleware_express.rs
index 4787e005..d48cf1c6 100644
--- a/src/dynamic/framework/adapters/middleware_express.rs
+++ b/src/dynamic/framework/adapters/middleware_express.rs
@@ -15,10 +15,7 @@ const ADAPTER_NAME: &str = "middleware-express";
fn callee_is_express(name: &str) -> bool {
let last = name.rsplit_once('.').map(|(_, s)| s).unwrap_or(name);
- matches!(
- last,
- "use" | "next" | "json" | "urlencoded" | "static"
- )
+ matches!(last, "use" | "next" | "json" | "urlencoded" | "static")
}
fn source_imports_express(file_bytes: &[u8]) -> bool {
@@ -27,11 +24,7 @@ fn source_imports_express(file_bytes: &[u8]) -> bool {
// import. Many non-middleware Express fixtures import the framework
// but never declare middleware; gating on the registration shape
// keeps the adapter focused on the function the brief targets.
- const NEEDLES: &[&[u8]] = &[
- b"app.use(",
- b"router.use(",
- b"express.Router()",
- ];
+ const NEEDLES: &[&[u8]] = &[b"app.use(", b"router.use(", b"express.Router()"];
NEEDLES
.iter()
.any(|n| file_bytes.windows(n.len()).any(|w| w == *n))
diff --git a/src/dynamic/framework/adapters/migration_rails.rs b/src/dynamic/framework/adapters/migration_rails.rs
index 80f0dc29..06820183 100644
--- a/src/dynamic/framework/adapters/migration_rails.rs
+++ b/src/dynamic/framework/adapters/migration_rails.rs
@@ -17,8 +17,7 @@ fn callee_is_rails_migration(name: &str) -> bool {
let last = name.rsplit_once('.').map(|(_, s)| s).unwrap_or(name);
matches!(
last,
- "up"
- | "down"
+ "up" | "down"
| "change"
| "create_table"
| "add_column"
diff --git a/src/dynamic/framework/adapters/migration_sequelize.rs b/src/dynamic/framework/adapters/migration_sequelize.rs
index 8665f07e..94e44e44 100644
--- a/src/dynamic/framework/adapters/migration_sequelize.rs
+++ b/src/dynamic/framework/adapters/migration_sequelize.rs
@@ -17,13 +17,7 @@ fn callee_is_sequelize_migration(name: &str) -> bool {
let last = name.rsplit_once('.').map(|(_, s)| s).unwrap_or(name);
matches!(
last,
- "up"
- | "down"
- | "createTable"
- | "addColumn"
- | "dropTable"
- | "removeColumn"
- | "addIndex"
+ "up" | "down" | "createTable" | "addColumn" | "dropTable" | "removeColumn" | "addIndex"
)
}
diff --git a/src/dynamic/framework/adapters/mod.rs b/src/dynamic/framework/adapters/mod.rs
index de81d408..be75f4bb 100644
--- a/src/dynamic/framework/adapters/mod.rs
+++ b/src/dynamic/framework/adapters/mod.rs
@@ -11,6 +11,16 @@
//! the route / framework adapters; the per-cap sink adapters live
//! here so the per-language verticals can ship independently.
+pub mod go_chi;
+pub mod go_echo;
+pub mod go_fiber;
+pub mod go_gin;
+pub mod go_routes;
+pub mod graphql_apollo;
+pub mod graphql_gqlgen;
+pub mod graphql_graphene;
+pub mod graphql_juniper;
+pub mod graphql_relay;
pub mod header_go;
pub mod header_java;
pub mod header_js;
@@ -18,11 +28,6 @@ pub mod header_php;
pub mod header_python;
pub mod header_ruby;
pub mod header_rust;
-pub mod go_chi;
-pub mod go_echo;
-pub mod go_fiber;
-pub mod go_gin;
-pub mod go_routes;
pub mod java_deserialize;
pub mod java_micronaut;
pub mod java_quarkus;
@@ -36,11 +41,6 @@ pub mod js_handlebars;
pub mod js_koa;
pub mod js_nest;
pub mod js_routes;
-pub mod graphql_apollo;
-pub mod graphql_gqlgen;
-pub mod graphql_graphene;
-pub mod graphql_juniper;
-pub mod graphql_relay;
pub mod kafka_java;
pub mod kafka_python;
pub mod ldap_php;
@@ -117,6 +117,15 @@ pub mod xxe_php;
pub mod xxe_python;
pub mod xxe_ruby;
+pub use go_chi::GoChiAdapter;
+pub use go_echo::GoEchoAdapter;
+pub use go_fiber::GoFiberAdapter;
+pub use go_gin::GoGinAdapter;
+pub use graphql_apollo::GraphqlApolloAdapter;
+pub use graphql_gqlgen::GraphqlGqlgenAdapter;
+pub use graphql_graphene::GraphqlGrapheneAdapter;
+pub use graphql_juniper::GraphqlJuniperAdapter;
+pub use graphql_relay::GraphqlRelayAdapter;
pub use header_go::HeaderGoAdapter;
pub use header_java::HeaderJavaAdapter;
pub use header_js::HeaderJsAdapter;
@@ -124,10 +133,6 @@ pub use header_php::HeaderPhpAdapter;
pub use header_python::HeaderPythonAdapter;
pub use header_ruby::HeaderRubyAdapter;
pub use header_rust::HeaderRustAdapter;
-pub use go_chi::GoChiAdapter;
-pub use go_echo::GoEchoAdapter;
-pub use go_fiber::GoFiberAdapter;
-pub use go_gin::GoGinAdapter;
pub use java_deserialize::JavaDeserializeAdapter;
pub use java_micronaut::JavaMicronautAdapter;
pub use java_quarkus::JavaQuarkusAdapter;
@@ -139,11 +144,6 @@ pub use js_fastify::JsFastifyAdapter;
pub use js_handlebars::JsHandlebarsAdapter;
pub use js_koa::JsKoaAdapter;
pub use js_nest::{JsNestAdapter, TsNestAdapter};
-pub use graphql_apollo::GraphqlApolloAdapter;
-pub use graphql_gqlgen::GraphqlGqlgenAdapter;
-pub use graphql_graphene::GraphqlGrapheneAdapter;
-pub use graphql_juniper::GraphqlJuniperAdapter;
-pub use graphql_relay::GraphqlRelayAdapter;
pub use kafka_java::KafkaJavaAdapter;
pub use kafka_python::KafkaPythonAdapter;
pub use ldap_php::LdapPhpAdapter;
@@ -221,10 +221,7 @@ fn any_callee_matches(
summary: &crate::summary::FuncSummary,
predicate: impl Fn(&str) -> bool,
) -> bool {
- summary
- .callees
- .iter()
- .any(|c| predicate(c.name.as_str()))
+ summary.callees.iter().any(|c| predicate(c.name.as_str()))
}
/// True when any callee in `summary.callees` matches `name_pred` AND
@@ -270,10 +267,7 @@ fn any_callee_matches_with_receiver(
/// Per-language sigil stripping covers PHP (`$x`), Ruby (`@x`), and
/// Java/Python/JS (no sigil). Leading whitespace is also trimmed so
/// adapters can pass the raw `utf8_text` of the argument node.
-pub(super) fn arg_is_tainted_param(
- summary: &crate::summary::FuncSummary,
- arg_text: &str,
-) -> bool {
+pub(super) fn arg_is_tainted_param(summary: &crate::summary::FuncSummary, arg_text: &str) -> bool {
fn strip(s: &str) -> &str {
s.trim()
.trim_start_matches('$')
@@ -281,15 +275,10 @@ pub(super) fn arg_is_tainted_param(
.trim_start_matches('&')
}
let needle = strip(arg_text);
- let Some(idx) = summary
- .param_names
- .iter()
- .position(|p| strip(p) == needle)
- else {
+ let Some(idx) = summary.param_names.iter().position(|p| strip(p) == needle) else {
return false;
};
- summary.tainted_sink_params.contains(&idx)
- || summary.propagating_params.contains(&idx)
+ summary.tainted_sink_params.contains(&idx) || summary.propagating_params.contains(&idx)
}
/// True when any descendant identifier in `node`'s subtree resolves to
diff --git a/src/dynamic/framework/adapters/nats_go.rs b/src/dynamic/framework/adapters/nats_go.rs
index 77b0bae7..c494a62c 100644
--- a/src/dynamic/framework/adapters/nats_go.rs
+++ b/src/dynamic/framework/adapters/nats_go.rs
@@ -18,11 +18,7 @@ fn callee_is_nats(name: &str) -> bool {
}
fn source_imports_nats(file_bytes: &[u8]) -> bool {
- const NEEDLES: &[&[u8]] = &[
- b"github.com/nats-io/nats.go",
- b"nats.Connect",
- b"nats.Msg",
- ];
+ const NEEDLES: &[&[u8]] = &[b"github.com/nats-io/nats.go", b"nats.Connect", b"nats.Msg"];
NEEDLES
.iter()
.any(|n| file_bytes.windows(n.len()).any(|w| w == *n))
diff --git a/src/dynamic/framework/adapters/php_codeigniter.rs b/src/dynamic/framework/adapters/php_codeigniter.rs
index 1515e94d..fe7111ad 100644
--- a/src/dynamic/framework/adapters/php_codeigniter.rs
+++ b/src/dynamic/framework/adapters/php_codeigniter.rs
@@ -11,9 +11,9 @@
//! inner name (after the `:`) for each so a `$id` formal whose name
//! matches the placeholder binds as [`super::super::ParamSource::PathSegment`].
-use crate::dynamic::framework::{FrameworkAdapter, FrameworkBinding, RouteShape};
#[cfg(test)]
use crate::dynamic::framework::HttpMethod;
+use crate::dynamic::framework::{FrameworkAdapter, FrameworkBinding, RouteShape};
use crate::evidence::EntryKind;
use crate::summary::FuncSummary;
use crate::symbol::Lang;
@@ -49,8 +49,7 @@ impl FrameworkAdapter for PhpCodeIgniterAdapter {
let (func_node, class) = find_php_function(ast, file_bytes, &summary.name)?;
let controller = class.and_then(|c| php_class_name(c, file_bytes));
- let (method, path) =
- find_codeigniter_route(ast, file_bytes, &summary.name, controller)?;
+ let (method, path) = find_codeigniter_route(ast, file_bytes, &summary.name, controller)?;
let formals = php_formal_names(func_node, file_bytes);
let request_params = bind_php_path_params(&formals, &path);
@@ -120,17 +119,21 @@ mod tests {
fn skips_when_codeigniter_not_imported() {
let src: &[u8] = b"get('users/(:num)', 'UserController::show');\n";
let tree = parse(src);
- assert!(PhpCodeIgniterAdapter
- .detect(&summary("show"), tree.root_node(), src)
- .is_none());
+ assert!(
+ PhpCodeIgniterAdapter
+ .detect(&summary("show"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
fn skips_when_callable_does_not_reference_method() {
let src: &[u8] = b"get('users/(:num)', 'UserController::show');\nclass UserController extends BaseController {\n public function helper($x) { return $x; }\n}\n";
let tree = parse(src);
- assert!(PhpCodeIgniterAdapter
- .detect(&summary("helper"), tree.root_node(), src)
- .is_none());
+ assert!(
+ PhpCodeIgniterAdapter
+ .detect(&summary("helper"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/php_laravel.rs b/src/dynamic/framework/adapters/php_laravel.rs
index a1b70534..857e1f2d 100644
--- a/src/dynamic/framework/adapters/php_laravel.rs
+++ b/src/dynamic/framework/adapters/php_laravel.rs
@@ -12,9 +12,9 @@
//! a `class UserController { public function show($id) {…} }`
//! declaration in the same file.
-use crate::dynamic::framework::{FrameworkAdapter, FrameworkBinding, RouteShape};
#[cfg(test)]
use crate::dynamic::framework::HttpMethod;
+use crate::dynamic::framework::{FrameworkAdapter, FrameworkBinding, RouteShape};
use crate::evidence::EntryKind;
use crate::summary::FuncSummary;
use crate::symbol::Lang;
@@ -50,8 +50,7 @@ impl FrameworkAdapter for PhpLaravelAdapter {
let (func_node, class) = find_php_function(ast, file_bytes, &summary.name)?;
let controller = class.and_then(|c| php_class_name(c, file_bytes));
- let (method, path) =
- find_laravel_static_route(ast, file_bytes, &summary.name, controller)?;
+ let (method, path) = find_laravel_static_route(ast, file_bytes, &summary.name, controller)?;
let formals = php_formal_names(func_node, file_bytes);
let request_params = bind_php_path_params(&formals, &path);
@@ -143,17 +142,21 @@ mod tests {
fn skips_when_laravel_not_imported() {
let src: &[u8] = b"(
&& let Some(name) = node
.child_by_field_name("name")
.and_then(|n| n.utf8_text(bytes).ok())
- && name == target {
- let klass = if node.kind() == "method_declaration" {
- here_class
- } else {
- None
- };
- *out = Some((node, klass));
- return;
- }
+ && name == target
+ {
+ let klass = if node.kind() == "method_declaration" {
+ here_class
+ } else {
+ None
+ };
+ *out = Some((node, klass));
+ return;
+ }
let mut cur = node.walk();
for child in node.children(&mut cur) {
walk(child, bytes, target, here_class, out);
@@ -511,10 +512,7 @@ fn laravel_callable_matches(
}
}
-fn parse_array_callable<'a>(
- array: Node<'a>,
- bytes: &'a [u8],
-) -> Option<(Option, String)> {
+fn parse_array_callable<'a>(array: Node<'a>, bytes: &'a [u8]) -> Option<(Option, String)> {
let mut cur = array.walk();
let elements: Vec> = array
.named_children(&mut cur)
@@ -544,10 +542,7 @@ fn split_laravel_callable(literal: &str) -> (Option, String) {
fn leaf(qualified: &str) -> &str {
let last_backslash = qualified.rsplit('\\').next().unwrap_or(qualified);
- last_backslash
- .rsplit("::")
- .next()
- .unwrap_or(last_backslash)
+ last_backslash.rsplit("::").next().unwrap_or(last_backslash)
}
fn verb_method(verb: &str) -> Option {
@@ -711,18 +706,12 @@ mod tests {
extract_php_path_placeholders("/u/{id}/p/{slug?}"),
vec!["id", "slug"]
);
- assert_eq!(
- extract_php_path_placeholders("/u/{id:[0-9]+}"),
- vec!["id"]
- );
+ assert_eq!(extract_php_path_placeholders("/u/{id:[0-9]+}"), vec!["id"]);
}
#[test]
fn extracts_codeigniter_placeholders() {
- assert_eq!(
- extract_php_path_placeholders("users/(:num)"),
- vec!["num"]
- );
+ assert_eq!(extract_php_path_placeholders("users/(:num)"), vec!["num"]);
assert_eq!(
extract_php_path_placeholders("p/(:any)/c/(:segment)"),
vec!["any", "segment"]
@@ -778,20 +767,16 @@ mod tests {
fn finds_laravel_static_route_with_string_callable() {
let src: &[u8] = b"get('users/(:num)', 'UserController::show');\n";
let tree = parse(src);
- let hit = find_codeigniter_route(
- tree.root_node(),
- src,
- "show",
- Some("UserController"),
- )
- .unwrap();
+ let hit =
+ find_codeigniter_route(tree.root_node(), src, "show", Some("UserController")).unwrap();
assert_eq!(hit.0, HttpMethod::GET);
assert_eq!(hit.1, "users/(:num)");
}
diff --git a/src/dynamic/framework/adapters/php_symfony.rs b/src/dynamic/framework/adapters/php_symfony.rs
index 51fa51ea..a76320e8 100644
--- a/src/dynamic/framework/adapters/php_symfony.rs
+++ b/src/dynamic/framework/adapters/php_symfony.rs
@@ -165,17 +165,21 @@ mod tests {
fn skips_when_symfony_not_imported() {
let src: &[u8] = b"createTemplate($body);\n return $tpl->render([]);\n}\n";
let tree = parse_php(src);
let summary = summary_for("render", &["body", "twig"], &[0]);
- assert!(PhpTwigAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ PhpTwigAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -158,9 +160,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(PhpTwigAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PhpTwigAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -170,9 +174,11 @@ mod tests {
let src: &[u8] = b"createTemplate('static');\n return $tpl->render([]);\n}\n";
let tree = parse_php(src);
let summary = summary_for("render", &["body", "twig"], &[0]);
- assert!(PhpTwigAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PhpTwigAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -180,8 +186,10 @@ mod tests {
let src: &[u8] = b"createTemplate($body);\n return $tpl->render([]);\n}\n";
let tree = parse_php(src);
let summary = summary_for("render", &["body", "twig"], &[]);
- assert!(PhpTwigAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PhpTwigAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/php_unserialize.rs b/src/dynamic/framework/adapters/php_unserialize.rs
index d5209e6c..9c9d2eb9 100644
--- a/src/dynamic/framework/adapters/php_unserialize.rs
+++ b/src/dynamic/framework/adapters/php_unserialize.rs
@@ -68,9 +68,11 @@ mod tests {
name: "run".into(),
..Default::default()
};
- assert!(PhpUnserializeAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ PhpUnserializeAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -81,8 +83,10 @@ mod tests {
name: "run".into(),
..Default::default()
};
- assert!(PhpUnserializeAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PhpUnserializeAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/pp_json_deep_assign.rs b/src/dynamic/framework/adapters/pp_json_deep_assign.rs
index 612f0a30..19e24856 100644
--- a/src/dynamic/framework/adapters/pp_json_deep_assign.rs
+++ b/src/dynamic/framework/adapters/pp_json_deep_assign.rs
@@ -141,9 +141,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("JSON.parse")],
..Default::default()
};
- assert!(PpJsonDeepAssignJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ PpJsonDeepAssignJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -155,9 +157,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("JSON.parse")],
..Default::default()
};
- assert!(PpJsonDeepAssignJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PpJsonDeepAssignJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -176,8 +180,10 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("JSON.parse")],
..Default::default()
};
- assert!(PpJsonDeepAssignJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PpJsonDeepAssignJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/pp_lodash_merge.rs b/src/dynamic/framework/adapters/pp_lodash_merge.rs
index 095f4c4e..510f29c4 100644
--- a/src/dynamic/framework/adapters/pp_lodash_merge.rs
+++ b/src/dynamic/framework/adapters/pp_lodash_merge.rs
@@ -13,7 +13,10 @@ use crate::symbol::Lang;
fn callee_is_lodash_merge(name: &str) -> bool {
let last = name.rsplit_once('.').map(|(_, s)| s).unwrap_or(name);
- matches!(last, "merge" | "mergeWith" | "defaultsDeep" | "set" | "setWith")
+ matches!(
+ last,
+ "merge" | "mergeWith" | "defaultsDeep" | "set" | "setWith"
+ )
}
/// True when `receiver` looks like a lodash module handle (`_`, `lodash`,
@@ -152,9 +155,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("merge")],
..Default::default()
};
- assert!(PpLodashMergeJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ PpLodashMergeJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -165,9 +170,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(PpLodashMergeJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PpLodashMergeJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -193,9 +200,11 @@ mod tests {
}],
..Default::default()
};
- assert!(PpLodashMergeJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PpLodashMergeJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -213,9 +222,11 @@ mod tests {
}],
..Default::default()
};
- assert!(PpLodashMergeJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ PpLodashMergeJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -233,9 +244,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("merge")],
..Default::default()
};
- assert!(PpLodashMergeJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PpLodashMergeJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -249,8 +262,10 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("merge")],
..Default::default()
};
- assert!(PpLodashMergeJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PpLodashMergeJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/pp_object_assign.rs b/src/dynamic/framework/adapters/pp_object_assign.rs
index d2dc7398..fd37d5c8 100644
--- a/src/dynamic/framework/adapters/pp_object_assign.rs
+++ b/src/dynamic/framework/adapters/pp_object_assign.rs
@@ -117,9 +117,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("Object.assign")],
..Default::default()
};
- assert!(PpObjectAssignJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ PpObjectAssignJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -130,24 +132,27 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(PpObjectAssignJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PpObjectAssignJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
fn skips_object_create_null_mitigation() {
- let src: &[u8] =
- b"function run(payload) { return Object.create(null); }\n";
+ let src: &[u8] = b"function run(payload) { return Object.create(null); }\n";
let tree = parse_js(src);
let summary = FuncSummary {
name: "run".into(),
callees: vec![crate::summary::CalleeSite::bare("Object.create")],
..Default::default()
};
- assert!(PpObjectAssignJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PpObjectAssignJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -164,8 +169,10 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("Object.assign")],
..Default::default()
};
- assert!(PpObjectAssignJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PpObjectAssignJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/pubsub_python.rs b/src/dynamic/framework/adapters/pubsub_python.rs
index 5456f5c2..d113f96a 100644
--- a/src/dynamic/framework/adapters/pubsub_python.rs
+++ b/src/dynamic/framework/adapters/pubsub_python.rs
@@ -11,10 +11,7 @@ const ADAPTER_NAME: &str = "pubsub-python";
fn callee_is_pubsub(name: &str) -> bool {
let last = name.rsplit_once('.').map(|(_, s)| s).unwrap_or(name);
- matches!(
- last,
- "subscribe" | "pull" | "callback" | "process_message"
- )
+ matches!(last, "subscribe" | "pull" | "callback" | "process_message")
}
fn source_imports_pubsub(file_bytes: &[u8]) -> bool {
diff --git a/src/dynamic/framework/adapters/python_django.rs b/src/dynamic/framework/adapters/python_django.rs
index 7334be3a..07f2d321 100644
--- a/src/dynamic/framework/adapters/python_django.rs
+++ b/src/dynamic/framework/adapters/python_django.rs
@@ -91,17 +91,19 @@ fn walk_url_registrations(
{
let last = callee.rsplit_once('.').map(|(_, s)| s).unwrap_or(callee);
if matches!(last, "path" | "re_path" | "url")
- && let Some(args) = node.child_by_field_name("arguments") {
- let positional = positional_args(args);
- if positional.len() >= 2 {
- let view_arg = positional[1];
- if view_arg_references(view_arg, bytes, target, class_target)
- && let Some(template) = first_string_arg(args, bytes) {
- *out = Some(template);
- return;
- }
+ && let Some(args) = node.child_by_field_name("arguments")
+ {
+ let positional = positional_args(args);
+ if positional.len() >= 2 {
+ let view_arg = positional[1];
+ if view_arg_references(view_arg, bytes, target, class_target)
+ && let Some(template) = first_string_arg(args, bytes)
+ {
+ *out = Some(template);
+ return;
}
}
+ }
}
let mut cur = node.walk();
for child in node.children(&mut cur) {
@@ -137,12 +139,15 @@ fn view_arg_references(
.and_then(|s| s.rfind('(').map(|i| &s[..i]))
.and_then(|s| s.strip_suffix(".as_view"))
&& let Some(ct) = class_target
- && class.rsplit_once('.').map(|(_, s)| s).unwrap_or(class) == ct
- {
- return true;
- }
+ && class.rsplit_once('.').map(|(_, s)| s).unwrap_or(class) == ct
+ {
+ return true;
+ }
let stripped = trimmed.trim_end_matches("()");
- let last = stripped.rsplit_once('.').map(|(_, s)| s).unwrap_or(stripped);
+ let last = stripped
+ .rsplit_once('.')
+ .map(|(_, s)| s)
+ .unwrap_or(stripped);
last == target || stripped == target
}
@@ -191,12 +196,8 @@ impl FrameworkAdapter for PythonDjangoAdapter {
// - urls.py registration referencing the function
// - urls.py `ClassName.as_view()` registration referencing the enclosing class
// - class-based view method name (path falls back to `/`)
- let url_template = url_template_for(
- ast,
- file_bytes,
- &summary.name,
- cbv_class_name.as_deref(),
- );
+ let url_template =
+ url_template_for(ast, file_bytes, &summary.name, cbv_class_name.as_deref());
let (method, path) = if let Some(m) = cbv_method {
(m, url_template.unwrap_or_else(|| "/".to_owned()))
@@ -288,18 +289,23 @@ mod tests {
fn skips_when_django_not_imported() {
let src: &[u8] = b"def list_users(request):\n return None\n";
let tree = parse(src);
- assert!(PythonDjangoAdapter
- .detect(&summary("list_users"), tree.root_node(), src)
- .is_none());
+ assert!(
+ PythonDjangoAdapter
+ .detect(&summary("list_users"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
fn skips_plain_helper_function() {
- let src: &[u8] = b"from django.http import HttpResponse\ndef helper(x):\n return HttpResponse(x)\n";
+ let src: &[u8] =
+ b"from django.http import HttpResponse\ndef helper(x):\n return HttpResponse(x)\n";
let tree = parse(src);
- assert!(PythonDjangoAdapter
- .detect(&summary("helper"), tree.root_node(), src)
- .is_none());
+ assert!(
+ PythonDjangoAdapter
+ .detect(&summary("helper"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -314,8 +320,10 @@ mod tests {
// pipeline surfaces `SpecDerivationFailed`.
let src: &[u8] = b"from django.http import HttpResponse\ndef authenticated(request, perm):\n return HttpResponse(perm)\n";
let tree = parse(src);
- assert!(PythonDjangoAdapter
- .detect(&summary("authenticated"), tree.root_node(), src)
- .is_none());
+ assert!(
+ PythonDjangoAdapter
+ .detect(&summary("authenticated"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/python_fastapi.rs b/src/dynamic/framework/adapters/python_fastapi.rs
index ebcdf89d..d64bc50a 100644
--- a/src/dynamic/framework/adapters/python_fastapi.rs
+++ b/src/dynamic/framework/adapters/python_fastapi.rs
@@ -62,8 +62,14 @@ fn decorator_route_shape(decorator: Node<'_>, bytes: &[u8]) -> Option<(HttpMetho
if target.kind() != "attribute" {
return None;
}
- let object = target.child_by_field_name("object")?.utf8_text(bytes).ok()?;
- let attr = target.child_by_field_name("attribute")?.utf8_text(bytes).ok()?;
+ let object = target
+ .child_by_field_name("object")?
+ .utf8_text(bytes)
+ .ok()?;
+ let attr = target
+ .child_by_field_name("attribute")?
+ .utf8_text(bytes)
+ .ok()?;
if !receiver_looks_like_fastapi(object) {
return None;
}
@@ -389,8 +395,10 @@ mod tests {
fn skips_when_fastapi_not_imported() {
let src: &[u8] = b"from flask import Flask\napp = Flask(__name__)\n@app.get(\"/x\")\ndef x():\n return 1\n";
let tree = parse(src);
- assert!(PythonFastApiAdapter
- .detect(&summary("x"), tree.root_node(), src)
- .is_none());
+ assert!(
+ PythonFastApiAdapter
+ .detect(&summary("x"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/python_flask.rs b/src/dynamic/framework/adapters/python_flask.rs
index 1f12cb80..55dcdb3b 100644
--- a/src/dynamic/framework/adapters/python_flask.rs
+++ b/src/dynamic/framework/adapters/python_flask.rs
@@ -202,10 +202,12 @@ mod tests {
.expect("binding");
let route = binding.route.unwrap();
assert_eq!(route.path, "/users/");
- assert!(binding
- .request_params
- .iter()
- .any(|p| p.name == "id" && matches!(p.source, ParamSource::PathSegment(_))));
+ assert!(
+ binding
+ .request_params
+ .iter()
+ .any(|p| p.name == "id" && matches!(p.source, ParamSource::PathSegment(_)))
+ );
}
#[test]
@@ -234,17 +236,22 @@ mod tests {
fn skips_when_flask_not_imported() {
let src: &[u8] = b"def add(a, b):\n return a + b\n";
let tree = parse(src);
- assert!(PythonFlaskAdapter
- .detect(&summary("add"), tree.root_node(), src)
- .is_none());
+ assert!(
+ PythonFlaskAdapter
+ .detect(&summary("add"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
fn skips_when_function_has_no_decorator() {
- let src: &[u8] = b"from flask import Flask\napp = Flask(__name__)\ndef helper(x):\n return x\n";
+ let src: &[u8] =
+ b"from flask import Flask\napp = Flask(__name__)\ndef helper(x):\n return x\n";
let tree = parse(src);
- assert!(PythonFlaskAdapter
- .detect(&summary("helper"), tree.root_node(), src)
- .is_none());
+ assert!(
+ PythonFlaskAdapter
+ .detect(&summary("helper"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/python_jinja2.rs b/src/dynamic/framework/adapters/python_jinja2.rs
index 895bdd4a..a6ab77fe 100644
--- a/src/dynamic/framework/adapters/python_jinja2.rs
+++ b/src/dynamic/framework/adapters/python_jinja2.rs
@@ -92,9 +92,7 @@ impl FrameworkAdapter for PythonJinja2Adapter {
ast: tree_sitter::Node<'_>,
file_bytes: &[u8],
) -> Option {
- let cheap_filter = file_bytes
- .windows(b"jinja2".len())
- .any(|w| w == b"jinja2")
+ let cheap_filter = file_bytes.windows(b"jinja2".len()).any(|w| w == b"jinja2")
|| file_bytes
.windows(b"from_string".len())
.any(|w| w == b"from_string")
@@ -149,9 +147,11 @@ mod tests {
b"from jinja2 import Template\ndef render(body):\n return Template(body).render()\n";
let tree = parse_python(src);
let summary = summary_for("render", &["body"], &[0]);
- assert!(PythonJinja2Adapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ PythonJinja2Adapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -161,9 +161,11 @@ mod tests {
let tree = parse_python(src);
let mut summary = summary_for("view", &["body"], &[0]);
summary.callees = vec![crate::summary::CalleeSite::bare("render_template_string")];
- assert!(PythonJinja2Adapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ PythonJinja2Adapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -174,9 +176,11 @@ mod tests {
name: "run".into(),
..Default::default()
};
- assert!(PythonJinja2Adapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PythonJinja2Adapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -186,9 +190,11 @@ mod tests {
let src: &[u8] = b"\"\"\"renders via jinja2.Template\"\"\"\ndef render(body):\n return Template(\"hello\").render()\n";
let tree = parse_python(src);
let summary = summary_for("render", &["body"], &[0]);
- assert!(PythonJinja2Adapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PythonJinja2Adapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -199,8 +205,10 @@ mod tests {
b"from jinja2 import Template\ndef render(body):\n return Template(body).render()\n";
let tree = parse_python(src);
let summary = summary_for("render", &["body"], &[]);
- assert!(PythonJinja2Adapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PythonJinja2Adapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/python_pickle.rs b/src/dynamic/framework/adapters/python_pickle.rs
index 9520aeed..36f4e5f5 100644
--- a/src/dynamic/framework/adapters/python_pickle.rs
+++ b/src/dynamic/framework/adapters/python_pickle.rs
@@ -34,9 +34,7 @@ impl FrameworkAdapter for PythonPickleAdapter {
file_bytes: &[u8],
) -> Option {
let matches_call = super::any_callee_matches(summary, callee_is_python_deserialize);
- let matches_source = file_bytes
- .windows(b"pickle".len())
- .any(|w| w == b"pickle")
+ let matches_source = file_bytes.windows(b"pickle".len()).any(|w| w == b"pickle")
|| file_bytes
.windows(b"yaml.unsafe_load".len())
.any(|w| w == b"yaml.unsafe_load")
@@ -77,9 +75,11 @@ mod tests {
name: "run".into(),
..Default::default()
};
- assert!(PythonPickleAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ PythonPickleAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -90,8 +90,10 @@ mod tests {
name: "run".into(),
..Default::default()
};
- assert!(PythonPickleAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ PythonPickleAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/python_routes.rs b/src/dynamic/framework/adapters/python_routes.rs
index c0b77325..847e9f73 100644
--- a/src/dynamic/framework/adapters/python_routes.rs
+++ b/src/dynamic/framework/adapters/python_routes.rs
@@ -33,7 +33,12 @@ pub fn source_imports_flask(bytes: &[u8]) -> bool {
pub fn source_imports_fastapi(bytes: &[u8]) -> bool {
contains_any(
bytes,
- &[b"from fastapi", b"import fastapi", b"FastAPI(", b"APIRouter("],
+ &[
+ b"from fastapi",
+ b"import fastapi",
+ b"FastAPI(",
+ b"APIRouter(",
+ ],
)
}
@@ -95,10 +100,11 @@ fn walk<'a>(node: Node<'a>, bytes: &[u8], target: &str) -> Option<(Node<'a>, Opt
&& let Some(name) = node
.child_by_field_name("name")
.and_then(|n| n.utf8_text(bytes).ok())
- && name == target {
- let decorated = node.parent().filter(|p| p.kind() == "decorated_definition");
- return Some((node, decorated));
- }
+ && name == target
+ {
+ let decorated = node.parent().filter(|p| p.kind() == "decorated_definition");
+ return Some((node, decorated));
+ }
let mut cur = node.walk();
for child in node.children(&mut cur) {
if let Some(found) = walk(child, bytes, target) {
diff --git a/src/dynamic/framework/adapters/python_starlette.rs b/src/dynamic/framework/adapters/python_starlette.rs
index 8737e396..4542b7fa 100644
--- a/src/dynamic/framework/adapters/python_starlette.rs
+++ b/src/dynamic/framework/adapters/python_starlette.rs
@@ -8,9 +8,7 @@
//! to the handler does not matter. Methods are picked up from the
//! `methods=[...]` kwarg when present and default to `GET`.
-use crate::dynamic::framework::{
- FrameworkAdapter, FrameworkBinding, HttpMethod, RouteShape,
-};
+use crate::dynamic::framework::{FrameworkAdapter, FrameworkBinding, HttpMethod, RouteShape};
use crate::evidence::EntryKind;
use crate::summary::FuncSummary;
use crate::symbol::Lang;
@@ -50,12 +48,13 @@ fn walk_routes(node: Node<'_>, bytes: &[u8], target: &str, out: &mut Option<(Htt
let last = callee.rsplit_once('.').map(|(_, s)| s).unwrap_or(callee);
if matches!(last, "Route" | "WebSocketRoute")
&& let Some(args) = node.child_by_field_name("arguments")
- && let Some(path) = first_string_arg(args, bytes)
- && endpoint_references(args, bytes, target) {
- let method = methods_kwarg(args, bytes).unwrap_or(HttpMethod::GET);
- *out = Some((method, path));
- return;
- }
+ && let Some(path) = first_string_arg(args, bytes)
+ && endpoint_references(args, bytes, target)
+ {
+ let method = methods_kwarg(args, bytes).unwrap_or(HttpMethod::GET);
+ *out = Some((method, path));
+ return;
+ }
}
let mut cur = node.walk();
for child in node.children(&mut cur) {
@@ -76,9 +75,10 @@ fn endpoint_references(args: Node<'_>, bytes: &[u8], target: &str) -> bool {
};
if name_text == "endpoint"
&& let Some(value) = arg.child_by_field_name("value")
- && identifier_matches(value, bytes, target) {
- return true;
- }
+ && identifier_matches(value, bytes, target)
+ {
+ return true;
+ }
} else {
seen_positional += 1;
// Second positional argument is the endpoint when no
@@ -204,8 +204,10 @@ mod tests {
fn skips_when_starlette_not_imported() {
let src: &[u8] = b"def homepage(request):\n return None\n";
let tree = parse(src);
- assert!(PythonStarletteAdapter
- .detect(&summary("homepage"), tree.root_node(), src)
- .is_none());
+ assert!(
+ PythonStarletteAdapter
+ .detect(&summary("homepage"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/redirect_go.rs b/src/dynamic/framework/adapters/redirect_go.rs
index ff92e5be..267a29c2 100644
--- a/src/dynamic/framework/adapters/redirect_go.rs
+++ b/src/dynamic/framework/adapters/redirect_go.rs
@@ -111,7 +111,8 @@ mod tests {
#[test]
fn fires_on_gin_redirect() {
- let src: &[u8] = b"package vuln\n\nimport (\n\t\"net/http\"\n\t\"github.com/gin-gonic/gin\"\n)\n\
+ let src: &[u8] =
+ b"package vuln\n\nimport (\n\t\"net/http\"\n\t\"github.com/gin-gonic/gin\"\n)\n\
func Run(c *gin.Context, v string) {\n\tc.Redirect(http.StatusFound, v)\n}\n";
let tree = parse_go(src);
let summary = FuncSummary {
@@ -119,9 +120,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("Redirect")],
..Default::default()
};
- assert!(RedirectGoAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ RedirectGoAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -132,9 +135,11 @@ mod tests {
name: "Add".into(),
..Default::default()
};
- assert!(RedirectGoAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ RedirectGoAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -153,9 +158,11 @@ mod tests {
],
..Default::default()
};
- assert!(RedirectGoAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ RedirectGoAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -168,8 +175,10 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("Redirect")],
..Default::default()
};
- assert!(RedirectGoAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ RedirectGoAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/redirect_java.rs b/src/dynamic/framework/adapters/redirect_java.rs
index 83cd704f..3b714889 100644
--- a/src/dynamic/framework/adapters/redirect_java.rs
+++ b/src/dynamic/framework/adapters/redirect_java.rs
@@ -108,9 +108,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("sendRedirect")],
..Default::default()
};
- assert!(RedirectJavaAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ RedirectJavaAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -121,9 +123,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(RedirectJavaAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ RedirectJavaAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -144,8 +148,10 @@ mod tests {
],
..Default::default()
};
- assert!(RedirectJavaAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ RedirectJavaAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/redirect_js.rs b/src/dynamic/framework/adapters/redirect_js.rs
index df462828..16c154fb 100644
--- a/src/dynamic/framework/adapters/redirect_js.rs
+++ b/src/dynamic/framework/adapters/redirect_js.rs
@@ -112,9 +112,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("redirect")],
..Default::default()
};
- assert!(RedirectJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ RedirectJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -125,9 +127,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(RedirectJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ RedirectJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -143,8 +147,10 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("redirect")],
..Default::default()
};
- assert!(RedirectJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ RedirectJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/redirect_php.rs b/src/dynamic/framework/adapters/redirect_php.rs
index ffb88aa8..af643ce6 100644
--- a/src/dynamic/framework/adapters/redirect_php.rs
+++ b/src/dynamic/framework/adapters/redirect_php.rs
@@ -73,13 +73,12 @@ impl FrameworkAdapter for RedirectPhpAdapter {
return None;
}
let has_location_token = file_contains_location_header_token(file_bytes);
- let matches_call = super::any_callee_matches(summary, |name| {
- match callee_last_segment(name) {
+ let matches_call =
+ super::any_callee_matches(summary, |name| match callee_last_segment(name) {
"redirect" | "withRedirect" | "RedirectResponse" => true,
"header" => has_location_token,
_ => false,
- }
- });
+ });
let matches_source = source_imports_php_web(file_bytes);
if matches_call && matches_source {
Some(FrameworkBinding {
@@ -109,17 +108,18 @@ mod tests {
#[test]
fn fires_on_header_location() {
- let src: &[u8] =
- b" bool {
let last = name.rsplit_once('.').map(|(_, s)| s).unwrap_or(name);
- matches!(last, "redirect" | "redirect_to" | "redirect!" )
+ matches!(last, "redirect" | "redirect_to" | "redirect!")
}
fn source_imports_ruby_web(file_bytes: &[u8]) -> bool {
@@ -110,9 +110,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("redirect")],
..Default::default()
};
- assert!(RedirectRubyAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ RedirectRubyAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -123,9 +125,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(RedirectRubyAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ RedirectRubyAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -144,8 +148,10 @@ mod tests {
],
..Default::default()
};
- assert!(RedirectRubyAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ RedirectRubyAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/redirect_rust.rs b/src/dynamic/framework/adapters/redirect_rust.rs
index 97e14f9e..e790ef24 100644
--- a/src/dynamic/framework/adapters/redirect_rust.rs
+++ b/src/dynamic/framework/adapters/redirect_rust.rs
@@ -128,9 +128,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("to")],
..Default::default()
};
- assert!(RedirectRustAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ RedirectRustAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -141,9 +143,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(RedirectRustAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ RedirectRustAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -166,9 +170,11 @@ mod tests {
}],
..Default::default()
};
- assert!(RedirectRustAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ RedirectRustAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -189,9 +195,11 @@ mod tests {
}],
..Default::default()
};
- assert!(RedirectRustAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ RedirectRustAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -211,8 +219,10 @@ mod tests {
],
..Default::default()
};
- assert!(RedirectRustAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ RedirectRustAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/ruby_erb.rs b/src/dynamic/framework/adapters/ruby_erb.rs
index 95ad27c1..6d7c43a6 100644
--- a/src/dynamic/framework/adapters/ruby_erb.rs
+++ b/src/dynamic/framework/adapters/ruby_erb.rs
@@ -26,7 +26,10 @@ fn callee_last_segment(name: &str) -> &str {
}
fn is_erb_entry(name: &str) -> bool {
- matches!(callee_last_segment(name), "result" | "result_with_hash" | "new")
+ matches!(
+ callee_last_segment(name),
+ "result" | "result_with_hash" | "new"
+ )
}
fn ast_confirms_tainted_call(root: Node<'_>, bytes: &[u8], summary: &FuncSummary) -> bool {
@@ -61,7 +64,10 @@ fn walk(node: Node<'_>, bytes: &[u8], summary: &FuncSummary, found: &mut bool) {
fn first_positional_arg<'a>(args: Node<'a>) -> Option> {
let mut cur = args.walk();
for arg in args.named_children(&mut cur) {
- if matches!(arg.kind(), "pair" | "hash_splat_argument" | "block_argument") {
+ if matches!(
+ arg.kind(),
+ "pair" | "hash_splat_argument" | "block_argument"
+ ) {
continue;
}
return Some(arg);
@@ -93,9 +99,7 @@ impl FrameworkAdapter for RubyErbAdapter {
|| file_bytes
.windows(b"require \"erb\"".len())
.any(|w| w == b"require \"erb\"")
- || file_bytes
- .windows(b"Erubi".len())
- .any(|w| w == b"Erubi");
+ || file_bytes.windows(b"Erubi".len()).any(|w| w == b"Erubi");
if !cheap_filter {
return None;
}
@@ -139,9 +143,11 @@ mod tests {
let src: &[u8] = b"require 'erb'\ndef render(body)\n ERB.new(body).result\nend\n";
let tree = parse_ruby(src);
let summary = summary_for("render", &["body"], &[0]);
- assert!(RubyErbAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ RubyErbAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -152,9 +158,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(RubyErbAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ RubyErbAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -163,9 +171,11 @@ mod tests {
b"# require 'erb' is mentioned\ndef render(body)\n ERB.new(\"static\").result\nend\n";
let tree = parse_ruby(src);
let summary = summary_for("render", &["body"], &[0]);
- assert!(RubyErbAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ RubyErbAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -173,8 +183,10 @@ mod tests {
let src: &[u8] = b"require 'erb'\ndef render(body)\n ERB.new(body).result\nend\n";
let tree = parse_ruby(src);
let summary = summary_for("render", &["body"], &[]);
- assert!(RubyErbAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ RubyErbAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/ruby_hanami.rs b/src/dynamic/framework/adapters/ruby_hanami.rs
index 3e1de949..d65b3ff2 100644
--- a/src/dynamic/framework/adapters/ruby_hanami.rs
+++ b/src/dynamic/framework/adapters/ruby_hanami.rs
@@ -172,7 +172,11 @@ mod tests {
let binding = RubyHanamiAdapter
.detect(&summary("call"), tree.root_node(), src)
.expect("binding");
- let id = binding.request_params.iter().find(|p| p.name == "id").unwrap();
+ let id = binding
+ .request_params
+ .iter()
+ .find(|p| p.name == "id")
+ .unwrap();
assert!(matches!(id.source, ParamSource::PathSegment(_)));
}
@@ -184,7 +188,11 @@ mod tests {
let binding = RubyHanamiAdapter
.detect(&summary("call"), tree.root_node(), src)
.expect("binding");
- let req = binding.request_params.iter().find(|p| p.name == "req").unwrap();
+ let req = binding
+ .request_params
+ .iter()
+ .find(|p| p.name == "req")
+ .unwrap();
assert!(matches!(req.source, ParamSource::Implicit));
}
@@ -194,9 +202,11 @@ mod tests {
b"require 'hanami/action'\nclass Plain\n def call(req)\n 'ok'\n end\nend\n";
let tree = parse(src);
// No `Hanami::Action` superclass / include — must skip.
- assert!(RubyHanamiAdapter
- .detect(&summary("call"), tree.root_node(), src)
- .is_none());
+ assert!(
+ RubyHanamiAdapter
+ .detect(&summary("call"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -207,8 +217,10 @@ mod tests {
// `Hanami::Action` substring, so this fixture in fact does
// trip the marker — the test exists to document that bare
// `Hanami::Action` superclass alone is sufficient.
- assert!(RubyHanamiAdapter
- .detect(&summary("call"), tree.root_node(), src)
- .is_some());
+ assert!(
+ RubyHanamiAdapter
+ .detect(&summary("call"), tree.root_node(), src)
+ .is_some()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/ruby_marshal.rs b/src/dynamic/framework/adapters/ruby_marshal.rs
index 466e223a..91eadb23 100644
--- a/src/dynamic/framework/adapters/ruby_marshal.rs
+++ b/src/dynamic/framework/adapters/ruby_marshal.rs
@@ -79,9 +79,11 @@ mod tests {
name: "run".into(),
..Default::default()
};
- assert!(RubyMarshalAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ RubyMarshalAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -92,8 +94,10 @@ mod tests {
name: "run".into(),
..Default::default()
};
- assert!(RubyMarshalAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ RubyMarshalAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/ruby_rails.rs b/src/dynamic/framework/adapters/ruby_rails.rs
index 5d7fa484..df38a694 100644
--- a/src/dynamic/framework/adapters/ruby_rails.rs
+++ b/src/dynamic/framework/adapters/ruby_rails.rs
@@ -81,19 +81,31 @@ fn visit_routes<'a>(
format!("{path_prefix}/{ident}"),
format!("{ctrl_prefix}{ident}/"),
),
- NestingKind::ScopePath => (format!("{path_prefix}/{ident}"), ctrl_prefix.to_owned()),
+ NestingKind::ScopePath => {
+ (format!("{path_prefix}/{ident}"), ctrl_prefix.to_owned())
+ }
};
recurse_into_block(node, bytes, controller, action, &path_pfx, &ctrl_pfx, out);
return;
}
- if let Some(found) = try_route_mapping(node, bytes, controller, action, path_prefix, ctrl_prefix) {
+ if let Some(found) =
+ try_route_mapping(node, bytes, controller, action, path_prefix, ctrl_prefix)
+ {
*out = Some(found);
return;
}
}
let mut cur = node.walk();
for child in node.children(&mut cur) {
- visit_routes(child, bytes, controller, action, path_prefix, ctrl_prefix, out);
+ visit_routes(
+ child,
+ bytes,
+ controller,
+ action,
+ path_prefix,
+ ctrl_prefix,
+ out,
+ );
}
}
@@ -153,7 +165,15 @@ fn recurse_into_block<'a>(
let mut cur = call.walk();
for child in call.named_children(&mut cur) {
if child.kind() == "do_block" || child.kind() == "block" {
- visit_routes(child, bytes, controller, action, path_prefix, ctrl_prefix, out);
+ visit_routes(
+ child,
+ bytes,
+ controller,
+ action,
+ path_prefix,
+ ctrl_prefix,
+ out,
+ );
}
}
}
@@ -208,9 +228,7 @@ fn controller_matches(routes_ctrl: &str, controller_class: &str) -> bool {
}
fn rails_controller_path(class_name: &str) -> String {
- let stripped = class_name
- .strip_suffix("Controller")
- .unwrap_or(class_name);
+ let stripped = class_name.strip_suffix("Controller").unwrap_or(class_name);
// Rails routes use the singular-segment lower form joined by `/`
// for module-namespaced controllers (`Api::Users` → `api/users`).
let segments: Vec = stripped
@@ -356,8 +374,15 @@ mod tests {
.expect("binding");
let route = binding.route.unwrap();
assert_eq!(route.path, "/u/:id");
- let id = binding.request_params.iter().find(|p| p.name == "id").unwrap();
- assert!(matches!(id.source, crate::dynamic::framework::ParamSource::PathSegment(_)));
+ let id = binding
+ .request_params
+ .iter()
+ .find(|p| p.name == "id")
+ .unwrap();
+ assert!(matches!(
+ id.source,
+ crate::dynamic::framework::ParamSource::PathSegment(_)
+ ));
}
#[test]
@@ -409,9 +434,11 @@ mod tests {
fn skips_when_class_is_not_a_controller() {
let src: &[u8] = b"class Foo\n def bar\n 'ok'\n end\nend\n";
let tree = parse(src);
- assert!(RubyRailsAdapter
- .detect(&summary("bar"), tree.root_node(), src)
- .is_none());
+ assert!(
+ RubyRailsAdapter
+ .detect(&summary("bar"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -419,29 +446,29 @@ mod tests {
let src: &[u8] =
b"class UsersController < ApplicationController\n def index\n 'ok'\n end\nend\n";
let tree = parse(src);
- assert!(RubyRailsAdapter
- .detect(&summary("missing"), tree.root_node(), src)
- .is_none());
+ assert!(
+ RubyRailsAdapter
+ .detect(&summary("missing"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
fn skips_files_without_rails_marker() {
- let src: &[u8] =
- b"class UsersController < Object\n def index\n 'ok'\n end\nend\n";
+ let src: &[u8] = b"class UsersController < Object\n def index\n 'ok'\n end\nend\n";
let tree = parse(src);
- assert!(RubyRailsAdapter
- .detect(&summary("index"), tree.root_node(), src)
- .is_none());
+ assert!(
+ RubyRailsAdapter
+ .detect(&summary("index"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
fn rails_controller_path_drops_suffix_and_snake_cases() {
assert_eq!(rails_controller_path("UsersController"), "users");
assert_eq!(rails_controller_path("UserPostsController"), "user_posts");
- assert_eq!(
- rails_controller_path("Api::UsersController"),
- "api/users"
- );
+ assert_eq!(rails_controller_path("Api::UsersController"), "api/users");
assert_eq!(rails_controller_path("Foo"), "foo");
}
}
diff --git a/src/dynamic/framework/adapters/ruby_routes.rs b/src/dynamic/framework/adapters/ruby_routes.rs
index e3a3c8d6..bd4b4736 100644
--- a/src/dynamic/framework/adapters/ruby_routes.rs
+++ b/src/dynamic/framework/adapters/ruby_routes.rs
@@ -96,10 +96,11 @@ fn walk_class<'a>(
return;
}
if node.kind() == "class"
- && let Some(method) = find_method_in_class(node, bytes, target) {
- *out = Some((node, method));
- return;
- }
+ && let Some(method) = find_method_in_class(node, bytes, target)
+ {
+ *out = Some((node, method));
+ return;
+ }
let mut cur = node.walk();
for child in node.children(&mut cur) {
walk_class(child, bytes, target, out);
@@ -109,7 +110,11 @@ fn walk_class<'a>(
/// Find a `method` node named `target` directly inside a `class`
/// body. Returns `None` when the class has no body or no method of
/// that name.
-pub fn find_method_in_class<'a>(class: Node<'a>, bytes: &'a [u8], target: &str) -> Option> {
+pub fn find_method_in_class<'a>(
+ class: Node<'a>,
+ bytes: &'a [u8],
+ target: &str,
+) -> Option> {
let body = named_child_of_kind(class, "body_statement")?;
let mut cur = body.walk();
for member in body.named_children(&mut cur) {
@@ -117,9 +122,10 @@ pub fn find_method_in_class<'a>(class: Node<'a>, bytes: &'a [u8], target: &str)
continue;
}
if let Some(name) = method_identifier(member, bytes)
- && name == target {
- return Some(member);
- }
+ && name == target
+ {
+ return Some(member);
+ }
}
None
}
@@ -349,7 +355,10 @@ pub fn bind_path_params(formals: &[String], path: &str) -> Vec {
}
fn is_implicit_formal(name: &str) -> bool {
- matches!(name, "env" | "request" | "req" | "params" | "response" | "res")
+ matches!(
+ name,
+ "env" | "request" | "req" | "params" | "response" | "res"
+ )
}
/// Read the first positional symbol argument (`:foo`) from an
@@ -489,8 +498,7 @@ mod tests {
#[test]
fn class_includes_detects_hanami_v2() {
- let src: &[u8] =
- b"class A\n include Hanami::Action\n def call(req)\n end\nend\n";
+ let src: &[u8] = b"class A\n include Hanami::Action\n def call(req)\n end\nend\n";
let tree = parse(src);
let mut cur = tree.root_node().walk();
let class = tree
diff --git a/src/dynamic/framework/adapters/ruby_sinatra.rs b/src/dynamic/framework/adapters/ruby_sinatra.rs
index 54a7c0d2..a44f1172 100644
--- a/src/dynamic/framework/adapters/ruby_sinatra.rs
+++ b/src/dynamic/framework/adapters/ruby_sinatra.rs
@@ -41,10 +41,11 @@ fn collect_routes(root: Node<'_>, bytes: &[u8]) -> Vec {
fn visit(node: Node<'_>, bytes: &[u8], out: &mut Vec) {
if node.kind() == "call"
- && let Some(route) = try_route(node, bytes) {
- out.push(route);
- return;
- }
+ && let Some(route) = try_route(node, bytes)
+ {
+ out.push(route);
+ return;
+ }
// Sinatra routes live at top level or directly under a `class App <
// Sinatra::Base` body — never inside a helper method's body. Skip
// descent through `method` / `singleton_method` so a stray `get '/x'
@@ -101,9 +102,10 @@ fn block_parameter_names(block: Node<'_>, bytes: &[u8]) -> Vec {
let mut bc = child.walk();
for p in child.named_children(&mut bc) {
if p.kind() == "identifier"
- && let Ok(t) = p.utf8_text(bytes) {
- out.push(t.to_owned());
- }
+ && let Ok(t) = p.utf8_text(bytes)
+ {
+ out.push(t.to_owned());
+ }
}
}
out
@@ -196,8 +198,7 @@ mod tests {
#[test]
fn fires_on_marker_comment() {
- let src: &[u8] =
- b"# nyx-shape: sinatra\nget '/run' do |payload|\n payload\nend\n";
+ let src: &[u8] = b"# nyx-shape: sinatra\nget '/run' do |payload|\n payload\nend\n";
let tree = parse(src);
let binding = RubySinatraAdapter
.detect(&summary("run"), tree.root_node(), src)
@@ -207,13 +208,16 @@ mod tests {
#[test]
fn binds_path_placeholder() {
- let src: &[u8] =
- b"require 'sinatra'\nget '/u/:id' do |id|\n id\nend\n";
+ let src: &[u8] = b"require 'sinatra'\nget '/u/:id' do |id|\n id\nend\n";
let tree = parse(src);
let binding = RubySinatraAdapter
.detect(&summary("id"), tree.root_node(), src)
.expect("binding");
- let id = binding.request_params.iter().find(|p| p.name == "id").unwrap();
+ let id = binding
+ .request_params
+ .iter()
+ .find(|p| p.name == "id")
+ .unwrap();
assert!(matches!(id.source, ParamSource::PathSegment(_)));
}
@@ -223,9 +227,11 @@ mod tests {
let tree = parse(src);
// No do/end block — the Sinatra adapter must not claim a
// Rails-style `routes.draw` mapping.
- assert!(RubySinatraAdapter
- .detect(&summary("run"), tree.root_node(), src)
- .is_none());
+ assert!(
+ RubySinatraAdapter
+ .detect(&summary("run"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -243,9 +249,11 @@ mod tests {
fn skips_when_sinatra_not_imported() {
let src: &[u8] = b"get '/run' do |p|\n p\nend\n";
let tree = parse(src);
- assert!(RubySinatraAdapter
- .detect(&summary("run"), tree.root_node(), src)
- .is_none());
+ assert!(
+ RubySinatraAdapter
+ .detect(&summary("run"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -279,9 +287,11 @@ mod tests {
let src: &[u8] =
b"require 'sinatra'\ndef helper\n get '/run' do |payload|\n payload\n end\nend\n";
let tree = parse(src);
- assert!(RubySinatraAdapter
- .detect(&summary("run"), tree.root_node(), src)
- .is_none());
+ assert!(
+ RubySinatraAdapter
+ .detect(&summary("run"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
diff --git a/src/dynamic/framework/adapters/rust_actix.rs b/src/dynamic/framework/adapters/rust_actix.rs
index e2b47442..f7cf5e0e 100644
--- a/src/dynamic/framework/adapters/rust_actix.rs
+++ b/src/dynamic/framework/adapters/rust_actix.rs
@@ -114,18 +114,22 @@ mod tests {
fn skips_when_actix_not_imported() {
let src: &[u8] = b"#[get(\"/u\")]\nfn show() {}\n";
let tree = parse(src);
- assert!(RustActixAdapter
- .detect(&summary("show"), tree.root_node(), src)
- .is_none());
+ assert!(
+ RustActixAdapter
+ .detect(&summary("show"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
fn skips_when_attribute_missing() {
let src: &[u8] = b"use actix_web::App;\nfn helper(x: String) {}\n";
let tree = parse(src);
- assert!(RustActixAdapter
- .detect(&summary("helper"), tree.root_node(), src)
- .is_none());
+ assert!(
+ RustActixAdapter
+ .detect(&summary("helper"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -170,8 +174,10 @@ mod tests {
async fn show() -> String { String::new() }\n\
async fn other() -> String { String::new() }\n";
let tree = parse(src);
- assert!(RustActixAdapter
- .detect(&summary("show"), tree.root_node(), src)
- .is_none());
+ assert!(
+ RustActixAdapter
+ .detect(&summary("show"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/rust_axum.rs b/src/dynamic/framework/adapters/rust_axum.rs
index 23f95a02..a09efc48 100644
--- a/src/dynamic/framework/adapters/rust_axum.rs
+++ b/src/dynamic/framework/adapters/rust_axum.rs
@@ -116,17 +116,21 @@ mod tests {
fn skips_when_axum_not_imported() {
let src: &[u8] = b"fn show() {}\n";
let tree = parse(src);
- assert!(RustAxumAdapter
- .detect(&summary("show"), tree.root_node(), src)
- .is_none());
+ assert!(
+ RustAxumAdapter
+ .detect(&summary("show"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
fn skips_when_route_does_not_reference_function() {
let src: &[u8] = b"use axum::Router;\nfn build() -> Router { Router::new().route(\"/u\", get(show)) }\nfn helper() {}\n";
let tree = parse(src);
- assert!(RustAxumAdapter
- .detect(&summary("helper"), tree.root_node(), src)
- .is_none());
+ assert!(
+ RustAxumAdapter
+ .detect(&summary("helper"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/rust_rocket.rs b/src/dynamic/framework/adapters/rust_rocket.rs
index b33be781..a2ecd43d 100644
--- a/src/dynamic/framework/adapters/rust_rocket.rs
+++ b/src/dynamic/framework/adapters/rust_rocket.rs
@@ -86,7 +86,8 @@ mod tests {
#[test]
fn fires_on_get_with_angle_placeholder() {
- let src: &[u8] = b"use rocket::get;\n#[get(\"/u/\")]\nfn show(id: String) -> String { id }\n";
+ let src: &[u8] =
+ b"use rocket::get;\n#[get(\"/u/\")]\nfn show(id: String) -> String { id }\n";
let tree = parse(src);
let binding = RustRocketAdapter
.detect(&summary("show"), tree.root_node(), src)
@@ -118,8 +119,10 @@ mod tests {
fn skips_when_rocket_not_imported() {
let src: &[u8] = b"#[get(\"/u\")]\nfn show() {}\n";
let tree = parse(src);
- assert!(RustRocketAdapter
- .detect(&summary("show"), tree.root_node(), src)
- .is_none());
+ assert!(
+ RustRocketAdapter
+ .detect(&summary("show"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/rust_routes.rs b/src/dynamic/framework/adapters/rust_routes.rs
index dcd1c26f..d53a933c 100644
--- a/src/dynamic/framework/adapters/rust_routes.rs
+++ b/src/dynamic/framework/adapters/rust_routes.rs
@@ -83,22 +83,13 @@ fn contains_any(haystack: &[u8], needles: &[&[u8]]) -> bool {
/// Find a top-level `function_item` whose `name` field equals
/// `target`. Walks the AST recursively so functions nested inside
/// `impl` blocks are also matched.
-pub fn find_rust_function<'a>(
- root: Node<'a>,
- bytes: &'a [u8],
- target: &str,
-) -> Option> {
+pub fn find_rust_function<'a>(root: Node<'a>, bytes: &'a [u8], target: &str) -> Option> {
let mut hit: Option> = None;
walk_rs(root, bytes, target, &mut hit);
hit
}
-fn walk_rs<'a>(
- node: Node<'a>,
- bytes: &'a [u8],
- target: &str,
- out: &mut Option>,
-) {
+fn walk_rs<'a>(node: Node<'a>, bytes: &'a [u8], target: &str, out: &mut Option>) {
if out.is_some() {
return;
}
@@ -143,9 +134,10 @@ fn push_pattern_name(pat: Node<'_>, bytes: &[u8], out: &mut Vec) {
match pat.kind() {
"identifier" => {
if let Ok(text) = pat.utf8_text(bytes)
- && text != "_" {
- out.push(text.to_owned());
- }
+ && text != "_"
+ {
+ out.push(text.to_owned());
+ }
}
"mut_pattern" | "ref_pattern" => {
let mut cur = pat.walk();
@@ -310,10 +302,7 @@ pub fn rust_string_literal(node: Node<'_>, bytes: &[u8]) -> Option {
}
let raw = node.utf8_text(bytes).ok()?;
let trimmed = raw.trim();
- if trimmed.len() >= 2
- && trimmed.starts_with('"')
- && trimmed.ends_with('"')
- {
+ if trimmed.len() >= 2 && trimmed.starts_with('"') && trimmed.ends_with('"') {
Some(trimmed[1..trimmed.len() - 1].to_owned())
} else {
None
@@ -324,10 +313,7 @@ pub fn rust_string_literal(node: Node<'_>, bytes: &[u8]) -> Option {
/// for a `#[get("/path")]` / `#[post(...)]` / `#[route(...)]` macro.
/// Returns `(method, path)` on first match. Used by both actix-web
/// (`#[get("/path")]`) and rocket (same syntax).
-pub fn find_method_attribute<'a>(
- func: Node<'a>,
- bytes: &'a [u8],
-) -> Option<(HttpMethod, String)> {
+pub fn find_method_attribute<'a>(func: Node<'a>, bytes: &'a [u8]) -> Option<(HttpMethod, String)> {
let parent = func.parent()?;
let mut cur = parent.walk();
let children: Vec> = parent.children(&mut cur).collect();
@@ -359,9 +345,10 @@ pub fn find_method_attribute<'a>(
let mut cur = func.walk();
for c in func.children(&mut cur) {
if c.kind() == "attribute_item"
- && let Some(hit) = read_route_attribute(c, bytes) {
- return Some(hit);
- }
+ && let Some(hit) = read_route_attribute(c, bytes)
+ {
+ return Some(hit);
+ }
}
None
}
@@ -494,20 +481,14 @@ fn try_axum_route_call<'a>(
/// Parse the `get(handler)` / `axum::routing::get(handler)` wrapper
/// emitted by axum. Returns `(method, handler_node)` on success.
-fn parse_axum_verb_wrapper<'a>(
- node: Node<'a>,
- bytes: &'a [u8],
-) -> Option<(HttpMethod, Node<'a>)> {
+fn parse_axum_verb_wrapper<'a>(node: Node<'a>, bytes: &'a [u8]) -> Option<(HttpMethod, Node<'a>)> {
if node.kind() != "call_expression" {
return None;
}
let func = node.child_by_field_name("function")?;
let leaf = match func.kind() {
"identifier" => func.utf8_text(bytes).ok()?,
- "scoped_identifier" => func
- .child_by_field_name("name")?
- .utf8_text(bytes)
- .ok()?,
+ "scoped_identifier" => func.child_by_field_name("name")?.utf8_text(bytes).ok()?,
_ => return None,
};
let method = verb_from_ident(leaf)?;
@@ -613,10 +594,7 @@ fn try_actix_route_call<'a>(
/// Parse `web::get().to(handler)` / `web::post().to(handler)` /
/// `web::method(Method::PATCH).to(handler)` shapes. Returns
/// `(method, handler_node)` on the first matching `.to(...)` call.
-fn parse_actix_web_verb_to<'a>(
- node: Node<'a>,
- bytes: &'a [u8],
-) -> Option<(HttpMethod, Node<'a>)> {
+fn parse_actix_web_verb_to<'a>(node: Node<'a>, bytes: &'a [u8]) -> Option<(HttpMethod, Node<'a>)> {
if node.kind() != "call_expression" {
return None;
}
@@ -721,21 +699,21 @@ fn walk_warp<'a>(
while let Some(p) = parent {
if p.kind() == "call_expression"
&& let Some(func) = p.child_by_field_name("function")
- && func.kind() == "field_expression"
- && let Some(field) = func.child_by_field_name("field")
- && let Ok(field_text) = field.utf8_text(bytes)
- && matches!(field_text, "map" | "and_then" | "untuple_one")
- {
- let args = p.child_by_field_name("arguments");
- if let Some(args) = args {
- let mut cur = args.walk();
- for c in args.named_children(&mut cur) {
- if axum_callable_matches(c, bytes, target) {
- hit_target = true;
- }
+ && func.kind() == "field_expression"
+ && let Some(field) = func.child_by_field_name("field")
+ && let Ok(field_text) = field.utf8_text(bytes)
+ && matches!(field_text, "map" | "and_then" | "untuple_one")
+ {
+ let args = p.child_by_field_name("arguments");
+ if let Some(args) = args {
+ let mut cur = args.walk();
+ for c in args.named_children(&mut cur) {
+ if axum_callable_matches(c, bytes, target) {
+ hit_target = true;
}
}
}
+ }
// Detect verb-filter calls (`warp::get()`, `warp::post()`).
let mut cur = p.walk();
for child in p.children(&mut cur) {
@@ -843,8 +821,7 @@ mod tests {
fn finds_axum_route_get() {
let src: &[u8] = b"use axum::Router;\nfn build() -> Router { Router::new().route(\"/u/{id}\", get(show)) }\nfn show() {}\n";
let tree = parse(src);
- let (method, path) =
- find_axum_route(tree.root_node(), src, "show").expect("hit");
+ let (method, path) = find_axum_route(tree.root_node(), src, "show").expect("hit");
assert_eq!(method, HttpMethod::GET);
assert_eq!(path, "/u/{id}");
}
@@ -853,8 +830,7 @@ mod tests {
fn finds_axum_route_with_scoped_verb() {
let src: &[u8] = b"use axum::Router;\nfn build() -> Router { Router::new().route(\"/x\", axum::routing::post(save)) }\nfn save() {}\n";
let tree = parse(src);
- let (method, path) =
- find_axum_route(tree.root_node(), src, "save").expect("hit");
+ let (method, path) = find_axum_route(tree.root_node(), src, "save").expect("hit");
assert_eq!(method, HttpMethod::POST);
assert_eq!(path, "/x");
}
@@ -871,8 +847,7 @@ mod tests {
#[test]
fn finds_rocket_post_attribute() {
- let src: &[u8] =
- b"#[post(\"/save\", data = \"\")]\nfn save(body: String) {}\n";
+ let src: &[u8] = b"#[post(\"/save\", data = \"\")]\nfn save(body: String) {}\n";
let tree = parse(src);
let func = find_rust_function(tree.root_node(), src, "save").unwrap();
let (method, path) = find_method_attribute(func, src).expect("hit");
@@ -890,7 +865,11 @@ mod tests {
#[test]
fn binds_implicit_request_as_implicit() {
- let formals = vec!["req".to_string(), "request".to_string(), "state".to_string()];
+ let formals = vec![
+ "req".to_string(),
+ "request".to_string(),
+ "state".to_string(),
+ ];
let bindings = bind_rust_path_params(&formals, "/x");
for b in &bindings {
assert!(matches!(b.source, ParamSource::Implicit));
@@ -908,8 +887,7 @@ mod tests {
fn finds_warp_path_macro_with_map_target() {
let src: &[u8] = b"use warp::Filter;\nfn build() { let r = warp::path!(\"users\" / u32).map(show); }\nfn show(id: u32) -> String { String::new() }\n";
let tree = parse(src);
- let (_method, path) =
- find_warp_route(tree.root_node(), src, "show").expect("hit");
+ let (_method, path) = find_warp_route(tree.root_node(), src, "show").expect("hit");
assert!(path.contains("users"));
}
@@ -923,8 +901,7 @@ mod tests {
#[test]
fn warp_multi_typed_anonymous_placeholders_bind_positionally() {
let formals = vec!["user_id".to_string(), "post_slug".to_string()];
- let bindings =
- bind_rust_path_params(&formals, "/users/{u32}/posts/{String}");
+ let bindings = bind_rust_path_params(&formals, "/users/{u32}/posts/{String}");
assert!(matches!(bindings[0].source, ParamSource::PathSegment(_)));
assert!(matches!(bindings[1].source, ParamSource::PathSegment(_)));
}
diff --git a/src/dynamic/framework/adapters/rust_warp.rs b/src/dynamic/framework/adapters/rust_warp.rs
index 637066bb..bc3d60bc 100644
--- a/src/dynamic/framework/adapters/rust_warp.rs
+++ b/src/dynamic/framework/adapters/rust_warp.rs
@@ -112,17 +112,21 @@ mod tests {
fn skips_when_warp_not_imported() {
let src: &[u8] = b"fn show() {}\n";
let tree = parse(src);
- assert!(RustWarpAdapter
- .detect(&summary("show"), tree.root_node(), src)
- .is_none());
+ assert!(
+ RustWarpAdapter
+ .detect(&summary("show"), tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
fn skips_when_no_path_macro() {
let src: &[u8] = b"use warp::Filter;\nfn show() {}\n";
let tree = parse(src);
- assert!(RustWarpAdapter
- .detect(&summary("show"), tree.root_node(), src)
- .is_none());
+ assert!(
+ RustWarpAdapter
+ .detect(&summary("show"), tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/scheduled_cron.rs b/src/dynamic/framework/adapters/scheduled_cron.rs
index dc09eb96..2174be2c 100644
--- a/src/dynamic/framework/adapters/scheduled_cron.rs
+++ b/src/dynamic/framework/adapters/scheduled_cron.rs
@@ -139,8 +139,10 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(ScheduledCronAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ ScheduledCronAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/scheduled_sidekiq.rs b/src/dynamic/framework/adapters/scheduled_sidekiq.rs
index 86eaf1d1..7d6189e3 100644
--- a/src/dynamic/framework/adapters/scheduled_sidekiq.rs
+++ b/src/dynamic/framework/adapters/scheduled_sidekiq.rs
@@ -15,10 +15,7 @@ const ADAPTER_NAME: &str = "scheduled-sidekiq";
fn callee_is_sidekiq(name: &str) -> bool {
let last = name.rsplit_once('.').map(|(_, s)| s).unwrap_or(name);
- matches!(
- last,
- "perform_async" | "perform_in" | "perform" | "set"
- )
+ matches!(last, "perform_async" | "perform_in" | "perform" | "set")
}
fn source_imports_sidekiq(file_bytes: &[u8]) -> bool {
diff --git a/src/dynamic/framework/adapters/websocket_actioncable.rs b/src/dynamic/framework/adapters/websocket_actioncable.rs
index 15588b51..6c377b4b 100644
--- a/src/dynamic/framework/adapters/websocket_actioncable.rs
+++ b/src/dynamic/framework/adapters/websocket_actioncable.rs
@@ -37,7 +37,12 @@ fn source_imports_actioncable(file_bytes: &[u8]) -> bool {
fn extract_path(file_bytes: &[u8]) -> String {
let text = std::str::from_utf8(file_bytes).unwrap_or("");
- for needle in ["stream_from '", "stream_from \"", "stream_for '", "stream_for \""] {
+ for needle in [
+ "stream_from '",
+ "stream_from \"",
+ "stream_for '",
+ "stream_for \"",
+ ] {
if let Some(idx) = text.find(needle) {
let after = &text[idx + needle.len()..];
let close = if needle.ends_with('"') { '"' } else { '\'' };
diff --git a/src/dynamic/framework/adapters/xpath_java.rs b/src/dynamic/framework/adapters/xpath_java.rs
index eb23eefa..99dd0097 100644
--- a/src/dynamic/framework/adapters/xpath_java.rs
+++ b/src/dynamic/framework/adapters/xpath_java.rs
@@ -27,7 +27,10 @@ const ADAPTER_NAME: &str = "xpath-java";
fn callee_is_xpath_eval(name: &str) -> bool {
let last = name.rsplit_once('.').map(|(_, s)| s).unwrap_or(name);
- matches!(last, "evaluate" | "compile" | "selectNodes" | "selectSingleNode")
+ matches!(
+ last,
+ "evaluate" | "compile" | "selectNodes" | "selectSingleNode"
+ )
}
fn source_imports_xpath(file_bytes: &[u8]) -> bool {
@@ -158,9 +161,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(XpathJavaAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XpathJavaAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -176,8 +181,10 @@ mod tests {
}\n}\n";
let tree = parse_java(src);
let summary = summary_for("run", &["name"], &[0]);
- assert!(XpathJavaAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XpathJavaAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/xpath_js.rs b/src/dynamic/framework/adapters/xpath_js.rs
index 0b868363..eddb78fb 100644
--- a/src/dynamic/framework/adapters/xpath_js.rs
+++ b/src/dynamic/framework/adapters/xpath_js.rs
@@ -142,9 +142,11 @@ mod tests {
}\nmodule.exports = { run };\n";
let tree = parse_js(src);
let summary = summary_for("run", &["name"], &[0]);
- assert!(XpathJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ XpathJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -155,9 +157,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(XpathJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XpathJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -168,8 +172,10 @@ mod tests {
}\nmodule.exports = { run };\n";
let tree = parse_js(src);
let summary = summary_for("run", &["name"], &[0]);
- assert!(XpathJsAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XpathJsAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/xpath_php.rs b/src/dynamic/framework/adapters/xpath_php.rs
index fd22c3d4..2c1f1854 100644
--- a/src/dynamic/framework/adapters/xpath_php.rs
+++ b/src/dynamic/framework/adapters/xpath_php.rs
@@ -143,9 +143,11 @@ mod tests {
}\n";
let tree = parse_php(src);
let summary = summary_for("run", &["name"], &[0]);
- assert!(XpathPhpAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ XpathPhpAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -156,9 +158,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(XpathPhpAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XpathPhpAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -172,8 +176,10 @@ mod tests {
}\n";
let tree = parse_php(src);
let summary = summary_for("run", &["name"], &[0]);
- assert!(XpathPhpAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XpathPhpAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/xpath_python.rs b/src/dynamic/framework/adapters/xpath_python.rs
index 59cba13f..c2f7d7ac 100644
--- a/src/dynamic/framework/adapters/xpath_python.rs
+++ b/src/dynamic/framework/adapters/xpath_python.rs
@@ -25,7 +25,10 @@ const ADAPTER_NAME: &str = "xpath-python";
fn callee_is_xpath_eval(name: &str) -> bool {
let last = name.rsplit_once('.').map(|(_, s)| s).unwrap_or(name);
- matches!(last, "xpath" | "evaluate" | "find" | "findall" | "iterfind" | "XPath")
+ matches!(
+ last,
+ "xpath" | "evaluate" | "find" | "findall" | "iterfind" | "XPath"
+ )
}
fn source_imports_lxml(file_bytes: &[u8]) -> bool {
@@ -141,9 +144,11 @@ mod tests {
return tree.xpath(\"//user[@name='\" + name + \"']\")\n";
let tree = parse_python(src);
let summary = summary_for("run", &["name"], &[0]);
- assert!(XpathPythonAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ XpathPythonAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -154,9 +159,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(XpathPythonAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XpathPythonAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -168,8 +175,10 @@ mod tests {
return q(tree, name=name)\n";
let tree = parse_python(src);
let summary = summary_for("run", &["name"], &[0]);
- assert!(XpathPythonAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XpathPythonAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/xxe_go.rs b/src/dynamic/framework/adapters/xxe_go.rs
index 54f23628..b4624e43 100644
--- a/src/dynamic/framework/adapters/xxe_go.rs
+++ b/src/dynamic/framework/adapters/xxe_go.rs
@@ -113,9 +113,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("NewDecoder")],
..Default::default()
};
- assert!(XxeGoAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ XxeGoAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -126,9 +128,11 @@ mod tests {
name: "Add".into(),
..Default::default()
};
- assert!(XxeGoAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XxeGoAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -145,8 +149,10 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("NewDecoder")],
..Default::default()
};
- assert!(XxeGoAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XxeGoAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/xxe_java.rs b/src/dynamic/framework/adapters/xxe_java.rs
index 11f3bc3f..87625ac1 100644
--- a/src/dynamic/framework/adapters/xxe_java.rs
+++ b/src/dynamic/framework/adapters/xxe_java.rs
@@ -161,9 +161,11 @@ mod tests {
name: "run".into(),
..Default::default()
};
- assert!(XxeJavaAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XxeJavaAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -180,9 +182,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("parse")],
..Default::default()
};
- assert!(XxeJavaAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XxeJavaAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -200,8 +204,10 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("parse")],
..Default::default()
};
- assert!(XxeJavaAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XxeJavaAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/xxe_php.rs b/src/dynamic/framework/adapters/xxe_php.rs
index 74346202..d827b941 100644
--- a/src/dynamic/framework/adapters/xxe_php.rs
+++ b/src/dynamic/framework/adapters/xxe_php.rs
@@ -19,7 +19,9 @@ pub struct XxePhpAdapter;
const ADAPTER_NAME: &str = "xxe-php";
fn callee_is_xml_parser(name: &str) -> bool {
- let last = name.rsplit_once("::").map(|(_, s)| s)
+ let last = name
+ .rsplit_once("::")
+ .map(|(_, s)| s)
.or_else(|| name.rsplit_once('.').map(|(_, s)| s))
.or_else(|| name.rsplit_once("->").map(|(_, s)| s))
.unwrap_or(name);
@@ -137,16 +139,19 @@ mod tests {
#[test]
fn fires_on_simplexml_load_string() {
- let src: &[u8] = b" bool {
let last = name.rsplit_once('.').map(|(_, s)| s).unwrap_or(name);
matches!(
last,
- "XMLParser"
- | "parse"
- | "fromstring"
- | "parseString"
- | "XMLPullParser"
- | "iterparse"
+ "XMLParser" | "parse" | "fromstring" | "parseString" | "XMLPullParser" | "iterparse"
)
}
@@ -126,9 +121,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("fromstring")],
..Default::default()
};
- assert!(XxePythonAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ XxePythonAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -139,9 +136,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(XxePythonAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XxePythonAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -156,9 +155,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("fromstring")],
..Default::default()
};
- assert!(XxePythonAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XxePythonAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -171,8 +172,10 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("fromstring")],
..Default::default()
};
- assert!(XxePythonAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XxePythonAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
}
diff --git a/src/dynamic/framework/adapters/xxe_ruby.rs b/src/dynamic/framework/adapters/xxe_ruby.rs
index 077740a1..3bd85070 100644
--- a/src/dynamic/framework/adapters/xxe_ruby.rs
+++ b/src/dynamic/framework/adapters/xxe_ruby.rs
@@ -17,7 +17,9 @@ pub struct XxeRubyAdapter;
const ADAPTER_NAME: &str = "xxe-ruby";
fn callee_is_xml_parser(name: &str) -> bool {
- let last = name.rsplit_once("::").map(|(_, s)| s)
+ let last = name
+ .rsplit_once("::")
+ .map(|(_, s)| s)
.or_else(|| name.rsplit_once('.').map(|(_, s)| s))
.unwrap_or(name);
matches!(last, "new" | "parse" | "XML" | "load")
@@ -124,9 +126,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("new")],
..Default::default()
};
- assert!(XxeRubyAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ XxeRubyAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
#[test]
@@ -137,9 +141,11 @@ mod tests {
name: "add".into(),
..Default::default()
};
- assert!(XxeRubyAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XxeRubyAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -153,9 +159,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("new")],
..Default::default()
};
- assert!(XxeRubyAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XxeRubyAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -168,9 +176,11 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("XML")],
..Default::default()
};
- assert!(XxeRubyAdapter
- .detect(&summary, tree.root_node(), src)
- .is_none());
+ assert!(
+ XxeRubyAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_none()
+ );
}
#[test]
@@ -183,8 +193,10 @@ mod tests {
callees: vec![crate::summary::CalleeSite::bare("XML")],
..Default::default()
};
- assert!(XxeRubyAdapter
- .detect(&summary, tree.root_node(), src)
- .is_some());
+ assert!(
+ XxeRubyAdapter
+ .detect(&summary, tree.root_node(), src)
+ .is_some()
+ );
}
}
diff --git a/src/dynamic/lang/c.rs b/src/dynamic/lang/c.rs
index 8646082d..05639028 100644
--- a/src/dynamic/lang/c.rs
+++ b/src/dynamic/lang/c.rs
@@ -70,9 +70,12 @@ impl CShape {
let kind = spec.entry_kind.tag();
let has_main_argv = (source.contains("int main(") || source.contains("int main ("))
- && (source.contains("argc") || source.contains("char *argv")
- || source.contains("char* argv") || source.contains("char **argv"));
- let has_libfuzzer = source.contains("LLVMFuzzerTestOneInput") || entry == "LLVMFuzzerTestOneInput";
+ && (source.contains("argc")
+ || source.contains("char *argv")
+ || source.contains("char* argv")
+ || source.contains("char **argv"));
+ let has_libfuzzer =
+ source.contains("LLVMFuzzerTestOneInput") || entry == "LLVMFuzzerTestOneInput";
if has_libfuzzer {
return Self::LibfuzzerEntry;
@@ -96,7 +99,10 @@ pub fn detect_shape(spec: &HarnessSpec) -> CShape {
}
fn read_entry_source(entry_file: &str) -> String {
- let candidates = [PathBuf::from(entry_file), PathBuf::from(".").join(entry_file)];
+ let candidates = [
+ PathBuf::from(entry_file),
+ PathBuf::from(".").join(entry_file),
+ ];
for path in &candidates {
if let Ok(s) = std::fs::read_to_string(path) {
return s;
@@ -735,9 +741,21 @@ mod tests {
#[test]
fn entry_kinds_supported_is_non_empty() {
assert!(!CEmitter.entry_kinds_supported().is_empty());
- assert!(CEmitter.entry_kinds_supported().contains(&EntryKindTag::Function));
- assert!(CEmitter.entry_kinds_supported().contains(&EntryKindTag::CliSubcommand));
- assert!(CEmitter.entry_kinds_supported().contains(&EntryKindTag::LibraryApi));
+ assert!(
+ CEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::Function)
+ );
+ assert!(
+ CEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::CliSubcommand)
+ );
+ assert!(
+ CEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::LibraryApi)
+ );
}
#[test]
@@ -806,14 +824,20 @@ mod tests {
!h.source.contains("char *new_argv[8]"),
"fixed-size stack array must be gone — Argv(n>=6) used to overrun",
);
- assert!(h.source.contains("char **new_argv = (char**)calloc(3, sizeof(char*))"));
+ assert!(
+ h.source
+ .contains("char **new_argv = (char**)calloc(3, sizeof(char*))")
+ );
assert!(h.source.contains("free(new_argv);"));
let mut spec6 = make_spec(PayloadSlot::Argv(6));
spec6.entry_kind = EntryKind::CliSubcommand;
spec6.entry_name = "nyx_entry_main".into();
let h6 = emit(&spec6).unwrap();
- assert!(h6.source.contains("char **new_argv = (char**)calloc(9, sizeof(char*))"));
+ assert!(
+ h6.source
+ .contains("char **new_argv = (char**)calloc(9, sizeof(char*))")
+ );
assert!(h6.source.contains("free(new_argv);"));
}
@@ -880,7 +904,10 @@ mod tests {
// The install must come after `nyx_payload()` returns and before the
// entry invocation — otherwise a crash inside payload decode would
// be misattributed to the sink (would defeat Phase 08(b)).
- let install_pos = h.source.find("__nyx_install_crash_guard(\"run\");").unwrap();
+ let install_pos = h
+ .source
+ .find("__nyx_install_crash_guard(\"run\");")
+ .unwrap();
let payload_pos = h.source.find("char *payload = nyx_payload();").unwrap();
let invoke_pos = h.source.find("run(payload, strlen(payload));").unwrap();
assert!(
@@ -927,7 +954,8 @@ mod tests {
spec.entry_name = "main".into();
let h = emit(&spec).unwrap();
assert!(
- h.source.contains("__nyx_install_crash_guard(\"__nyx_entry_main\");"),
+ h.source
+ .contains("__nyx_install_crash_guard(\"__nyx_entry_main\");"),
"install_crash_guard must use the post-rename symbol when entry_name == 'main'",
);
}
@@ -938,14 +966,21 @@ mod tests {
spec.entry_kind = EntryKind::LibraryApi;
spec.entry_name = "LLVMFuzzerTestOneInput".into();
let h = emit(&spec).unwrap();
- assert!(h.source.contains("LLVMFuzzerTestOneInput((const uint8_t *)payload, strlen(payload))"));
+ assert!(
+ h.source
+ .contains("LLVMFuzzerTestOneInput((const uint8_t *)payload, strlen(payload))")
+ );
}
#[test]
fn emit_makefile_in_extra_files() {
let spec = make_spec(PayloadSlot::Param(0));
let h = emit(&spec).unwrap();
- let mk = h.extra_files.iter().find(|(n, _)| n == "Makefile").expect("Makefile must be staged");
+ let mk = h
+ .extra_files
+ .iter()
+ .find(|(n, _)| n == "Makefile")
+ .expect("Makefile must be staged");
assert!(mk.1.contains("nyx_harness: main.c entry.c"));
}
@@ -965,7 +1000,8 @@ mod tests {
"probe_shim banner missing from chain step source",
);
assert!(
- step.source.contains("static void __nyx_install_crash_guard("),
+ step.source
+ .contains("static void __nyx_install_crash_guard("),
"install_crash_guard missing from chain step source",
);
let shim_pos = step
diff --git a/src/dynamic/lang/cpp.rs b/src/dynamic/lang/cpp.rs
index c96e0f33..39150bad 100644
--- a/src/dynamic/lang/cpp.rs
+++ b/src/dynamic/lang/cpp.rs
@@ -51,10 +51,12 @@ impl CppShape {
let kind = spec.entry_kind.tag();
let has_main_argv = (source.contains("int main(") || source.contains("int main ("))
- && (source.contains("argc") || source.contains("char *argv")
- || source.contains("char* argv") || source.contains("char **argv"));
- let has_libfuzzer = source.contains("LLVMFuzzerTestOneInput")
- || entry == "LLVMFuzzerTestOneInput";
+ && (source.contains("argc")
+ || source.contains("char *argv")
+ || source.contains("char* argv")
+ || source.contains("char **argv"));
+ let has_libfuzzer =
+ source.contains("LLVMFuzzerTestOneInput") || entry == "LLVMFuzzerTestOneInput";
if has_libfuzzer {
return Self::LibfuzzerEntry;
@@ -76,7 +78,10 @@ pub fn detect_shape(spec: &HarnessSpec) -> CppShape {
}
fn read_entry_source(entry_file: &str) -> String {
- let candidates = [PathBuf::from(entry_file), PathBuf::from(".").join(entry_file)];
+ let candidates = [
+ PathBuf::from(entry_file),
+ PathBuf::from(".").join(entry_file),
+ ];
for path in &candidates {
if let Ok(s) = std::fs::read_to_string(path) {
return s;
@@ -649,9 +654,21 @@ mod tests {
#[test]
fn entry_kinds_supported_is_non_empty() {
assert!(!CppEmitter.entry_kinds_supported().is_empty());
- assert!(CppEmitter.entry_kinds_supported().contains(&EntryKindTag::Function));
- assert!(CppEmitter.entry_kinds_supported().contains(&EntryKindTag::CliSubcommand));
- assert!(CppEmitter.entry_kinds_supported().contains(&EntryKindTag::LibraryApi));
+ assert!(
+ CppEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::Function)
+ );
+ assert!(
+ CppEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::CliSubcommand)
+ );
+ assert!(
+ CppEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::LibraryApi)
+ );
}
#[test]
@@ -672,7 +689,8 @@ mod tests {
#[test]
fn shape_detect_libfuzzer() {
- let src = "extern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* d, size_t n) { return 0; }";
+ let src =
+ "extern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* d, size_t n) { return 0; }";
let mut spec = make_spec(PayloadSlot::Param(0));
spec.entry_kind = EntryKind::LibraryApi;
spec.entry_name = "LLVMFuzzerTestOneInput".into();
@@ -713,7 +731,10 @@ mod tests {
spec.entry_name = "nyx_entry_main".into();
let h = emit(&spec).unwrap();
assert!(h.source.contains("argv_storage.push_back(payload)"));
- assert!(h.source.contains("nyx_entry_main(static_cast(argv_storage.size()), new_argv.data())"));
+ assert!(
+ h.source
+ .contains("nyx_entry_main(static_cast(argv_storage.size()), new_argv.data())")
+ );
}
#[test]
@@ -731,7 +752,9 @@ mod tests {
);
assert!(h.source.contains("#undef main"), "undef guard missing");
assert!(
- h.source.contains("__nyx_entry_main(static_cast(argv_storage.size()), new_argv.data())"),
+ h.source.contains(
+ "__nyx_entry_main(static_cast(argv_storage.size()), new_argv.data())"
+ ),
"harness call site must target the renamed symbol",
);
assert!(h.source.contains("int main(int argc, char *argv[])"));
@@ -742,7 +765,10 @@ mod tests {
let fh = emit(&fixture_spec).unwrap();
assert!(!fh.source.contains("#define main"));
assert!(!fh.source.contains("#undef main"));
- assert!(fh.source.contains("nyx_entry_main(static_cast(argv_storage.size()), new_argv.data())"));
+ assert!(
+ fh.source
+ .contains("nyx_entry_main(static_cast(argv_storage.size()), new_argv.data())")
+ );
}
#[test]
@@ -764,9 +790,18 @@ mod tests {
h.source.contains("__nyx_install_crash_guard(\"run\");"),
"install_crash_guard call site missing or wrong callee",
);
- let install_pos = h.source.find("__nyx_install_crash_guard(\"run\");").unwrap();
- let payload_pos = h.source.find("std::string payload = nyx_payload();").unwrap();
- let invoke_pos = h.source.find("run(payload.c_str(), payload.size());").unwrap();
+ let install_pos = h
+ .source
+ .find("__nyx_install_crash_guard(\"run\");")
+ .unwrap();
+ let payload_pos = h
+ .source
+ .find("std::string payload = nyx_payload();")
+ .unwrap();
+ let invoke_pos = h
+ .source
+ .find("run(payload.c_str(), payload.size());")
+ .unwrap();
assert!(
payload_pos < install_pos && install_pos < invoke_pos,
"install_crash_guard ordering wrong: payload_pos={payload_pos} install_pos={install_pos} invoke_pos={invoke_pos}",
@@ -780,7 +815,8 @@ mod tests {
spec.entry_name = "main".into();
let h = emit(&spec).unwrap();
assert!(
- h.source.contains("__nyx_install_crash_guard(\"__nyx_entry_main\");"),
+ h.source
+ .contains("__nyx_install_crash_guard(\"__nyx_entry_main\");"),
"install_crash_guard must use post-rename symbol when entry_name == 'main'",
);
}
@@ -814,7 +850,11 @@ mod tests {
fn emit_cmake_in_extra_files() {
let spec = make_spec(PayloadSlot::Param(0));
let h = emit(&spec).unwrap();
- let mk = h.extra_files.iter().find(|(n, _)| n == "CMakeLists.txt").expect("CMakeLists.txt must be staged");
+ let mk = h
+ .extra_files
+ .iter()
+ .find(|(n, _)| n == "CMakeLists.txt")
+ .expect("CMakeLists.txt must be staged");
assert!(mk.1.contains("add_executable(nyx_harness main.cpp)"));
}
@@ -832,7 +872,8 @@ mod tests {
"probe_shim banner missing from chain step source",
);
assert!(
- step.source.contains("inline void __nyx_install_crash_guard("),
+ step.source
+ .contains("inline void __nyx_install_crash_guard("),
"install_crash_guard missing from chain step source",
);
let shim_pos = step
diff --git a/src/dynamic/lang/go.rs b/src/dynamic/lang/go.rs
index 3b465dfe..322e3be6 100644
--- a/src/dynamic/lang/go.rs
+++ b/src/dynamic/lang/go.rs
@@ -220,10 +220,10 @@ impl GoShape {
let entry = spec.entry_name.as_str();
let kind = spec.entry_kind.tag();
- let has_http_handler = source.contains("http.ResponseWriter")
- && source.contains("*http.Request");
- let has_gin_import = source.contains("github.com/gin-gonic/gin")
- || source.contains("// nyx-shape: gin");
+ let has_http_handler =
+ source.contains("http.ResponseWriter") && source.contains("*http.Request");
+ let has_gin_import =
+ source.contains("github.com/gin-gonic/gin") || source.contains("// nyx-shape: gin");
let has_gin_ctx = source.contains("gin.Context") || source.contains("*gin.Context");
let has_echo = source.contains("github.com/labstack/echo")
|| source.contains("echo.New")
@@ -286,7 +286,10 @@ pub fn detect_shape(spec: &HarnessSpec) -> GoShape {
}
fn read_entry_source(entry_file: &str) -> String {
- let candidates = [PathBuf::from(entry_file), PathBuf::from(".").join(entry_file)];
+ let candidates = [
+ PathBuf::from(entry_file),
+ PathBuf::from(".").join(entry_file),
+ ];
for path in &candidates {
if let Ok(s) = std::fs::read_to_string(path) {
return s;
@@ -595,7 +598,11 @@ pub fn emit(spec: &HarnessSpec) -> Result {
// Phase 21 (Track M.3): GraphQLResolver short-circuit (gqlgen).
if let crate::evidence::EntryKind::GraphQLResolver { type_name, field } = &spec.entry_kind {
- return Ok(emit_graphql_resolver_harness(&spec.entry_name, type_name, field));
+ return Ok(emit_graphql_resolver_harness(
+ &spec.entry_name,
+ type_name,
+ field,
+ ));
}
let entry_source = read_entry_source(&spec.entry_file);
@@ -923,13 +930,7 @@ func nyxPayload() string {{
/// Imports required by the spliced probe shim. Always present, deduped
/// against per-shape additions in [`imports_for_shape`].
-const SHIM_IMPORTS: &[&str] = &[
- "encoding/json",
- "os/signal",
- "strings",
- "syscall",
- "time",
-];
+const SHIM_IMPORTS: &[&str] = &["encoding/json", "os/signal", "strings", "syscall", "time"];
fn imports_for_shape(shape: GoShape) -> String {
let stdlib_base: &[&str] = &["encoding/base64", "os"];
@@ -939,10 +940,9 @@ fn imports_for_shape(shape: GoShape) -> String {
GoShape::GinHandler => &["net/http", "net/http/httptest"],
// Phase 17 framework variants drive a `httptest.NewServer`
// bootstrap so they need the full net/http surface.
- GoShape::GinRoute
- | GoShape::EchoRoute
- | GoShape::FiberRoute
- | GoShape::ChiRoute => &["fmt", "net/http", "net/http/httptest"],
+ GoShape::GinRoute | GoShape::EchoRoute | GoShape::FiberRoute | GoShape::ChiRoute => {
+ &["fmt", "net/http", "net/http/httptest"]
+ }
};
let local_pkgs: &[&str] = match shape {
GoShape::GinHandler => &["nyx-harness/entry", "nyx-harness/entry/gin"],
@@ -979,7 +979,10 @@ fn pre_call_setup(spec: &HarnessSpec) -> String {
match &spec.payload_slot {
PayloadSlot::EnvVar(name) => format!("\tos.Setenv({name:?}, payload)\n"),
PayloadSlot::Argv(n) => {
- let pads = (0..*n).map(|_| "\"\"".to_owned()).collect::>().join(", ");
+ let pads = (0..*n)
+ .map(|_| "\"\"".to_owned())
+ .collect::>()
+ .join(", ");
if pads.is_empty() {
"\tos.Args = []string{\"nyx_harness\", payload}\n".to_string()
} else {
@@ -1037,34 +1040,18 @@ fn invoke_for_shape(spec: &HarnessSpec, shape: GoShape, entry_fn: &str) -> Strin
// because the synthetic entry.go ships a stdlib
// `(w, r)` handler shim that mirrors the framework
// handler's body.
- GoShape::GinRoute => framework_route_invocation(
- spec,
- "NYX_GIN_TEST=1",
- entry_fn,
- use_body,
- &query_param,
- ),
- GoShape::EchoRoute => framework_route_invocation(
- spec,
- "NYX_ECHO_TEST=1",
- entry_fn,
- use_body,
- &query_param,
- ),
- GoShape::FiberRoute => framework_route_invocation(
- spec,
- "NYX_FIBER_TEST=1",
- entry_fn,
- use_body,
- &query_param,
- ),
- GoShape::ChiRoute => framework_route_invocation(
- spec,
- "NYX_CHI_TEST=1",
- entry_fn,
- use_body,
- &query_param,
- ),
+ GoShape::GinRoute => {
+ framework_route_invocation(spec, "NYX_GIN_TEST=1", entry_fn, use_body, &query_param)
+ }
+ GoShape::EchoRoute => {
+ framework_route_invocation(spec, "NYX_ECHO_TEST=1", entry_fn, use_body, &query_param)
+ }
+ GoShape::FiberRoute => {
+ framework_route_invocation(spec, "NYX_FIBER_TEST=1", entry_fn, use_body, &query_param)
+ }
+ GoShape::ChiRoute => {
+ framework_route_invocation(spec, "NYX_CHI_TEST=1", entry_fn, use_body, &query_param)
+ }
}
}
@@ -1187,10 +1174,7 @@ func main() {{
command: vec!["./nyx_harness".to_owned()],
extra_files: vec![
("go.mod".to_owned(), go_mod),
- (
- "entry/nyx_auto_registry.go".to_owned(),
- auto_registry,
- ),
+ ("entry/nyx_auto_registry.go".to_owned(), auto_registry),
],
entry_subpath: Some("entry/entry.go".to_owned()),
}
@@ -1591,9 +1575,21 @@ mod tests {
#[test]
fn entry_kinds_supported_is_non_empty() {
assert!(!GoEmitter.entry_kinds_supported().is_empty());
- assert!(GoEmitter.entry_kinds_supported().contains(&EntryKindTag::Function));
- assert!(GoEmitter.entry_kinds_supported().contains(&EntryKindTag::HttpRoute));
- assert!(GoEmitter.entry_kinds_supported().contains(&EntryKindTag::CliSubcommand));
+ assert!(
+ GoEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::Function)
+ );
+ assert!(
+ GoEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::HttpRoute)
+ );
+ assert!(
+ GoEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::CliSubcommand)
+ );
}
#[test]
@@ -1644,7 +1640,8 @@ mod tests {
#[test]
fn shape_detect_gin_route() {
- let src = "package main\nimport \"github.com/gin-gonic/gin\"\nfunc Handle(c *gin.Context) {}";
+ let src =
+ "package main\nimport \"github.com/gin-gonic/gin\"\nfunc Handle(c *gin.Context) {}";
let spec = make_spec_with(EntryKind::HttpRoute, "Handle", "entry.go");
assert_eq!(GoShape::detect(&spec, src), GoShape::GinRoute);
}
@@ -1769,7 +1766,8 @@ mod tests {
"install_crash_guard definition missing from generated main.go",
);
assert!(
- h.source.contains("__nyx_install_crash_guard(\"HandleRequest\")"),
+ h.source
+ .contains("__nyx_install_crash_guard(\"HandleRequest\")"),
"install_crash_guard call site missing or wrong callee in main()",
);
let install_pos = h
diff --git a/src/dynamic/lang/java.rs b/src/dynamic/lang/java.rs
index 7f337ac8..2a46c81f 100644
--- a/src/dynamic/lang/java.rs
+++ b/src/dynamic/lang/java.rs
@@ -275,7 +275,6 @@ impl JavaShape {
// the JDK accepts whitespace / newline / modifier variation that no
// single template captures.)
-
// ── Probe shim (Phase 06 + Phase 08) ─────────────────────────────────────────
/// Source of the `__nyx_probe` shim for the Java harness (Phase 06 —
@@ -617,7 +616,11 @@ pub fn emit(spec: &HarnessSpec) -> Result {
if let crate::evidence::EntryKind::ScheduledJob { schedule } = &spec.entry_kind {
let entry_source = read_entry_source(&spec.entry_file);
let entry_class = derive_entry_class(&entry_source);
- return Ok(emit_scheduled_job_harness(spec, schedule.as_deref(), &entry_class));
+ return Ok(emit_scheduled_job_harness(
+ spec,
+ schedule.as_deref(),
+ &entry_class,
+ ));
}
// Phase 21 (Track M.3): Middleware short-circuit (Spring HandlerInterceptor / Filter).
@@ -1754,9 +1757,9 @@ fn invoke_for_shape(spec: &HarnessSpec, shape: JavaShape, entry_class: &str) ->
JavaShape::StaticMain => format!(
" String[] mainArgs = new String[] {{ payload }};\n {entry_class}.main(mainArgs);"
),
- JavaShape::ServletDoGet => format!(
- " invokeServlet({entry_class}.class, \"doGet\", payload, \"GET\");"
- ),
+ JavaShape::ServletDoGet => {
+ format!(" invokeServlet({entry_class}.class, \"doGet\", payload, \"GET\");")
+ }
JavaShape::ServletDoPost => format!(
" invokeServlet({entry_class}.class, \"doPost\", payload, \"POST\");"
),
@@ -1772,20 +1775,18 @@ fn invoke_for_shape(spec: &HarnessSpec, shape: JavaShape, entry_class: &str) ->
" System.out.println(\"NYX_SPRING_TEST=1\");\n invokeReflective({entry_class}.class, \"{method}\", payload);"
)
} else {
- format!(
- " invokeReflective({entry_class}.class, \"{method}\", payload);"
- )
+ format!(" invokeReflective({entry_class}.class, \"{method}\", payload);")
}
}
- JavaShape::QuarkusRoute => format!(
- " invokeReflective({entry_class}.class, \"{method}\", payload);"
- ),
- JavaShape::MicronautRoute => format!(
- " invokeReflective({entry_class}.class, \"{method}\", payload);"
- ),
- JavaShape::JunitTest => format!(
- " invokeJunitTest({entry_class}.class, \"{method}\");"
- ),
+ JavaShape::QuarkusRoute => {
+ format!(" invokeReflective({entry_class}.class, \"{method}\", payload);")
+ }
+ JavaShape::MicronautRoute => {
+ format!(" invokeReflective({entry_class}.class, \"{method}\", payload);")
+ }
+ JavaShape::JunitTest => {
+ format!(" invokeJunitTest({entry_class}.class, \"{method}\");")
+ }
}
}
@@ -1794,9 +1795,9 @@ fn shape_helpers(shape: JavaShape) -> &'static str {
match shape {
JavaShape::StaticMethod | JavaShape::StaticMain => "",
JavaShape::ServletDoGet | JavaShape::ServletDoPost => SERVLET_HELPER,
- JavaShape::SpringController
- | JavaShape::QuarkusRoute
- | JavaShape::MicronautRoute => REFLECTIVE_HELPER,
+ JavaShape::SpringController | JavaShape::QuarkusRoute | JavaShape::MicronautRoute => {
+ REFLECTIVE_HELPER
+ }
JavaShape::JunitTest => JUNIT_HELPER,
}
}
@@ -2522,15 +2523,21 @@ mod tests {
#[test]
fn entry_kinds_supported_is_non_empty() {
assert!(!JavaEmitter.entry_kinds_supported().is_empty());
- assert!(JavaEmitter
- .entry_kinds_supported()
- .contains(&EntryKindTag::Function));
- assert!(JavaEmitter
- .entry_kinds_supported()
- .contains(&EntryKindTag::HttpRoute));
- assert!(JavaEmitter
- .entry_kinds_supported()
- .contains(&EntryKindTag::CliSubcommand));
+ assert!(
+ JavaEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::Function)
+ );
+ assert!(
+ JavaEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::HttpRoute)
+ );
+ assert!(
+ JavaEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::CliSubcommand)
+ );
}
#[test]
@@ -2602,7 +2609,8 @@ mod tests {
#[test]
fn shape_detect_junit_test() {
- let src = "import org.junit.jupiter.api.Test;\npublic class V { @Test public void testRun() {} }";
+ let src =
+ "import org.junit.jupiter.api.Test;\npublic class V { @Test public void testRun() {} }";
let spec = make_spec_with(EntryKind::Function, "testRun", "V.java");
assert_eq!(JavaShape::detect(&spec, src), JavaShape::JunitTest);
}
@@ -2689,7 +2697,11 @@ mod tests {
let mut spec = make_spec_with(EntryKind::HttpRoute, "doGet", &entry_file);
spec.payload_slot = PayloadSlot::QueryParam("payload".into());
let harness = emit(&spec).unwrap();
- let paths: Vec<&str> = harness.extra_files.iter().map(|(p, _)| p.as_str()).collect();
+ let paths: Vec<&str> = harness
+ .extra_files
+ .iter()
+ .map(|(p, _)| p.as_str())
+ .collect();
assert!(
paths.contains(&"javax/servlet/http/HttpServletRequest.java"),
"doGet bundle missing javax HttpServletRequest stub; got {paths:?}"
@@ -2714,7 +2726,11 @@ mod tests {
spec.payload_slot = PayloadSlot::HttpBody;
let harness = emit(&spec).unwrap();
assert!(!harness.extra_files.is_empty(), "doPost bundle is empty");
- let paths: Vec<&str> = harness.extra_files.iter().map(|(p, _)| p.as_str()).collect();
+ let paths: Vec<&str> = harness
+ .extra_files
+ .iter()
+ .map(|(p, _)| p.as_str())
+ .collect();
assert!(paths.contains(&"javax/servlet/http/HttpServlet.java"));
assert!(paths.contains(&"jakarta/servlet/http/HttpServlet.java"));
}
@@ -2729,7 +2745,11 @@ mod tests {
assert!(
harness.extra_files.is_empty(),
"non-servlet shape unexpectedly ships extra files: {:?}",
- harness.extra_files.iter().map(|(p, _)| p).collect::>()
+ harness
+ .extra_files
+ .iter()
+ .map(|(p, _)| p)
+ .collect::>()
);
}
@@ -2756,7 +2776,11 @@ mod tests {
);
let spec = make_spec_with(EntryKind::HttpRoute, "doGet", &entry_file);
let harness = emit(&spec).unwrap();
- let paths: Vec<&str> = harness.extra_files.iter().map(|(p, _)| p.as_str()).collect();
+ let paths: Vec<&str> = harness
+ .extra_files
+ .iter()
+ .map(|(p, _)| p.as_str())
+ .collect();
// Servlet stubs are present (same as the non-OWASP servlet case).
assert!(paths.contains(&"javax/servlet/http/HttpServletRequest.java"));
// OWASP helpers + esapi + spring stubs are appended.
@@ -2779,7 +2803,11 @@ mod tests {
);
let spec = make_spec_with(EntryKind::HttpRoute, "doGet", &entry_file);
let harness = emit(&spec).unwrap();
- let paths: Vec<&str> = harness.extra_files.iter().map(|(p, _)| p.as_str()).collect();
+ let paths: Vec<&str> = harness
+ .extra_files
+ .iter()
+ .map(|(p, _)| p.as_str())
+ .collect();
assert!(
!paths.iter().any(|p| p.starts_with("org/owasp/")),
"plain servlet entry unexpectedly bundles OWASP stubs: {paths:?}"
@@ -2803,7 +2831,11 @@ mod tests {
);
let spec = make_spec_with(EntryKind::Function, "run", &entry_file);
let harness = emit(&spec).unwrap();
- let paths: Vec<&str> = harness.extra_files.iter().map(|(p, _)| p.as_str()).collect();
+ let paths: Vec<&str> = harness
+ .extra_files
+ .iter()
+ .map(|(p, _)| p.as_str())
+ .collect();
assert!(paths.contains(&"org/owasp/benchmark/helpers/Utils.java"));
// No servlet stubs for a non-servlet shape.
assert!(!paths.iter().any(|p| p.starts_with("javax/servlet/")));
@@ -2965,7 +2997,10 @@ mod tests {
"Java chain step must keep its NYX_PREV_OUTPUT forwarder"
);
let shim_pos = step.source.find("__nyx_probe").unwrap();
- let driver_pos = step.source.find("System.getenv(\"NYX_PREV_OUTPUT\")").unwrap();
+ let driver_pos = step
+ .source
+ .find("System.getenv(\"NYX_PREV_OUTPUT\")")
+ .unwrap();
assert!(
shim_pos < driver_pos,
"probe shim must come before the driver so the shim's helpers are in scope when a sink rewrite splices in"
@@ -2983,10 +3018,7 @@ mod tests {
// Drive the public `detect_shape(spec)` wrapper end-to-end:
// write a representative source to a tempfile, then assert the
// wrapper reads it and produces the expected JavaShape variant.
- let dir = std::env::temp_dir().join(format!(
- "nyx_detect_shape_{}",
- std::process::id()
- ));
+ let dir = std::env::temp_dir().join(format!("nyx_detect_shape_{}", std::process::id()));
let _ = std::fs::create_dir_all(&dir);
let cases: &[(&str, &str, &str, EntryKind, JavaShape)] = &[
(
diff --git a/src/dynamic/lang/java_owasp_stubs.rs b/src/dynamic/lang/java_owasp_stubs.rs
index 2898609c..571bee9f 100644
--- a/src/dynamic/lang/java_owasp_stubs.rs
+++ b/src/dynamic/lang/java_owasp_stubs.rs
@@ -78,14 +78,8 @@ pub fn owasp_stub_files() -> Vec<(String, String)> {
"org/owasp/benchmark/helpers/ThingInterface.java".to_owned(),
thing_interface_stub(),
),
- (
- "org/owasp/esapi/ESAPI.java".to_owned(),
- esapi_stub(),
- ),
- (
- "org/owasp/esapi/Encoder.java".to_owned(),
- encoder_stub(),
- ),
+ ("org/owasp/esapi/ESAPI.java".to_owned(), esapi_stub()),
+ ("org/owasp/esapi/Encoder.java".to_owned(), encoder_stub()),
(
"org/springframework/dao/DataAccessException.java".to_owned(),
data_access_exception_stub(),
@@ -344,10 +338,7 @@ mod tests {
#[test]
fn bundle_includes_owasp_helpers() {
- let paths: Vec = owasp_stub_files()
- .into_iter()
- .map(|(p, _)| p)
- .collect();
+ let paths: Vec = owasp_stub_files().into_iter().map(|(p, _)| p).collect();
for required in &[
"org/owasp/benchmark/helpers/Utils.java",
"org/owasp/benchmark/helpers/DatabaseHelper.java",
@@ -457,6 +448,10 @@ mod tests {
// count drift here usually means a stub was added without
// updating the assertion or a stub got accidentally dropped.
let files = owasp_stub_files();
- assert_eq!(files.len(), 13, "expected 9 owasp + 4 springframework stubs");
+ assert_eq!(
+ files.len(),
+ 13,
+ "expected 9 owasp + 4 springframework stubs"
+ );
}
}
diff --git a/src/dynamic/lang/java_servlet_stubs.rs b/src/dynamic/lang/java_servlet_stubs.rs
index 4880ab4e..da969d15 100644
--- a/src/dynamic/lang/java_servlet_stubs.rs
+++ b/src/dynamic/lang/java_servlet_stubs.rs
@@ -69,10 +69,7 @@ fn make_servlet_stubs(pkg: &str) -> Vec<(String, String)> {
format!("{http_path}/HttpServletResponse.java"),
http_servlet_response(&http),
),
- (
- format!("{http_path}/HttpSession.java"),
- http_session(&http),
- ),
+ (format!("{http_path}/HttpSession.java"), http_session(&http)),
(format!("{http_path}/Cookie.java"), cookie(&http)),
]
}
diff --git a/src/dynamic/lang/javascript.rs b/src/dynamic/lang/javascript.rs
index cd1240b4..9e9e1f07 100644
--- a/src/dynamic/lang/javascript.rs
+++ b/src/dynamic/lang/javascript.rs
@@ -15,11 +15,13 @@
//! - [`PayloadSlot::Argv`] — coerced to positional `Param(0)` by build_call.
use crate::dynamic::environment::{Environment, RuntimeArtifacts};
-use crate::dynamic::lang::{js_shared, ChainStepHarness, ChainStepTerminal, HarnessSource, LangEmitter};
+use crate::dynamic::lang::{
+ ChainStepHarness, ChainStepTerminal, HarnessSource, LangEmitter, js_shared,
+};
use crate::dynamic::spec::{EntryKindTag, HarnessSpec};
use crate::evidence::UnsupportedReason;
-pub use js_shared::{detect_shape, materialize_node, probe_shim, JsShape};
+pub use js_shared::{JsShape, detect_shape, materialize_node, probe_shim};
/// Zero-sized [`LangEmitter`] handle for JavaScript.
pub struct JavaScriptEmitter;
@@ -115,7 +117,11 @@ mod tests {
fn emit_env_var_slot() {
let spec = make_spec(PayloadSlot::EnvVar("DB_HOST".into()));
let harness = emit(&spec).unwrap();
- assert!(harness.source.contains("process.env[\"DB_HOST\"] = payload"));
+ assert!(
+ harness
+ .source
+ .contains("process.env[\"DB_HOST\"] = payload")
+ );
}
#[test]
@@ -155,5 +161,4 @@ mod tests {
assert!(hint.contains("HttpRoute"));
assert!(hint.contains("Phase 13"));
}
-
}
diff --git a/src/dynamic/lang/js_shared.rs b/src/dynamic/lang/js_shared.rs
index c3573dc3..6dc3acdd 100644
--- a/src/dynamic/lang/js_shared.rs
+++ b/src/dynamic/lang/js_shared.rs
@@ -79,11 +79,21 @@ impl JsShape {
// ── Framework / runtime markers ─────────────────────────────
let has_express = source_has_marker(
source,
- &["require('express')", "require(\"express\")", "from 'express'", "from \"express\""],
+ &[
+ "require('express')",
+ "require(\"express\")",
+ "from 'express'",
+ "from \"express\"",
+ ],
);
let has_koa = source_has_marker(
source,
- &["require('koa')", "require(\"koa\")", "from 'koa'", "from \"koa\""],
+ &[
+ "require('koa')",
+ "require(\"koa\")",
+ "from 'koa'",
+ "from \"koa\"",
+ ],
);
let has_fastify = source_has_marker(
source,
@@ -109,7 +119,13 @@ impl JsShape {
);
let has_next = source_has_marker(
source,
- &["from 'next'", "from \"next\"", "NextApiRequest", "NextApiResponse", "// nyx-shape: next"],
+ &[
+ "from 'next'",
+ "from \"next\"",
+ "NextApiRequest",
+ "NextApiResponse",
+ "// nyx-shape: next",
+ ],
);
let has_jsdom = source_has_marker(
source,
@@ -374,9 +390,10 @@ pub fn materialize_node(env: &Environment) -> RuntimeArtifacts {
}
for fw in &env.frameworks {
if let Some(name) = node_framework_pkg_name(*fw)
- && seen.insert(name.to_owned()) {
- deps.push((name.to_owned(), "*"));
- }
+ && seen.insert(name.to_owned())
+ {
+ deps.push((name.to_owned(), "*"));
+ }
}
deps.sort_by(|a, b| a.0.cmp(&b.0));
@@ -406,10 +423,26 @@ pub fn materialize_node(env: &Environment) -> RuntimeArtifacts {
fn is_node_builtin(name: &str) -> bool {
matches!(
name,
- "fs" | "path" | "http" | "https" | "url" | "crypto" | "stream"
- | "util" | "child_process" | "os" | "events" | "buffer"
- | "querystring" | "zlib" | "assert" | "process" | "net"
- | "tls" | "dns" | "readline" | "tty"
+ "fs" | "path"
+ | "http"
+ | "https"
+ | "url"
+ | "crypto"
+ | "stream"
+ | "util"
+ | "child_process"
+ | "os"
+ | "events"
+ | "buffer"
+ | "querystring"
+ | "zlib"
+ | "assert"
+ | "process"
+ | "net"
+ | "tls"
+ | "dns"
+ | "readline"
+ | "tty"
)
}
@@ -431,24 +464,54 @@ fn node_framework_pkg_name(fw: DetectedFramework) -> Option<&'static str> {
fn extra_files_for_shape(shape: JsShape) -> Vec<(String, String)> {
match shape {
JsShape::Express => vec![
- ("package.json".to_owned(), package_json_for("express", "^4.19.2")),
- ("package-lock.json".to_owned(), package_lock_skeleton("nyx-harness-express")),
+ (
+ "package.json".to_owned(),
+ package_json_for("express", "^4.19.2"),
+ ),
+ (
+ "package-lock.json".to_owned(),
+ package_lock_skeleton("nyx-harness-express"),
+ ),
],
JsShape::Koa => vec![
- ("package.json".to_owned(), package_json_for("koa", "^2.15.3")),
- ("package-lock.json".to_owned(), package_lock_skeleton("nyx-harness-koa")),
+ (
+ "package.json".to_owned(),
+ package_json_for("koa", "^2.15.3"),
+ ),
+ (
+ "package-lock.json".to_owned(),
+ package_lock_skeleton("nyx-harness-koa"),
+ ),
],
JsShape::NextRoute => vec![
- ("package.json".to_owned(), package_json_for("next", "^14.2.5")),
- ("package-lock.json".to_owned(), package_lock_skeleton("nyx-harness-next")),
+ (
+ "package.json".to_owned(),
+ package_json_for("next", "^14.2.5"),
+ ),
+ (
+ "package-lock.json".to_owned(),
+ package_lock_skeleton("nyx-harness-next"),
+ ),
],
JsShape::BrowserEvent => vec![
- ("package.json".to_owned(), package_json_for("jsdom", "^24.1.1")),
- ("package-lock.json".to_owned(), package_lock_skeleton("nyx-harness-jsdom")),
+ (
+ "package.json".to_owned(),
+ package_json_for("jsdom", "^24.1.1"),
+ ),
+ (
+ "package-lock.json".to_owned(),
+ package_lock_skeleton("nyx-harness-jsdom"),
+ ),
],
JsShape::Fastify => vec![
- ("package.json".to_owned(), package_json_for("fastify", "^4.28.1")),
- ("package-lock.json".to_owned(), package_lock_skeleton("nyx-harness-fastify")),
+ (
+ "package.json".to_owned(),
+ package_json_for("fastify", "^4.28.1"),
+ ),
+ (
+ "package-lock.json".to_owned(),
+ package_lock_skeleton("nyx-harness-fastify"),
+ ),
],
JsShape::Nest => vec![
(
@@ -634,7 +697,11 @@ fn emit_class_method(
is_typescript: bool,
) -> HarnessSource {
let probe = probe_shim();
- let entry_subpath = if is_typescript { "entry.ts" } else { "entry.js" };
+ let entry_subpath = if is_typescript {
+ "entry.ts"
+ } else {
+ "entry.js"
+ };
let entry_require_path = entry_require_path(entry_subpath);
let mock_http = crate::dynamic::stubs::mock_source(
crate::dynamic::stubs::MockKind::HttpClient,
@@ -733,13 +800,13 @@ if (typeof _m !== 'function') {{
/// and publishes the payload onto `queue` so the handler fires
/// synchronously. SQS is the only broker Node has a dedicated Phase
/// 20 adapter for (`sqs-node`); the dispatch defaults to it.
-fn emit_message_handler(
- spec: &HarnessSpec,
- queue: &str,
- is_typescript: bool,
-) -> HarnessSource {
+fn emit_message_handler(spec: &HarnessSpec, queue: &str, is_typescript: bool) -> HarnessSource {
let probe = probe_shim();
- let entry_subpath = if is_typescript { "entry.ts" } else { "entry.js" };
+ let entry_subpath = if is_typescript {
+ "entry.ts"
+ } else {
+ "entry.js"
+ };
let entry_require_path = entry_require_path(entry_subpath);
let handler = &spec.entry_name;
let sqs_src = crate::dynamic::stubs::sqs_source(crate::symbol::Lang::JavaScript);
@@ -808,7 +875,11 @@ _broker.subscribe({queue:?}, async (envelope) => {{
fn nyx_js_preamble(spec: &HarnessSpec, is_typescript: bool) -> (String, String) {
let probe = probe_shim();
- let entry_subpath = if is_typescript { "entry.ts" } else { "entry.js" };
+ let entry_subpath = if is_typescript {
+ "entry.ts"
+ } else {
+ "entry.js"
+ };
let require_path = entry_require_path(entry_subpath);
let preamble = format!(
r#"'use strict';
@@ -844,7 +915,11 @@ process.stdout.write('__NYX_SINK_HIT__\n');
(preamble, entry_subpath.to_owned())
}
-fn emit_scheduled_job(spec: &HarnessSpec, schedule: Option<&str>, is_typescript: bool) -> HarnessSource {
+fn emit_scheduled_job(
+ spec: &HarnessSpec,
+ schedule: Option<&str>,
+ is_typescript: bool,
+) -> HarnessSource {
let (preamble, entry_subpath) = nyx_js_preamble(spec, is_typescript);
let handler = &spec.entry_name;
let schedule_repr = schedule.unwrap_or("");
@@ -2214,21 +2289,33 @@ mod tests {
#[test]
fn detect_express_via_require() {
let src = "const express = require('express');\nfunction ping(req, res) {}";
- let spec = make_spec(EntryKind::Function, "ping", PayloadSlot::QueryParam("host".into()));
+ let spec = make_spec(
+ EntryKind::Function,
+ "ping",
+ PayloadSlot::QueryParam("host".into()),
+ );
assert_eq!(JsShape::detect(&spec, src), JsShape::Express);
}
#[test]
fn detect_koa_via_require() {
let src = "const Koa = require('koa');\nasync function ping(ctx) {}";
- let spec = make_spec(EntryKind::Function, "ping", PayloadSlot::QueryParam("host".into()));
+ let spec = make_spec(
+ EntryKind::Function,
+ "ping",
+ PayloadSlot::QueryParam("host".into()),
+ );
assert_eq!(JsShape::detect(&spec, src), JsShape::Koa);
}
#[test]
fn detect_next_via_marker() {
let src = "// nyx-shape: next\nmodule.exports = async function handler(req, res) {};";
- let spec = make_spec(EntryKind::HttpRoute, "handler", PayloadSlot::QueryParam("host".into()));
+ let spec = make_spec(
+ EntryKind::HttpRoute,
+ "handler",
+ PayloadSlot::QueryParam("host".into()),
+ );
assert_eq!(JsShape::detect(&spec, src), JsShape::NextRoute);
}
@@ -2248,7 +2335,8 @@ mod tests {
#[test]
fn detect_esm_default_export() {
- let src = "// nyx-shape: esm-default\nexport default function runPing(host) { return host; }";
+ let src =
+ "// nyx-shape: esm-default\nexport default function runPing(host) { return host; }";
let spec = make_spec(EntryKind::Function, "runPing", PayloadSlot::Param(0));
assert_eq!(JsShape::detect(&spec, src), JsShape::EsModuleDefault);
}
@@ -2262,7 +2350,11 @@ mod tests {
#[test]
fn emit_express_uses_mock_req_res() {
- let spec = make_spec(EntryKind::HttpRoute, "ping", PayloadSlot::QueryParam("host".into()));
+ let spec = make_spec(
+ EntryKind::HttpRoute,
+ "ping",
+ PayloadSlot::QueryParam("host".into()),
+ );
let src = generate_for_shape(&spec, JsShape::Express, "entry.js");
assert!(src.contains("Express handler"));
assert!(src.contains("_req.query[_payload_key] = payload"));
@@ -2270,7 +2362,11 @@ mod tests {
#[test]
fn emit_koa_awaits_middleware() {
- let spec = make_spec(EntryKind::HttpRoute, "ping", PayloadSlot::QueryParam("host".into()));
+ let spec = make_spec(
+ EntryKind::HttpRoute,
+ "ping",
+ PayloadSlot::QueryParam("host".into()),
+ );
let src = generate_for_shape(&spec, JsShape::Koa, "entry.js");
assert!(src.contains("await _mw(_ctx"));
}
@@ -2293,7 +2389,11 @@ mod tests {
#[test]
fn extra_files_for_express_has_package_json() {
let extras = extra_files_for_shape(JsShape::Express);
- assert!(extras.iter().any(|(p, c)| p == "package.json" && c.contains("express")));
+ assert!(
+ extras
+ .iter()
+ .any(|(p, c)| p == "package.json" && c.contains("express"))
+ );
assert!(extras.iter().any(|(p, _)| p == "package-lock.json"));
}
diff --git a/src/dynamic/lang/mod.rs b/src/dynamic/lang/mod.rs
index 3d285161..cb24498a 100644
--- a/src/dynamic/lang/mod.rs
+++ b/src/dynamic/lang/mod.rs
@@ -257,8 +257,7 @@ pub fn emit(spec: &HarnessSpec) -> Result {
if !supported.is_empty() && !supported.contains(&spec.entry_kind.tag()) {
return Err(UnsupportedReason::EntryKindUnsupported);
}
- dispatch(spec.lang, |e| e.emit(spec))
- .unwrap_or(Err(UnsupportedReason::LangUnsupported))
+ dispatch(spec.lang, |e| e.emit(spec)).unwrap_or(Err(UnsupportedReason::LangUnsupported))
}
/// Public free-fn dispatcher for the supported entry kinds of `lang`.
@@ -276,9 +275,7 @@ pub fn entry_kinds_supported(lang: Lang) -> &'static [EntryKindTag] {
/// callers do not need to special-case that path.
pub fn entry_kind_hint(lang: Lang, attempted: EntryKindTag) -> String {
dispatch(lang, |e| e.entry_kind_hint(attempted)).unwrap_or_else(|| {
- format!(
- "no harness emitter is registered for {lang:?}; attempted {attempted}"
- )
+ format!("no harness emitter is registered for {lang:?}; attempted {attempted}")
})
}
@@ -384,13 +381,13 @@ mod tests {
T::WebSocket
);
assert_eq!(
- EntryKind::Middleware { name: "auth".into() }.tag(),
+ EntryKind::Middleware {
+ name: "auth".into()
+ }
+ .tag(),
T::Middleware
);
- assert_eq!(
- EntryKind::Migration { version: None }.tag(),
- T::Migration
- );
+ assert_eq!(EntryKind::Migration { version: None }.tag(), T::Migration);
assert_eq!(EntryKind::Unknown.tag(), T::Unknown);
}
@@ -418,16 +415,14 @@ mod tests {
// juniper (Rust), gqlgen (Go). TypeScript shares the JS
// emitter so it inherits resolver dispatch.
(
- Lang::Python
- | Lang::JavaScript
- | Lang::TypeScript
- | Lang::Rust
- | Lang::Go,
+ Lang::Python | Lang::JavaScript | Lang::TypeScript | Lang::Rust | Lang::Go,
T::GraphQLResolver,
) => true,
// WebSocket: socketio + channels (Python), ws (JS),
// actioncable (Ruby).
- (Lang::Python | Lang::JavaScript | Lang::TypeScript | Lang::Ruby, T::WebSocket) => true,
+ (Lang::Python | Lang::JavaScript | Lang::TypeScript | Lang::Ruby, T::WebSocket) => {
+ true
+ }
// Middleware: express (JS), django (Python), rails (Ruby),
// spring (Java), laravel (PHP).
(
@@ -442,11 +437,7 @@ mod tests {
// Migration: rails (Ruby), django + flask (Python),
// laravel (PHP), sequelize + prisma (JS).
(
- Lang::Python
- | Lang::JavaScript
- | Lang::TypeScript
- | Lang::Ruby
- | Lang::Php,
+ Lang::Python | Lang::JavaScript | Lang::TypeScript | Lang::Ruby | Lang::Php,
T::Migration,
) => true,
_ => false,
@@ -505,13 +496,7 @@ mod tests {
Lang::TypeScript,
Lang::Go,
];
- let unsupported_langs = [
- Lang::Php,
- Lang::Ruby,
- Lang::Rust,
- Lang::C,
- Lang::Cpp,
- ];
+ let unsupported_langs = [Lang::Php, Lang::Ruby, Lang::Rust, Lang::C, Lang::Cpp];
for lang in supported_langs {
let supported = entry_kinds_supported(lang);
assert!(
diff --git a/src/dynamic/lang/php.rs b/src/dynamic/lang/php.rs
index 12d448b5..ae166fd5 100644
--- a/src/dynamic/lang/php.rs
+++ b/src/dynamic/lang/php.rs
@@ -212,8 +212,8 @@ impl PhpShape {
|| source.contains("$router->post(")
|| source.contains("// nyx-shape: route");
let has_argv = source.contains("$argv") || source.contains("// nyx-shape: cli");
- let has_function_decl = source.contains("function ")
- && !source.trim_start().starts_with(" PhpShape {
}
fn read_entry_source(entry_file: &str) -> String {
- let candidates = [PathBuf::from(entry_file), PathBuf::from(".").join(entry_file)];
+ let candidates = [
+ PathBuf::from(entry_file),
+ PathBuf::from(".").join(entry_file),
+ ];
for path in &candidates {
if let Ok(s) = std::fs::read_to_string(path) {
return s;
@@ -1124,7 +1127,11 @@ fn generate_source(spec: &HarnessSpec, shape: PhpShape) -> String {
let call_expr = build_call_expr(spec, shape, entry_fn);
let shim = probe_shim();
let toolchain_marker = build_toolchain_marker(shape);
- let crash_callee = if entry_fn.is_empty() { "main" } else { entry_fn.as_str() };
+ let crash_callee = if entry_fn.is_empty() {
+ "main"
+ } else {
+ entry_fn.as_str()
+ };
format!(
r#" String {
"null".to_owned()
}
}
- PhpShape::RouteClosure
- | PhpShape::LaravelRoute
- | PhpShape::CodeIgniterRoute => {
+ PhpShape::RouteClosure | PhpShape::LaravelRoute | PhpShape::CodeIgniterRoute => {
// Entry script publishes the route closure via
// `$GLOBALS['__nyx_route']`. When the global is missing,
// fall back to calling the named function directly.
@@ -1608,15 +1613,21 @@ mod tests {
#[test]
fn entry_kinds_supported_is_non_empty() {
assert!(!PhpEmitter.entry_kinds_supported().is_empty());
- assert!(PhpEmitter
- .entry_kinds_supported()
- .contains(&EntryKindTag::Function));
- assert!(PhpEmitter
- .entry_kinds_supported()
- .contains(&EntryKindTag::HttpRoute));
- assert!(PhpEmitter
- .entry_kinds_supported()
- .contains(&EntryKindTag::CliSubcommand));
+ assert!(
+ PhpEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::Function)
+ );
+ assert!(
+ PhpEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::HttpRoute)
+ );
+ assert!(
+ PhpEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::CliSubcommand)
+ );
}
#[test]
diff --git a/src/dynamic/lang/python.rs b/src/dynamic/lang/python.rs
index 4964fcc3..76948faa 100644
--- a/src/dynamic/lang/python.rs
+++ b/src/dynamic/lang/python.rs
@@ -187,8 +187,7 @@ impl PythonShape {
let kind = spec.entry_kind.tag();
// ── Framework-first detection ────────────────────────────────
- let has_flask =
- source_has_marker(source, &["from flask", "import flask", "Flask("]);
+ let has_flask = source_has_marker(source, &["from flask", "import flask", "Flask("]);
let has_fastapi = source_has_marker(
source,
&["from fastapi", "import fastapi", "FastAPI(", "APIRouter("],
@@ -270,8 +269,7 @@ fn source_has_marker(source: &str, markers: &[&str]) -> bool {
fn function_is_pytest(source: &str, name: &str) -> bool {
let needle = format!("def {name}(");
let async_needle = format!("async def {name}(");
- (source.contains(&needle) || source.contains(&async_needle))
- && name.starts_with("test_")
+ (source.contains(&needle) || source.contains(&async_needle)) && name.starts_with("test_")
}
fn function_is_async(source: &str, name: &str) -> bool {
@@ -613,8 +611,12 @@ fn python_framework_pkg_name(fw: DetectedFramework) -> Option<&'static str> {
/// pre-Phase-12 behaviour.
pub fn emit(spec: &HarnessSpec) -> Result {
match &spec.payload_slot {
- PayloadSlot::Param(_) | PayloadSlot::EnvVar(_) | PayloadSlot::Stdin
- | PayloadSlot::QueryParam(_) | PayloadSlot::HttpBody | PayloadSlot::Argv(_) => {}
+ PayloadSlot::Param(_)
+ | PayloadSlot::EnvVar(_)
+ | PayloadSlot::Stdin
+ | PayloadSlot::QueryParam(_)
+ | PayloadSlot::HttpBody
+ | PayloadSlot::Argv(_) => {}
}
// Phase 03 (Track J.1): short-circuit to the deserialize harness
@@ -1934,10 +1936,9 @@ fn read_entry_source(entry_file: &str) -> String {
fn extra_files_for_shape(shape: PythonShape) -> Vec<(String, String)> {
match shape {
PythonShape::FlaskRoute => vec![("requirements.txt".to_owned(), "Flask\n".to_owned())],
- PythonShape::FastApiRoute => vec![(
- "requirements.txt".to_owned(),
- "fastapi\nhttpx\n".to_owned(),
- )],
+ PythonShape::FastApiRoute => {
+ vec![("requirements.txt".to_owned(), "fastapi\nhttpx\n".to_owned())]
+ }
PythonShape::StarletteRoute => vec![(
"requirements.txt".to_owned(),
"starlette\nhttpx\n".to_owned(),
@@ -2494,7 +2495,12 @@ fn build_call(spec: &HarnessSpec, func: &str) -> (String, String) {
// Heuristic: identifiers starting with lowercase that look
// like Python identifiers are kwargs; everything else is an
// env var.
- if name.chars().next().map(|c| c.is_ascii_lowercase()).unwrap_or(false) {
+ if name
+ .chars()
+ .next()
+ .map(|c| c.is_ascii_lowercase())
+ .unwrap_or(false)
+ {
let pre = String::new();
let call = format!("_entry_mod.{func}({name}=payload)");
(pre, call)
@@ -2505,8 +2511,8 @@ fn build_call(spec: &HarnessSpec, func: &str) -> (String, String) {
}
}
PayloadSlot::Stdin => {
- let pre = "import io\nsys.stdin = io.TextIOWrapper(io.BytesIO(_payload_raw))\n"
- .to_owned();
+ let pre =
+ "import io\nsys.stdin = io.TextIOWrapper(io.BytesIO(_payload_raw))\n".to_owned();
let call = format!("_entry_mod.{func}()");
(pre, call)
}
@@ -2534,7 +2540,12 @@ fn build_call_args(spec: &HarnessSpec) -> (String, String) {
(pre, args)
}
PayloadSlot::EnvVar(name) => {
- if name.chars().next().map(|c| c.is_ascii_lowercase()).unwrap_or(false) {
+ if name
+ .chars()
+ .next()
+ .map(|c| c.is_ascii_lowercase())
+ .unwrap_or(false)
+ {
(String::new(), format!("{name}=payload"))
} else {
let pre = format!("os.environ[{name:?}] = payload\n");
@@ -2542,8 +2553,8 @@ fn build_call_args(spec: &HarnessSpec) -> (String, String) {
}
}
PayloadSlot::Stdin => {
- let pre = "import io\nsys.stdin = io.TextIOWrapper(io.BytesIO(_payload_raw))\n"
- .to_owned();
+ let pre =
+ "import io\nsys.stdin = io.TextIOWrapper(io.BytesIO(_payload_raw))\n".to_owned();
(pre, String::new())
}
_ => (String::new(), "payload".to_owned()),
@@ -2625,7 +2636,11 @@ mod tests {
fn emit_env_var_slot_uppercase_sets_env() {
let spec = make_spec(PayloadSlot::EnvVar("USER_INPUT".into()));
let harness = emit(&spec).unwrap();
- assert!(harness.source.contains("os.environ[\"USER_INPUT\"] = payload"));
+ assert!(
+ harness
+ .source
+ .contains("os.environ[\"USER_INPUT\"] = payload")
+ );
assert!(harness.source.contains("login()"));
}
@@ -2687,7 +2702,8 @@ mod tests {
#[test]
fn shape_detect_fastapi() {
- let src = "from fastapi import FastAPI\napp = FastAPI()\n@app.get('/')\ndef index(): pass\n";
+ let src =
+ "from fastapi import FastAPI\napp = FastAPI()\n@app.get('/')\ndef index(): pass\n";
let spec = make_spec_with(EntryKind::HttpRoute, "index");
assert_eq!(PythonShape::detect(&spec, src), PythonShape::FastApiRoute);
}
@@ -2809,15 +2825,21 @@ mod tests {
#[test]
fn extra_files_flask_pins_flask() {
let extras = extra_files_for_shape(PythonShape::FlaskRoute);
- assert!(extras.iter().any(|(p, c)| p == "requirements.txt" && c.contains("Flask")));
+ assert!(
+ extras
+ .iter()
+ .any(|(p, c)| p == "requirements.txt" && c.contains("Flask"))
+ );
}
#[test]
fn extra_files_fastapi_pins_httpx() {
let extras = extra_files_for_shape(PythonShape::FastApiRoute);
- assert!(extras
- .iter()
- .any(|(p, c)| p == "requirements.txt" && c.contains("fastapi") && c.contains("httpx")));
+ assert!(
+ extras.iter().any(|(p, c)| p == "requirements.txt"
+ && c.contains("fastapi")
+ && c.contains("httpx"))
+ );
}
#[test]
@@ -2832,9 +2854,9 @@ mod tests {
#[test]
fn extra_files_starlette_pins_httpx() {
let extras = extra_files_for_shape(PythonShape::StarletteRoute);
- assert!(extras.iter().any(
- |(p, c)| p == "requirements.txt" && c.contains("starlette") && c.contains("httpx")
- ));
+ assert!(extras.iter().any(|(p, c)| p == "requirements.txt"
+ && c.contains("starlette")
+ && c.contains("httpx")));
}
fn make_spec_with(kind: EntryKind, name: &str) -> HarnessSpec {
diff --git a/src/dynamic/lang/ruby.rs b/src/dynamic/lang/ruby.rs
index 78da8456..3b844854 100644
--- a/src/dynamic/lang/ruby.rs
+++ b/src/dynamic/lang/ruby.rs
@@ -92,9 +92,7 @@ fn chain_step(
terminal: Option<&ChainStepTerminal>,
) -> ChainStepHarness {
let shim = probe_shim();
- let mut driver = String::from(
- "prev = ENV[\"NYX_PREV_OUTPUT\"] || \"\"\n$stdout.write(prev)\n",
- );
+ let mut driver = String::from("prev = ENV[\"NYX_PREV_OUTPUT\"] || \"\"\n$stdout.write(prev)\n");
if let Some(t) = terminal {
let callee = ruby_string_literal(&t.sink_callee);
let sentinel = ruby_string_literal(ChainStepHarness::SINK_HIT_SENTINEL);
@@ -211,7 +209,10 @@ pub fn detect_shape(spec: &HarnessSpec) -> RubyShape {
}
fn read_entry_source(entry_file: &str) -> String {
- let candidates = [PathBuf::from(entry_file), PathBuf::from(".").join(entry_file)];
+ let candidates = [
+ PathBuf::from(entry_file),
+ PathBuf::from(".").join(entry_file),
+ ];
for path in &candidates {
if let Ok(s) = std::fs::read_to_string(path) {
return s;
@@ -443,7 +444,10 @@ pub fn emit(spec: &HarnessSpec) -> Result {
// Phase 21 (Track M.3): ScheduledJob short-circuit (Sidekiq workers).
if let crate::evidence::EntryKind::ScheduledJob { schedule } = &spec.entry_kind {
- return Ok(emit_scheduled_job_harness(&spec.entry_name, schedule.as_deref()));
+ return Ok(emit_scheduled_job_harness(
+ &spec.entry_name,
+ schedule.as_deref(),
+ ));
}
// Phase 21 (Track M.3): WebSocket short-circuit (ActionCable channels).
@@ -1188,7 +1192,11 @@ fn generate_source(spec: &HarnessSpec, shape: RubyShape) -> String {
let pre_call = build_pre_call(spec);
let invocation = invoke_for_shape(spec, shape, entry_fn);
let shim = probe_shim();
- let crash_callee = if entry_fn.is_empty() { "main" } else { entry_fn.as_str() };
+ let crash_callee = if entry_fn.is_empty() {
+ "main"
+ } else {
+ entry_fn.as_str()
+ };
format!(
r#"# Nyx dynamic harness — auto-generated, do not edit (Phase 15 — RubyShape::{shape:?}).
@@ -1448,15 +1456,21 @@ mod tests {
#[test]
fn entry_kinds_supported_is_non_empty() {
assert!(!RubyEmitter.entry_kinds_supported().is_empty());
- assert!(RubyEmitter
- .entry_kinds_supported()
- .contains(&EntryKindTag::Function));
- assert!(RubyEmitter
- .entry_kinds_supported()
- .contains(&EntryKindTag::HttpRoute));
- assert!(RubyEmitter
- .entry_kinds_supported()
- .contains(&EntryKindTag::CliSubcommand));
+ assert!(
+ RubyEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::Function)
+ );
+ assert!(
+ RubyEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::HttpRoute)
+ );
+ assert!(
+ RubyEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::CliSubcommand)
+ );
}
#[test]
@@ -1576,8 +1590,14 @@ mod tests {
#[test]
fn parse_first_class_name_picks_up_class_decl() {
- assert_eq!(parse_first_class_name("class Foo\nend\n"), Some("Foo".to_owned()));
- assert_eq!(parse_first_class_name("class Bar < Base\nend\n"), Some("Bar".to_owned()));
+ assert_eq!(
+ parse_first_class_name("class Foo\nend\n"),
+ Some("Foo".to_owned())
+ );
+ assert_eq!(
+ parse_first_class_name("class Bar < Base\nend\n"),
+ Some("Bar".to_owned())
+ );
assert_eq!(parse_first_class_name("def foo\nend\n"), None);
}
@@ -1590,7 +1610,8 @@ mod tests {
"probe_shim banner missing from generated harness.rb — splicing regressed",
);
assert!(
- h.source.contains("def __nyx_install_crash_guard(sink_callee)"),
+ h.source
+ .contains("def __nyx_install_crash_guard(sink_callee)"),
"install_crash_guard definition missing from generated harness.rb",
);
assert!(
diff --git a/src/dynamic/lang/rust.rs b/src/dynamic/lang/rust.rs
index 60df449b..f9405bdd 100644
--- a/src/dynamic/lang/rust.rs
+++ b/src/dynamic/lang/rust.rs
@@ -793,7 +793,10 @@ fn main() {{
}
fn read_entry_source(entry_file: &str) -> String {
- let candidates = [PathBuf::from(entry_file), PathBuf::from(".").join(entry_file)];
+ let candidates = [
+ PathBuf::from(entry_file),
+ PathBuf::from(".").join(entry_file),
+ ];
for path in &candidates {
if let Ok(s) = std::fs::read_to_string(path) {
return s;
@@ -1079,29 +1082,28 @@ fn class_derives_default(entry_src: &str, class: &str) -> bool {
let window_start = decl_pos.saturating_sub(256);
let window = &entry_src[window_start..decl_pos];
if let Some(derive_pos) = window.rfind("#[derive(")
- && let Some(end_rel) = window[derive_pos..].find(")]") {
- let end = derive_pos + end_rel;
- let derive_list = &window[derive_pos + "#[derive(".len()..end];
- let between = &window[end + ")]".len()..];
- // The derive attribute must directly precede the
- // declaration — no other item / statement may sit
- // between `#[derive(...)]` and the `struct` /
- // `enum` token. Forbidden tokens (`;`, `{`, `}`,
- // `=`, or another item keyword) signal the derive
- // belongs to an earlier declaration.
- let between_clean = strip_attrs_and_comments(between);
- let forbidden = ['{', '}', ';', '='];
- let item_keyword = ["struct", "enum", "fn", "impl", "trait", "type", "mod"]
- .iter()
- .any(|kw| word_in_text(&between_clean, kw));
- let attaches_to_decl = !between_clean.chars().any(|c| forbidden.contains(&c))
- && !item_keyword;
- if attaches_to_decl
- && derive_list.split(',').any(|t| t.trim() == "Default")
- {
- return true;
- }
+ && let Some(end_rel) = window[derive_pos..].find(")]")
+ {
+ let end = derive_pos + end_rel;
+ let derive_list = &window[derive_pos + "#[derive(".len()..end];
+ let between = &window[end + ")]".len()..];
+ // The derive attribute must directly precede the
+ // declaration — no other item / statement may sit
+ // between `#[derive(...)]` and the `struct` /
+ // `enum` token. Forbidden tokens (`;`, `{`, `}`,
+ // `=`, or another item keyword) signal the derive
+ // belongs to an earlier declaration.
+ let between_clean = strip_attrs_and_comments(between);
+ let forbidden = ['{', '}', ';', '='];
+ let item_keyword = ["struct", "enum", "fn", "impl", "trait", "type", "mod"]
+ .iter()
+ .any(|kw| word_in_text(&between_clean, kw));
+ let attaches_to_decl =
+ !between_clean.chars().any(|c| forbidden.contains(&c)) && !item_keyword;
+ if attaches_to_decl && derive_list.split(',').any(|t| t.trim() == "Default") {
+ return true;
}
+ }
}
search_from = decl_pos + 1;
}
@@ -1143,8 +1145,7 @@ fn word_in_text(text: &str, kw: &str) -> bool {
let mut i = 0usize;
while i + kw_bytes.len() <= bytes.len() {
if &bytes[i..i + kw_bytes.len()] == kw_bytes {
- let before_ok = i == 0
- || !bytes[i - 1].is_ascii_alphanumeric() && bytes[i - 1] != b'_';
+ let before_ok = i == 0 || !bytes[i - 1].is_ascii_alphanumeric() && bytes[i - 1] != b'_';
let after_idx = i + kw_bytes.len();
let after_ok = after_idx >= bytes.len()
|| (!bytes[after_idx].is_ascii_alphanumeric() && bytes[after_idx] != b'_');
@@ -1319,15 +1320,10 @@ fn actix_invocation(spec: &HarnessSpec, func: &str) -> (String, String) {
format!(" std::env::set_var({name:?}, &payload);\n"),
format!("let _ = entry::{func}(\"\");"),
),
- PayloadSlot::HttpBody => (
- String::new(),
- format!("let _ = entry::{func}(&payload);"),
- ),
+ PayloadSlot::HttpBody => (String::new(), format!("let _ = entry::{func}(&payload);")),
PayloadSlot::QueryParam(name) => (
String::new(),
- format!(
- "let _ = entry::{func}(&format!(\"{name}={{}}\", payload));",
- ),
+ format!("let _ = entry::{func}(&format!(\"{name}={{}}\", payload));",),
),
_ => (String::new(), format!("let _ = entry::{func}(&payload);")),
}
@@ -1399,8 +1395,14 @@ mod tests {
let cargo = harness.extra_files.iter().find(|(n, _)| n == "Cargo.toml");
assert!(cargo.is_some(), "Cargo.toml must be in extra_files");
let cargo_content = &cargo.unwrap().1;
- assert!(cargo_content.contains("rusqlite"), "SQL_QUERY cap needs rusqlite dep");
- assert!(cargo_content.contains("bundled"), "rusqlite must use bundled feature");
+ assert!(
+ cargo_content.contains("rusqlite"),
+ "SQL_QUERY cap needs rusqlite dep"
+ );
+ assert!(
+ cargo_content.contains("bundled"),
+ "rusqlite must use bundled feature"
+ );
}
#[test]
@@ -1408,8 +1410,15 @@ mod tests {
let mut spec = make_spec(PayloadSlot::Param(0));
spec.expected_cap = Cap::CODE_EXEC;
let harness = emit(&spec).unwrap();
- let cargo = harness.extra_files.iter().find(|(n, _)| n == "Cargo.toml").unwrap();
- assert!(!cargo.1.contains("rusqlite"), "CODE_EXEC must not have rusqlite dep");
+ let cargo = harness
+ .extra_files
+ .iter()
+ .find(|(n, _)| n == "Cargo.toml")
+ .unwrap();
+ assert!(
+ !cargo.1.contains("rusqlite"),
+ "CODE_EXEC must not have rusqlite dep"
+ );
}
#[test]
@@ -1433,7 +1442,8 @@ mod tests {
#[test]
fn class_derives_default_matches_explicit_impl() {
- let src = "struct UserService;\nimpl Default for UserService { fn default() -> Self { Self } }";
+ let src =
+ "struct UserService;\nimpl Default for UserService { fn default() -> Self { Self } }";
assert!(class_derives_default(src, "UserService"));
}
@@ -1487,9 +1497,11 @@ mod tests {
#[test]
fn entry_kinds_supported_is_non_empty() {
assert!(!RustEmitter.entry_kinds_supported().is_empty());
- assert!(RustEmitter
- .entry_kinds_supported()
- .contains(&EntryKindTag::Function));
+ assert!(
+ RustEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::Function)
+ );
}
#[test]
@@ -1516,7 +1528,8 @@ mod tests {
// shape; the legacy [`RustShape::AxumHandler`] fires only on
// weak detectors (`IntoResponse` / `Json(` without `use
// axum::`).
- let src = "use axum::extract::Query; pub fn handler(payload: &str) -> String { String::new() }";
+ let src =
+ "use axum::extract::Query; pub fn handler(payload: &str) -> String { String::new() }";
let spec = make_spec_with(EntryKind::HttpRoute, "handler", "src/entry.rs");
assert_eq!(RustShape::detect(&spec, src), RustShape::AxumRoute);
}
diff --git a/src/dynamic/lang/typescript.rs b/src/dynamic/lang/typescript.rs
index 26535ca1..b551137d 100644
--- a/src/dynamic/lang/typescript.rs
+++ b/src/dynamic/lang/typescript.rs
@@ -15,7 +15,9 @@
//! runtime ignores.
use crate::dynamic::environment::{Environment, RuntimeArtifacts};
-use crate::dynamic::lang::{js_shared, ChainStepHarness, ChainStepTerminal, HarnessSource, LangEmitter};
+use crate::dynamic::lang::{
+ ChainStepHarness, ChainStepTerminal, HarnessSource, LangEmitter, js_shared,
+};
use crate::dynamic::spec::{EntryKindTag, HarnessSpec};
use crate::evidence::UnsupportedReason;
@@ -87,9 +89,11 @@ mod tests {
#[test]
fn entry_kinds_supported_is_non_empty_and_includes_http_route() {
assert!(!TypeScriptEmitter.entry_kinds_supported().is_empty());
- assert!(TypeScriptEmitter
- .entry_kinds_supported()
- .contains(&EntryKindTag::HttpRoute));
+ assert!(
+ TypeScriptEmitter
+ .entry_kinds_supported()
+ .contains(&EntryKindTag::HttpRoute)
+ );
}
#[test]
@@ -101,7 +105,9 @@ mod tests {
#[test]
fn typescript_emit_stages_entry_at_entry_js_for_node_resolution() {
- let h = TypeScriptEmitter.emit(&make_spec(EntryKind::Function)).unwrap();
+ let h = TypeScriptEmitter
+ .emit(&make_spec(EntryKind::Function))
+ .unwrap();
// TS fixtures use ES-compatible syntax; the workdir layout matches
// JavaScript so Node's CJS `require('./entry')` resolves without an
// extension-loader hook. See js_shared::entry_subpath_for_shape.
diff --git a/src/dynamic/mod.rs b/src/dynamic/mod.rs
index e8149121..e779783e 100644
--- a/src/dynamic/mod.rs
+++ b/src/dynamic/mod.rs
@@ -78,8 +78,8 @@ pub mod oracle;
pub mod policy;
pub mod probe;
pub mod rand;
-pub mod repro;
pub mod report;
+pub mod repro;
pub mod runner;
pub mod sandbox;
pub mod spec;
@@ -91,4 +91,4 @@ pub mod verify;
pub use report::{VerifyResult, VerifyStatus};
pub use spec::HarnessSpec;
-pub use verify::{verify_finding, VerifyOptions};
+pub use verify::{VerifyOptions, verify_finding};
diff --git a/src/dynamic/mount_filter.rs b/src/dynamic/mount_filter.rs
index 83d71bc6..8e55a9b6 100644
--- a/src/dynamic/mount_filter.rs
+++ b/src/dynamic/mount_filter.rs
@@ -53,7 +53,10 @@ fn scan_dir_recursive(project_root: &Path, dir: &Path, notes: &mut Vec bool {
- matches!(name, ".git" | "node_modules" | "__pycache__" | ".tox" | "venv" | ".venv")
+ matches!(
+ name,
+ ".git" | "node_modules" | "__pycache__" | ".tox" | "venv" | ".venv"
+ )
}
fn matches_dir_pattern(name: &str) -> Option<&'static str> {
@@ -128,9 +131,17 @@ mod tests {
#[test]
fn detects_pem_file() {
let dir = TempDir::new().unwrap();
- fs::write(dir.path().join("server.pem"), "-----BEGIN CERTIFICATE-----\n").unwrap();
+ fs::write(
+ dir.path().join("server.pem"),
+ "-----BEGIN CERTIFICATE-----\n",
+ )
+ .unwrap();
let notes = scan_sensitive_files(dir.path());
- assert!(notes.iter().any(|n| n.path.ends_with(".pem") || n.path.contains("server.pem")));
+ assert!(
+ notes
+ .iter()
+ .any(|n| n.path.ends_with(".pem") || n.path.contains("server.pem"))
+ );
}
#[test]
@@ -146,6 +157,9 @@ mod tests {
let dir = TempDir::new().unwrap();
fs::write(dir.path().join("main.py"), "print('hi')\n").unwrap();
let notes = scan_sensitive_files(dir.path());
- assert!(notes.is_empty(), "clean dir should produce no notes: {notes:?}");
+ assert!(
+ notes.is_empty(),
+ "clean dir should produce no notes: {notes:?}"
+ );
}
}
diff --git a/src/dynamic/oob.rs b/src/dynamic/oob.rs
index 49ad97f5..15eb3b92 100644
--- a/src/dynamic/oob.rs
+++ b/src/dynamic/oob.rs
@@ -63,7 +63,11 @@ impl OobListener {
accept_loop(listener, hits_clone, shutdown_clone);
});
- Ok(Self { port, hits, shutdown })
+ Ok(Self {
+ port,
+ hits,
+ shutdown,
+ })
}
/// Port the listener is bound to.
@@ -86,10 +90,7 @@ impl OobListener {
/// Returns `true` if `nonce` was received by the listener.
pub fn was_nonce_hit(&self, nonce: &str) -> bool {
- self.hits
- .lock()
- .map(|h| h.contains(nonce))
- .unwrap_or(false)
+ self.hits.lock().map(|h| h.contains(nonce)).unwrap_or(false)
}
/// Polls until `nonce` is recorded or `timeout` elapses.
@@ -144,9 +145,10 @@ fn handle_connection(stream: TcpStream, hits: Arc>>) {
let mut first_line = String::new();
if reader.read_line(&mut first_line).is_ok()
&& let Some(nonce) = parse_nonce_from_request_line(&first_line)
- && let Ok(mut h) = hits.lock() {
- h.insert(nonce);
- }
+ && let Ok(mut h) = hits.lock()
+ {
+ h.insert(nonce);
+ }
// Drain remaining headers so the client doesn't get ECONNRESET.
loop {
let mut line = String::new();
@@ -158,7 +160,8 @@ fn handle_connection(stream: TcpStream, hits: Arc>>) {
}
}
let mut w = &stream;
- let _ = w.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\nContent-Type: text/plain\r\n\r\nok");
+ let _ =
+ w.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\nContent-Type: text/plain\r\n\r\nok");
}
/// Extract the nonce from a `GET /{nonce} HTTP/1.1` request line.
diff --git a/src/dynamic/oracle.rs b/src/dynamic/oracle.rs
index e811b97e..1d0bca98 100644
--- a/src/dynamic/oracle.rs
+++ b/src/dynamic/oracle.rs
@@ -397,7 +397,9 @@ pub enum Oracle {
/// declaration `const`-friendly (Phase 06 deferred the
/// `Vec` shape the plan listed because the corpus is
/// declared in static memory; a `Vec` would require runtime init).
- SinkProbe { predicates: &'static [ProbePredicate] },
+ SinkProbe {
+ predicates: &'static [ProbePredicate],
+ },
/// Phase 08 sink-site crash oracle. Fires iff at least one drained
/// probe has [`ProbeKind::Crash { signal }`] with `signal ∈ signals`.
/// A process-level abort that did not reach the sink handler leaves no
@@ -584,9 +586,7 @@ pub fn oracle_fired_with_stubs(
return false;
}
let idor_ok = cross.iter().all(|p| match p {
- ProbePredicate::IdorBoundaryCrossed => {
- probes_satisfy_idor_crossed(probes)
- }
+ ProbePredicate::IdorBoundaryCrossed => probes_satisfy_idor_crossed(probes),
_ => true,
});
if !idor_ok {
@@ -745,12 +745,15 @@ fn stdout_template_equals(stdout: &[u8], expected: u64) -> bool {
}
let parsed: serde_json::Result = serde_json::from_str(trimmed);
let Ok(v) = parsed else { continue };
- let Some(render) = v.get("render") else { continue };
+ let Some(render) = v.get("render") else {
+ continue;
+ };
let Some(s) = render.as_str() else { continue };
if let Ok(n) = s.trim().parse::()
- && n == expected {
- return true;
- }
+ && n == expected
+ {
+ return true;
+ }
}
false
}
@@ -759,9 +762,9 @@ fn stdout_template_equals(stdout: &[u8], expected: u64) -> bool {
/// [`ProbeKind::Deserialize`] record matching `require_invoked`.
fn probes_satisfy_deserialize(probes: &[SinkProbe], require_invoked: bool) -> bool {
probes.iter().any(|p| match &p.kind {
- ProbeKind::Deserialize { gadget_chain_invoked } => {
- *gadget_chain_invoked == require_invoked
- }
+ ProbeKind::Deserialize {
+ gadget_chain_invoked,
+ } => *gadget_chain_invoked == require_invoked,
_ => false,
})
}
@@ -795,8 +798,7 @@ fn probes_satisfy_count_gt(probes: &[SinkProbe], n: u32) -> bool {
fn probes_satisfy_header_injected(probes: &[SinkProbe], header_name: &str) -> bool {
probes.iter().any(|p| match &p.kind {
ProbeKind::HeaderEmit { name, value } => {
- (header_name == "*" || name.eq_ignore_ascii_case(header_name))
- && value.contains("\r\n")
+ (header_name == "*" || name.eq_ignore_ascii_case(header_name)) && value.contains("\r\n")
}
_ => false,
})
@@ -813,9 +815,10 @@ fn probes_satisfy_header_injected(probes: &[SinkProbe], header_name: &str) -> bo
/// `//host/...` references are parsed as off-origin.
fn probes_satisfy_redirect_off_origin(probes: &[SinkProbe], allowlist: &[&str]) -> bool {
probes.iter().any(|p| match &p.kind {
- ProbeKind::Redirect { location, request_host } => {
- redirect_is_off_origin(location, request_host, allowlist)
- }
+ ProbeKind::Redirect {
+ location,
+ request_host,
+ } => redirect_is_off_origin(location, request_host, allowlist),
_ => false,
})
}
@@ -861,7 +864,10 @@ fn probes_satisfy_weak_key(probes: &[SinkProbe], max_bits: u32) -> bool {
/// [`ProbePredicate::IdorBoundaryCrossed`] (Phase 11 — Track J.9).
fn probes_satisfy_idor_crossed(probes: &[SinkProbe]) -> bool {
probes.iter().any(|p| match &p.kind {
- ProbeKind::IdorAccess { caller_id, owner_id } => caller_id != owner_id,
+ ProbeKind::IdorAccess {
+ caller_id,
+ owner_id,
+ } => caller_id != owner_id,
_ => false,
})
}
@@ -877,9 +883,7 @@ fn probes_satisfy_outbound_off_list(probes: &[SinkProbe], allowlist: &[&str]) ->
if h.is_empty() {
return false;
}
- !allowlist
- .iter()
- .any(|a| h == a.trim().to_ascii_lowercase())
+ !allowlist.iter().any(|a| h == a.trim().to_ascii_lowercase())
}
_ => false,
})
@@ -899,9 +903,7 @@ pub(crate) fn redirect_is_off_origin(
return false;
};
let host_lower = host.to_ascii_lowercase();
- if !request_host.is_empty()
- && host_lower == request_host.trim().to_ascii_lowercase()
- {
+ if !request_host.is_empty() && host_lower == request_host.trim().to_ascii_lowercase() {
return false;
}
!allowlist
@@ -929,14 +931,15 @@ fn extract_redirect_host(location: &str) -> Option {
return None;
};
// Strip path / query / fragment from the host segment.
- let end = rest
- .find(['/', '?', '#'])
- .unwrap_or(rest.len());
+ let end = rest.find(['/', '?', '#']).unwrap_or(rest.len());
let authority = &rest[..end];
// Strip userinfo + port. Bracketed IPv6 authorities (`[::1]` or
// `[::1]:8080`) must keep the brackets together — splitting on the
// last `:` inside the literal would slice the address apart.
- let after_userinfo = authority.rsplit_once('@').map(|(_, h)| h).unwrap_or(authority);
+ let after_userinfo = authority
+ .rsplit_once('@')
+ .map(|(_, h)| h)
+ .unwrap_or(authority);
let host_only = if let Some(rest) = after_userinfo.strip_prefix('[') {
match rest.find(']') {
Some(end) => &after_userinfo[..end + 2],
@@ -1077,7 +1080,10 @@ mod tests {
let oracle = Oracle::SinkProbe {
predicates: &[
ProbePredicate::CalleeEquals("os.system"),
- ProbePredicate::ArgContains { index: 0, needle: "; echo" },
+ ProbePredicate::ArgContains {
+ index: 0,
+ needle: "; echo",
+ },
],
};
let probes = vec![probe(
@@ -1100,13 +1106,13 @@ mod tests {
let oracle = Oracle::SinkProbe {
predicates: &[
ProbePredicate::CalleeEquals("os.system"),
- ProbePredicate::ArgContains { index: 0, needle: "NEVER_PRESENT" },
+ ProbePredicate::ArgContains {
+ index: 0,
+ needle: "NEVER_PRESENT",
+ },
],
};
- let probes = vec![probe(
- "os.system",
- vec![ProbeArg::String("hello".into())],
- )];
+ let probes = vec![probe("os.system", vec![ProbeArg::String("hello".into())])];
assert!(!oracle_fired(&oracle, &outcome(), &probes));
}
@@ -1158,7 +1164,10 @@ mod tests {
#[test]
fn arg_equals_predicate() {
let oracle = Oracle::SinkProbe {
- predicates: &[ProbePredicate::ArgEquals { index: 0, value: "exact" }],
+ predicates: &[ProbePredicate::ArgEquals {
+ index: 0,
+ value: "exact",
+ }],
};
let hit = vec![probe("f", vec![ProbeArg::String("exact".into())])];
let miss = vec![probe("f", vec![ProbeArg::String("inexact".into())])];
@@ -1306,7 +1315,10 @@ mod tests {
allowlist: &["example.com", "cdn.example.com"],
}],
};
- let probes = vec![redirect_probe("https://cdn.example.com/asset", "example.com")];
+ let probes = vec![redirect_probe(
+ "https://cdn.example.com/asset",
+ "example.com",
+ )];
assert!(!oracle_fired(&oracle, &outcome(), &probes));
}
@@ -1315,7 +1327,10 @@ mod tests {
let oracle = Oracle::SinkProbe {
predicates: &[ProbePredicate::RedirectHostNotIn { allowlist: &[] }],
};
- let probes = vec![redirect_probe("https://example.com/dashboard", "example.com")];
+ let probes = vec![redirect_probe(
+ "https://example.com/dashboard",
+ "example.com",
+ )];
assert!(!oracle_fired(&oracle, &outcome(), &probes));
}
diff --git a/src/dynamic/policy.rs b/src/dynamic/policy.rs
index 7d653b2e..cfa09c94 100644
--- a/src/dynamic/policy.rs
+++ b/src/dynamic/policy.rs
@@ -211,7 +211,9 @@ impl Scrubber {
return true;
}
let lower = text.to_ascii_lowercase();
- PII_LITERAL_SUBSTRINGS.iter().any(|needle| lower.contains(*needle))
+ PII_LITERAL_SUBSTRINGS
+ .iter()
+ .any(|needle| lower.contains(*needle))
}
/// Scrub `text`, returning a new `String` whose value is either the
@@ -572,7 +574,10 @@ mod tests {
#[test]
fn truncate_at_exact_boundary_unchanged() {
let bytes = vec![0u8; PAYLOAD_CAPTURE_LIMIT_BYTES];
- assert_eq!(truncate_payload_bytes(&bytes).len(), PAYLOAD_CAPTURE_LIMIT_BYTES);
+ assert_eq!(
+ truncate_payload_bytes(&bytes).len(),
+ PAYLOAD_CAPTURE_LIMIT_BYTES
+ );
}
#[test]
diff --git a/src/dynamic/probe.rs b/src/dynamic/probe.rs
index 1dc519bd..b493d456 100644
--- a/src/dynamic/probe.rs
+++ b/src/dynamic/probe.rs
@@ -307,7 +307,6 @@ pub enum ProbeKind {
},
}
-
/// Bounded forensic snapshot captured alongside a [`SinkProbe`]
/// (Phase 08 — Track C.5).
///
@@ -515,9 +514,8 @@ impl ProbeChannel {
.append(true)
.create(true)
.open(&self.path)?;
- let line = serde_json::to_string(probe).map_err(|e| {
- std::io::Error::new(std::io::ErrorKind::InvalidData, e)
- })?;
+ let line = serde_json::to_string(probe)
+ .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
file.write_all(line.as_bytes())?;
file.write_all(b"\n")?;
Ok(())
@@ -611,13 +609,17 @@ mod tests {
let dir = TempDir::new().unwrap();
let ch = ProbeChannel::for_workdir(dir.path()).unwrap();
let mut p = sample_probe("crash-test");
- p.kind = ProbeKind::Crash { signal: Signal::Sigsegv };
+ p.kind = ProbeKind::Crash {
+ signal: Signal::Sigsegv,
+ };
ch.write(&p).unwrap();
let drained = ch.drain();
assert_eq!(drained.len(), 1);
assert!(matches!(
drained[0].kind,
- ProbeKind::Crash { signal: Signal::Sigsegv }
+ ProbeKind::Crash {
+ signal: Signal::Sigsegv
+ }
));
}
@@ -660,7 +662,9 @@ mod tests {
assert_eq!(w.payload_bytes.len(), policy::PAYLOAD_CAPTURE_LIMIT_BYTES);
assert_eq!(w.env_snapshot.get("PATH").map(String::as_str), Some("/bin"));
assert_eq!(
- w.env_snapshot.get("AWS_SECRET_ACCESS_KEY").map(String::as_str),
+ w.env_snapshot
+ .get("AWS_SECRET_ACCESS_KEY")
+ .map(String::as_str),
Some(policy::REDACTED_VALUE)
);
assert_eq!(w.args_repr, vec!["ls; whoami".to_owned()]);
diff --git a/src/dynamic/repro.rs b/src/dynamic/repro.rs
index 0e4192e0..94b12ce8 100644
--- a/src/dynamic/repro.rs
+++ b/src/dynamic/repro.rs
@@ -136,7 +136,10 @@ pub fn write(
fs::create_dir_all(&src_dir)?;
// Also write Cargo.toml for Rust repro bundles.
let cargo_content = crate::dynamic::lang::rust::generate_cargo_toml(spec.expected_cap);
- fs::write(root.join("harness").join("Cargo.toml"), cargo_content.as_bytes())?;
+ fs::write(
+ root.join("harness").join("Cargo.toml"),
+ cargo_content.as_bytes(),
+ )?;
src_dir.join("main.rs")
} else {
root.join("harness").join(format!("harness.{ext}"))
@@ -145,7 +148,10 @@ pub fn write(
// harness/Dockerfile.harness
let dockerfile = dockerfile_for_spec(spec);
- fs::write(root.join("harness").join("Dockerfile.harness"), dockerfile.as_bytes())?;
+ fs::write(
+ root.join("harness").join("Dockerfile.harness"),
+ dockerfile.as_bytes(),
+ )?;
// payload/payload.bin + payload.meta.json
fs::write(root.join("payload").join("payload.bin"), payload_bytes)?;
@@ -154,7 +160,10 @@ pub fn write(
"len": payload_bytes.len(),
"encoding": "raw",
});
- write_json(&root.join("payload").join("payload.meta.json"), &payload_meta)?;
+ write_json(
+ &root.join("payload").join("payload.meta.json"),
+ &payload_meta,
+ )?;
// sandbox/options.json
let sandbox_opts = serde_json::json!({
@@ -166,7 +175,10 @@ pub fn write(
// sandbox/env.allowlist.json
let env_list: Vec<&str> = opts.env_passthrough.iter().map(|s| s.as_str()).collect();
- write_json(&root.join("sandbox").join("env.allowlist.json"), &serde_json::json!(env_list))?;
+ write_json(
+ &root.join("sandbox").join("env.allowlist.json"),
+ &serde_json::json!(env_list),
+ )?;
// expected/outcome.json — redacted
let redacted_stdout = redact::redact(&outcome.stdout);
@@ -235,7 +247,10 @@ pub fn write(
// Per-project symlink (§12 Q1)
let symlink = if let Some(proj_root) = project_root {
- let link_dir = proj_root.join(".nyx").join("dynamic-cache").join("symlinks");
+ let link_dir = proj_root
+ .join(".nyx")
+ .join("dynamic-cache")
+ .join("symlinks");
let _ = fs::create_dir_all(&link_dir);
let link_path = link_dir.join(&spec.spec_hash);
let _ = create_symlink(&root, &link_path);
@@ -252,11 +267,12 @@ fn repro_root(spec_hash: &str) -> Result {
let base = if let Ok(p) = std::env::var("NYX_REPRO_BASE") {
PathBuf::from(p)
} else {
- let dirs = ProjectDirs::from("", "", "nyx")
- .ok_or_else(|| ReproError::Io(std::io::Error::new(
+ let dirs = ProjectDirs::from("", "", "nyx").ok_or_else(|| {
+ ReproError::Io(std::io::Error::new(
std::io::ErrorKind::NotFound,
"cannot determine cache dir",
- )))?;
+ ))
+ })?;
dirs.cache_dir().join("dynamic").join("repro")
};
@@ -328,7 +344,10 @@ fn resolve_dockerfile_from(spec: &HarnessSpec) -> String {
format!("rust:{toolchain}-slim")
}
Lang::Python => {
- format!("python:{}", spec.toolchain_id.strip_prefix("python-").unwrap_or("3"))
+ format!(
+ "python:{}",
+ spec.toolchain_id.strip_prefix("python-").unwrap_or("3")
+ )
}
_ => "ubuntu:latest".to_owned(),
}
@@ -391,7 +410,10 @@ fn reproduce_script(spec: &HarnessSpec, payload_label: &str) -> String {
// `reproduce.sh --docker` which sources the runtime from the pinned
// image and bypasses the host toolchain entirely.
let host_probe_cmd = match spec.lang {
- Lang::Rust | Lang::Go | Lang::C | Lang::Cpp => "./harness/nyx_harness --help >/dev/null 2>&1 || test -x ./harness/nyx_harness".to_owned(),
+ Lang::Rust | Lang::Go | Lang::C | Lang::Cpp => {
+ "./harness/nyx_harness --help >/dev/null 2>&1 || test -x ./harness/nyx_harness"
+ .to_owned()
+ }
Lang::Python => "command -v python3".to_owned(),
Lang::JavaScript | Lang::TypeScript => "command -v node".to_owned(),
Lang::Java => "command -v java".to_owned(),
@@ -510,7 +532,10 @@ fn build_toolchain_lock(spec: &HarnessSpec, root: &Path) -> Result Result Option {
///
/// Callers who want "did this bundle replay green?" semantics get a typed
/// result instead of parsing shell output.
-pub fn replay_bundle(
- bundle_root: &Path,
- extra_args: &[&str],
-) -> ReplayResult {
+pub fn replay_bundle(bundle_root: &Path, extra_args: &[&str]) -> ReplayResult {
use std::process::Command;
let script = bundle_root.join("reproduce.sh");
if !script.exists() {
@@ -779,9 +804,17 @@ mod tests {
let outcome = make_outcome();
let verdict = make_verdict();
let artifact = write(
- &spec, &opts, &outcome, &verdict,
- "# harness", "# entry", b"payload", "label", None,
- ).unwrap();
+ &spec,
+ &opts,
+ &outcome,
+ &verdict,
+ "# harness",
+ "# entry",
+ b"payload",
+ "label",
+ None,
+ )
+ .unwrap();
let lock_path = artifact.root.join("toolchain.lock");
assert!(lock_path.exists(), "toolchain.lock missing");
let lock: serde_json::Value =
@@ -848,9 +881,17 @@ mod tests {
let dir = TempDir::new().unwrap();
unsafe { std::env::set_var("NYX_REPRO_BASE", dir.path().to_str().unwrap()) };
let artifact = write(
- &make_spec(), &SandboxOptions::default(), &make_outcome(), &make_verdict(),
- "# harness", "# entry", b"payload", "label", None,
- ).unwrap();
+ &make_spec(),
+ &SandboxOptions::default(),
+ &make_outcome(),
+ &make_verdict(),
+ "# harness",
+ "# entry",
+ b"payload",
+ "label",
+ None,
+ )
+ .unwrap();
let script = std::fs::read_to_string(artifact.root.join("reproduce.sh")).unwrap();
// Exit code 3 documented + emitted on host toolchain mismatch.
assert!(script.contains("EXPECTED_TOOLCHAIN=\"python-3.11\""));
@@ -872,7 +913,8 @@ mod tests {
std::fs::set_permissions(
bundle.join("reproduce.sh"),
std::fs::Permissions::from_mode(0o755),
- ).unwrap();
+ )
+ .unwrap();
}
assert_eq!(replay_bundle(&bundle, &[]), ReplayResult::Pass);
}
@@ -891,14 +933,16 @@ mod tests {
std::fs::write(
bundle.join("reproduce.sh"),
format!("#!/bin/sh\nexit {code}\n"),
- ).unwrap();
+ )
+ .unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(
bundle.join("reproduce.sh"),
std::fs::Permissions::from_mode(0o755),
- ).unwrap();
+ )
+ .unwrap();
}
assert_eq!(replay_bundle(&bundle, &[]), *expected);
}
@@ -961,9 +1005,17 @@ mod tests {
let outcome = make_outcome();
let verdict = make_verdict();
let artifact = write(
- &spec, &opts, &outcome, &verdict,
- "# harness", "# entry", b"payload", "label", None,
- ).unwrap();
+ &spec,
+ &opts,
+ &outcome,
+ &verdict,
+ "# harness",
+ "# entry",
+ b"payload",
+ "label",
+ None,
+ )
+ .unwrap();
let resolved = bundle_root_for(&spec.spec_hash).unwrap();
assert_eq!(resolved, artifact.root);
unsafe { std::env::remove_var("NYX_REPRO_BASE") };
@@ -982,12 +1034,24 @@ mod tests {
let verdict = make_verdict();
let artifact = write(
- &spec, &opts, &outcome, &verdict,
- "# harness", "# entry", b"payload", "label", None,
- ).unwrap();
+ &spec,
+ &opts,
+ &outcome,
+ &verdict,
+ "# harness",
+ "# entry",
+ b"payload",
+ "label",
+ None,
+ )
+ .unwrap();
- let outcome_json = std::fs::read_to_string(artifact.root.join("expected/outcome.json")).unwrap();
- assert!(!outcome_json.contains("AKIAFAKETEST00000000"), "AWS key must be redacted in outcome.json");
+ let outcome_json =
+ std::fs::read_to_string(artifact.root.join("expected/outcome.json")).unwrap();
+ assert!(
+ !outcome_json.contains("AKIAFAKETEST00000000"),
+ "AWS key must be redacted in outcome.json"
+ );
unsafe { std::env::remove_var("NYX_REPRO_BASE") };
}
diff --git a/src/dynamic/runner.rs b/src/dynamic/runner.rs
index 023323cf..faa51a33 100644
--- a/src/dynamic/runner.rs
+++ b/src/dynamic/runner.rs
@@ -7,16 +7,16 @@
use crate::dynamic::build_sandbox;
use crate::dynamic::corpus::{
- materialise_bytes, payloads_for, payloads_for_lang, resolve_benign_control,
- resolve_benign_control_lang, Payload,
+ Payload, materialise_bytes, payloads_for, payloads_for_lang, resolve_benign_control,
+ resolve_benign_control_lang,
};
use crate::dynamic::differential;
use crate::dynamic::harness::{self, HarnessError};
-use crate::dynamic::oracle::{oracle_fired_with_stubs, probe_crash_signal, Oracle};
+use crate::dynamic::oracle::{Oracle, oracle_fired_with_stubs, probe_crash_signal};
use crate::dynamic::probe::{ProbeChannel, SinkProbe};
-use crate::dynamic::stubs::StubEvent;
use crate::dynamic::sandbox::{self, SandboxBackend, SandboxError, SandboxOptions, SandboxOutcome};
use crate::dynamic::spec::HarnessSpec;
+use crate::dynamic::stubs::StubEvent;
use crate::dynamic::trace::{TraceStage, VerifyTrace};
use crate::evidence::{DifferentialOutcome, DifferentialVerdict};
use crate::symbol::Lang;
@@ -105,10 +105,17 @@ pub enum RunError {
/// at the verify boundary so unsupported-budget accounting
/// distinguishes "no oracle exists" from "no payloads carved
/// yet".
- SoundOracleUnavailable { cap: crate::labels::Cap, lang: Lang, hint: String },
+ SoundOracleUnavailable {
+ cap: crate::labels::Cap,
+ lang: Lang,
+ hint: String,
+ },
Harness(HarnessError),
Sandbox(SandboxError),
- BuildFailed { stderr: String, attempts: u32 },
+ BuildFailed {
+ stderr: String,
+ attempts: u32,
+ },
}
impl From for RunError {
@@ -198,12 +205,13 @@ pub fn run_spec(spec: &HarnessSpec, opts: &SandboxOptions) -> Result {
if let Some(cmd0) = harness.command.first_mut()
- && (cmd0 == "python3" || cmd0 == "python") {
- let venv_python = build_result.venv_path.join("bin").join("python3");
- if venv_python.exists() {
- *cmd0 = venv_python.to_string_lossy().into_owned();
- }
+ && (cmd0 == "python3" || cmd0 == "python")
+ {
+ let venv_python = build_result.venv_path.join("bin").join("python3");
+ if venv_python.exists() {
+ *cmd0 = venv_python.to_string_lossy().into_owned();
}
+ }
}
Err(build_sandbox::BuildError::BuildFailed { stderr, attempts }) => {
return Err(RunError::BuildFailed { stderr, attempts });
@@ -221,17 +229,18 @@ pub fn run_spec(spec: &HarnessSpec, opts: &SandboxOptions) -> Result {
- return Err(RunError::BuildFailed {
- stderr,
- attempts,
- });
+ return Err(RunError::BuildFailed { stderr, attempts });
}
Err(_) => {
// Io: fall back to whatever command was set (will likely fail at exec).
@@ -240,7 +249,9 @@ pub fn run_spec(spec: &HarnessSpec, opts: &SandboxOptions) -> Result {
// npm install for dependency resolution (no deps in basic fixtures).
- if let Err(build_sandbox::BuildError::BuildFailed { stderr, attempts }) = build_sandbox::prepare_node(spec, &harness.workdir) {
+ if let Err(build_sandbox::BuildError::BuildFailed { stderr, attempts }) =
+ build_sandbox::prepare_node(spec, &harness.workdir)
+ {
return Err(RunError::BuildFailed { stderr, attempts });
}
}
@@ -284,7 +295,9 @@ pub fn run_spec(spec: &HarnessSpec, opts: &SandboxOptions) -> Result {
// composer install if composer.json is present.
- if let Err(build_sandbox::BuildError::BuildFailed { stderr, attempts }) = build_sandbox::prepare_php(spec, &harness.workdir) {
+ if let Err(build_sandbox::BuildError::BuildFailed { stderr, attempts }) =
+ build_sandbox::prepare_php(spec, &harness.workdir)
+ {
return Err(RunError::BuildFailed { stderr, attempts });
}
}
@@ -352,9 +365,10 @@ pub fn run_spec(spec: &HarnessSpec, opts: &SandboxOptions) -> Result> = effective_opts.probe_channel.clone();
// Run only vuln (non-benign) payloads in the main loop.
@@ -435,12 +449,8 @@ pub fn run_spec(spec: &HarnessSpec, opts: &SandboxOptions) -> Result Result = probe_channel
.as_ref()
.map(|ch| ch.drain())
@@ -605,7 +614,6 @@ fn uses_docker_backend(opts: &SandboxOptions) -> bool {
}
}
-
/// Generate a random 16-character hex nonce for OOB callback tracking.
fn generate_nonce() -> String {
use std::time::{SystemTime, UNIX_EPOCH};
@@ -629,7 +637,10 @@ mod tests {
fn generate_nonce_is_16_hex_chars() {
let n = generate_nonce();
assert_eq!(n.len(), 16);
- assert!(n.chars().all(|c| c.is_ascii_hexdigit()), "nonce must be hex: {n}");
+ assert!(
+ n.chars().all(|c| c.is_ascii_hexdigit()),
+ "nonce must be hex: {n}"
+ );
}
#[test]
diff --git a/src/dynamic/sandbox/docker.rs b/src/dynamic/sandbox/docker.rs
index 6fbb51bf..1fc31994 100644
--- a/src/dynamic/sandbox/docker.rs
+++ b/src/dynamic/sandbox/docker.rs
@@ -90,7 +90,11 @@ pub fn ensure_image_pulled(image: &str) -> bool {
// succeeds we can skip the network pull entirely. When it fails we fall
// through to `docker pull` so registry-side rotations / first-time runs
// still settle.
- let ok = if docker_image_present(image) { true } else { docker_pull(image) };
+ let ok = if docker_image_present(image) {
+ true
+ } else {
+ docker_pull(image)
+ };
cache.insert(image.to_owned(), ok);
ok
}
@@ -249,7 +253,10 @@ mod tests {
.expect("oob listener must bind on 127.0.0.1 in tests"),
);
let args = network_args(&NetworkPolicy::OobOutbound { listener });
- assert!(args.iter().any(|a| a == "--add-host=host-gateway:host-gateway"));
+ assert!(
+ args.iter()
+ .any(|a| a == "--add-host=host-gateway:host-gateway")
+ );
}
#[test]
@@ -261,8 +268,8 @@ mod tests {
fn image_reference_for_toolchain_known_returns_pinned_digest() {
// The catalogue ships with hand-seeded sha256 digests for every
// catalogue entry, so known IDs resolve to `@sha256:…` refs.
- let r = image_reference_for_toolchain("python-3.11")
- .expect("python-3.11 is in the catalogue");
+ let r =
+ image_reference_for_toolchain("python-3.11").expect("python-3.11 is in the catalogue");
assert!(r.starts_with("python:3.11-slim@sha256:"), "got {r}");
}
diff --git a/src/dynamic/sandbox/firecracker.rs b/src/dynamic/sandbox/firecracker.rs
index 8b1b381b..07999dad 100644
--- a/src/dynamic/sandbox/firecracker.rs
+++ b/src/dynamic/sandbox/firecracker.rs
@@ -77,11 +77,15 @@ pub fn run(
_opts: &SandboxOptions,
) -> Result {
if !firecracker_available() {
- return Err(SandboxError::BackendUnavailable(SandboxBackend::Firecracker));
+ return Err(SandboxError::BackendUnavailable(
+ SandboxBackend::Firecracker,
+ ));
}
// Binary present but no VM logic yet. Surface BackendUnavailable
// explicitly so callers do not mistakenly think the run succeeded.
- Err(SandboxError::BackendUnavailable(SandboxBackend::Firecracker))
+ Err(SandboxError::BackendUnavailable(
+ SandboxBackend::Firecracker,
+ ))
}
#[cfg(test)]
@@ -122,7 +126,9 @@ mod tests {
let result = run(&harness, b"", &opts);
assert!(matches!(
result,
- Err(SandboxError::BackendUnavailable(SandboxBackend::Firecracker))
+ Err(SandboxError::BackendUnavailable(
+ SandboxBackend::Firecracker
+ ))
));
}
}
diff --git a/src/dynamic/sandbox/mod.rs b/src/dynamic/sandbox/mod.rs
index 07426ff4..3637e7f7 100644
--- a/src/dynamic/sandbox/mod.rs
+++ b/src/dynamic/sandbox/mod.rs
@@ -24,7 +24,7 @@
use crate::dynamic::harness::BuiltHarness;
use crate::dynamic::oob::OobListener;
-use crate::dynamic::probe::{ProbeChannel, PROBE_PATH_ENV};
+use crate::dynamic::probe::{PROBE_PATH_ENV, ProbeChannel};
use std::path::{Path, PathBuf};
use std::sync::{Arc, OnceLock};
use std::time::{Duration, Instant};
@@ -276,15 +276,13 @@ pub struct SandboxOptions {
/// default-deny seccomp filter scoped to [`SandboxOptions::seccomp_caps`].
/// Each primitive is best-effort; failures degrade to
/// [`HardeningLevel::Partial`] without aborting the run.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-#[derive(Default)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ProcessHardeningProfile {
#[default]
Standard,
Strict,
}
-
/// Phase 20 follow-up (Track E.4 ablation harness): selectively skip or
/// loosen individual Strict-profile primitives so the escape-fixture
/// matrix can prove the acceptance literal "removing any one Phase 17
@@ -387,7 +385,10 @@ pub struct HostPort {
impl HostPort {
pub fn new(host: impl Into, port: u16) -> Self {
- Self { host: host.into(), port }
+ Self {
+ host: host.into(),
+ port,
+ }
}
}
@@ -415,13 +416,16 @@ impl HostPort {
/// - [`NetworkPolicy::Open`] — unrestricted outbound. Docker: `bridge`
/// with no egress filter. Reserved for diagnostic / dev-only runs;
/// the verifier never sets this in production.
-#[derive(Debug, Clone)]
-#[derive(Default)]
+#[derive(Debug, Clone, Default)]
pub enum NetworkPolicy {
#[default]
None,
- StubsOnly { allow: Vec },
- OobOutbound { listener: Arc },
+ StubsOnly {
+ allow: Vec,
+ },
+ OobOutbound {
+ listener: Arc,
+ },
Open,
}
@@ -460,7 +464,6 @@ impl NetworkPolicy {
}
}
-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SandboxBackend {
Auto,
@@ -590,14 +593,14 @@ fn apply_oob_egress_filter(container_name: &str, oob_port: u16) {
let rules: &[&[&str]] = &[
// Allow container → host OOB port (INPUT; docker0 bridge to host).
- &["-I", "INPUT", "1", "-i", "docker0",
- "-s", ip, "-p", "tcp", "--dport", &port_str, "-j", "ACCEPT"],
+ &[
+ "-I", "INPUT", "1", "-i", "docker0", "-s", ip, "-p", "tcp", "--dport", &port_str, "-j",
+ "ACCEPT",
+ ],
// Drop all other container → host traffic (INPUT; position 2 fires after accept).
- &["-I", "INPUT", "2", "-i", "docker0",
- "-s", ip, "-j", "DROP"],
+ &["-I", "INPUT", "2", "-i", "docker0", "-s", ip, "-j", "DROP"],
// Drop all container egress to external internet (FORWARD / DOCKER-USER).
- &["-I", "DOCKER-USER", "1",
- "-s", ip, "-j", "DROP"],
+ &["-I", "DOCKER-USER", "1", "-s", ip, "-j", "DROP"],
];
let mut applied = 0usize;
@@ -617,7 +620,10 @@ fn apply_oob_egress_filter(container_name: &str, oob_port: u16) {
if applied == rules.len() {
oob_egress_registry().insert(
container_name.to_owned(),
- OobEgressState { container_ip, oob_port },
+ OobEgressState {
+ container_ip,
+ oob_port,
+ },
);
} else {
eprintln!(
@@ -644,12 +650,12 @@ fn remove_oob_egress_filter(container_name: &str) {
let ip = state.container_ip.as_str();
let rules: &[&[&str]] = &[
- &["-D", "INPUT", "-i", "docker0",
- "-s", ip, "-p", "tcp", "--dport", &port_str, "-j", "ACCEPT"],
- &["-D", "INPUT", "-i", "docker0",
- "-s", ip, "-j", "DROP"],
- &["-D", "DOCKER-USER",
- "-s", ip, "-j", "DROP"],
+ &[
+ "-D", "INPUT", "-i", "docker0", "-s", ip, "-p", "tcp", "--dport", &port_str, "-j",
+ "ACCEPT",
+ ],
+ &["-D", "INPUT", "-i", "docker0", "-s", ip, "-j", "DROP"],
+ &["-D", "DOCKER-USER", "-s", ip, "-j", "DROP"],
];
for rule in rules {
@@ -680,7 +686,9 @@ fn container_registry() -> &'static dashmap::DashMap {
/// on SIGKILL; the `sleep 300` in started containers bounds the leak window.
#[cfg(unix)]
extern "C" fn stop_all_containers() {
- let Some(reg) = CONTAINER_REGISTRY.get() else { return };
+ let Some(reg) = CONTAINER_REGISTRY.get() else {
+ return;
+ };
let bin = std::env::var("NYX_DOCKER_BIN").unwrap_or_else(|_| "docker".to_owned());
for entry in reg.iter() {
// Remove OOB egress filter before stopping the container so stale
@@ -779,10 +787,7 @@ pub fn run(
// backend in that case so the harness picks up the host
// venv / node_modules / vendor dir already prepared.
let needs_host_deps = harness_needs_host_deps(harness);
- if docker_available()
- && harness_is_interpreted(&harness.command)
- && !needs_host_deps
- {
+ if docker_available() && harness_is_interpreted(&harness.command) && !needs_host_deps {
run_docker(harness, payload_bytes, opts)
} else if docker_available() && harness_is_native_binary(&harness.command) {
run_native_binary_docker(harness, payload_bytes, opts)
@@ -841,7 +846,9 @@ fn run_firecracker(
}
#[cfg(not(feature = "firecracker"))]
{
- Err(SandboxError::BackendUnavailable(SandboxBackend::Firecracker))
+ Err(SandboxError::BackendUnavailable(
+ SandboxBackend::Firecracker,
+ ))
}
}
@@ -880,12 +887,9 @@ fn rewrite_extra_env_for_container(
&& let Some(idx) = fs_stub_roots
.iter()
.position(|p| p.as_os_str() == std::ffi::OsStr::new(v))
- {
- return (
- k.clone(),
- format!("{}/{idx}", docker::STUB_MOUNT_ROOT),
- );
- }
+ {
+ return (k.clone(), format!("{}/{idx}", docker::STUB_MOUNT_ROOT));
+ }
(k.clone(), v.clone())
})
.collect()
@@ -930,7 +934,13 @@ fn run_docker(
registry.insert(container_name.clone(), container_name.clone());
}
- exec_in_container(&container_name, harness, payload_bytes, opts, &fs_stub_roots)
+ exec_in_container(
+ &container_name,
+ harness,
+ payload_bytes,
+ opts,
+ &fs_stub_roots,
+ )
}
/// Returns true when `docker info` succeeds using the current `NYX_DOCKER_BIN`.
@@ -998,16 +1008,20 @@ fn start_container(
"run".into(),
"-d".into(),
"--rm".into(),
- "--name".into(), name.into(),
+ "--name".into(),
+ name.into(),
"--cap-drop=ALL".into(),
- "--security-opt".into(), "no-new-privileges:true".into(),
- "--tmpfs".into(), "/tmp:size=128m,exec".into(),
+ "--security-opt".into(),
+ "no-new-privileges:true".into(),
+ "--tmpfs".into(),
+ "/tmp:size=128m,exec".into(),
// Bind-mount the host workdir at the fixed `/work` path
// read-write so harness code can reference `/work/...` without
// threading the host tempdir through every layer. The mount
// alone is sufficient to deliver harness files into the
// container — no follow-up `docker cp` is needed.
- "-v".into(), workdir_mount,
+ "-v".into(),
+ workdir_mount,
];
// Phase 10 / Phase 19 (Track D.3 + E.3): bind-mount each
// filesystem-stub root at `STUB_MOUNT_ROOT/:rw` so the
@@ -1141,8 +1155,10 @@ fn exec_in_container(
// checks provide a second layer of defence on top of --cap-drop=ALL.
// The container itself starts as root for setup (mkdir, docker cp),
// but harness execution runs as nobody (uid/gid 65534).
- "--user".into(), "65534:65534".into(),
- "-e".into(), format!("NYX_PAYLOAD_B64={payload_b64}"),
+ "--user".into(),
+ "65534:65534".into(),
+ "-e".into(),
+ format!("NYX_PAYLOAD_B64={payload_b64}"),
];
// Mirror the process backend's `NYX_PAYLOAD` raw env var when the
// payload bytes are valid UTF-8 (most curated payloads are ASCII).
@@ -1157,10 +1173,11 @@ fn exec_in_container(
// non-UTF-8 payloads (a `docker -e` argument must be valid UTF-8),
// leaving consumers to decode `NYX_PAYLOAD_B64` themselves.
if let Ok(s) = std::str::from_utf8(payload_bytes)
- && !s.contains('\0') {
- cmd_args.push("-e".into());
- cmd_args.push(format!("NYX_PAYLOAD={s}"));
- }
+ && !s.contains('\0')
+ {
+ cmd_args.push("-e".into());
+ cmd_args.push(format!("NYX_PAYLOAD={s}"));
+ }
// Forward harness-specific env vars.
for (k, v) in &harness.env {
cmd_args.push("-e".into());
@@ -1276,7 +1293,11 @@ fn exec_in_container(
/// fall through to the legacy tag mapping below so behaviour on a fresh
/// catalogue stays unchanged.
fn detect_image_for_harness(harness: &BuiltHarness) -> String {
- let cmd0 = harness.command.first().map(|s| s.as_str()).unwrap_or("python3");
+ let cmd0 = harness
+ .command
+ .first()
+ .map(|s| s.as_str())
+ .unwrap_or("python3");
let base = std::path::Path::new(cmd0)
.file_name()
.and_then(|n| n.to_str())
@@ -1329,10 +1350,12 @@ fn run_native_binary_docker(
let binary_path = match harness.command.first() {
Some(p) => p.clone(),
- None => return Err(SandboxError::Spawn(std::io::Error::new(
- std::io::ErrorKind::InvalidInput,
- "empty command for native binary",
- ))),
+ None => {
+ return Err(SandboxError::Spawn(std::io::Error::new(
+ std::io::ErrorKind::InvalidInput,
+ "empty command for native binary",
+ )));
+ }
};
let container_name = workdir_to_container_name(&harness.workdir);
@@ -1385,7 +1408,13 @@ fn run_native_binary_docker(
registry.insert(container_name.clone(), container_name.clone());
}
- exec_native_binary_in_container(&container_name, harness, payload_bytes, opts, &fs_stub_roots)
+ exec_native_binary_in_container(
+ &container_name,
+ harness,
+ payload_bytes,
+ opts,
+ &fs_stub_roots,
+ )
}
/// Execute a native binary already in the container at `/work/nyx_harness`.
@@ -1403,8 +1432,10 @@ fn exec_native_binary_in_container(
let mut cmd_args: Vec = vec![
"exec".into(),
"-i".into(),
- "--user".into(), "65534:65534".into(),
- "-e".into(), format!("NYX_PAYLOAD_B64={payload_b64}"),
+ "--user".into(),
+ "65534:65534".into(),
+ "-e".into(),
+ format!("NYX_PAYLOAD_B64={payload_b64}"),
];
for (k, v) in &harness.env {
cmd_args.push("-e".into());
@@ -1566,10 +1597,8 @@ fn run_process(
None => (resolved_cmd_path.clone(), harness.command[1..].to_vec()),
};
#[cfg(not(target_os = "macos"))]
- let (effective_cmd_path, effective_cmd_args): (std::path::PathBuf, Vec) = (
- resolved_cmd_path.clone(),
- harness.command[1..].to_vec(),
- );
+ let (effective_cmd_path, effective_cmd_args): (std::path::PathBuf, Vec) =
+ (resolved_cmd_path.clone(), harness.command[1..].to_vec());
let mut cmd = Command::new(&effective_cmd_path);
cmd.args(&effective_cmd_args);
@@ -1894,9 +1923,15 @@ mod tests {
#[test]
fn python_image_for_known_toolchains() {
- assert_eq!(python_image_for_toolchain("python-3.11"), "python:3.11-slim");
+ assert_eq!(
+ python_image_for_toolchain("python-3.11"),
+ "python:3.11-slim"
+ );
assert_eq!(python_image_for_toolchain("python-3"), "python:3-slim");
- assert_eq!(python_image_for_toolchain("python-3.12"), "python:3.12-slim");
+ assert_eq!(
+ python_image_for_toolchain("python-3.12"),
+ "python:3.12-slim"
+ );
}
#[test]
@@ -1908,8 +1943,14 @@ mod tests {
#[test]
fn java_image_for_known_toolchains() {
- assert_eq!(java_image_for_toolchain("java-21"), "eclipse-temurin:21-jre-jammy");
- assert_eq!(java_image_for_toolchain("java-17"), "eclipse-temurin:17-jre-jammy");
+ assert_eq!(
+ java_image_for_toolchain("java-21"),
+ "eclipse-temurin:21-jre-jammy"
+ );
+ assert_eq!(
+ java_image_for_toolchain("java-17"),
+ "eclipse-temurin:17-jre-jammy"
+ );
}
#[test]
@@ -1927,13 +1968,21 @@ mod tests {
#[test]
fn harness_is_interpreted_java() {
- let cmd = vec!["java".to_owned(), "-cp".to_owned(), ".".to_owned(), "NyxHarness".to_owned()];
+ let cmd = vec![
+ "java".to_owned(),
+ "-cp".to_owned(),
+ ".".to_owned(),
+ "NyxHarness".to_owned(),
+ ];
assert!(harness_is_interpreted(&cmd));
}
#[test]
fn harness_is_interpreted_node() {
- assert!(harness_is_interpreted(&["node".to_owned(), "harness.js".to_owned()]));
+ assert!(harness_is_interpreted(&[
+ "node".to_owned(),
+ "harness.js".to_owned()
+ ]));
}
#[test]
@@ -2076,7 +2125,10 @@ mod tests {
fn fetch_docker_image_digest_short_returns_empty_on_bad_image() {
// A non-existent image tag always returns empty (inspect fails).
let digest = fetch_docker_image_digest_short("nyx-nonexistent-image:does-not-exist-99999");
- assert!(digest.is_empty(), "non-existent image must return empty digest");
+ assert!(
+ digest.is_empty(),
+ "non-existent image must return empty digest"
+ );
}
#[test]
@@ -2174,7 +2226,10 @@ mod tests {
fn rewrite_extra_env_passes_unrelated_pairs_through() {
let extra = vec![
("NYX_SQL_ENDPOINT".to_owned(), "/tmp/abc.db".to_owned()),
- ("NYX_HTTP_ENDPOINT".to_owned(), "http://127.0.0.1:12345".to_owned()),
+ (
+ "NYX_HTTP_ENDPOINT".to_owned(),
+ "http://127.0.0.1:12345".to_owned(),
+ ),
];
let out = rewrite_extra_env_for_container(&extra, &[]);
assert_eq!(out, extra);
@@ -2183,9 +2238,10 @@ mod tests {
#[test]
fn rewrite_extra_env_maps_fs_root_to_container_mount() {
let host_root = PathBuf::from("/tmp/host-fs-root-abc");
- let extra = vec![
- ("NYX_FS_ROOT".to_owned(), host_root.to_string_lossy().into_owned()),
- ];
+ let extra = vec![(
+ "NYX_FS_ROOT".to_owned(),
+ host_root.to_string_lossy().into_owned(),
+ )];
let out = rewrite_extra_env_for_container(&extra, &[host_root]);
assert_eq!(out.len(), 1);
assert_eq!(out[0].0, "NYX_FS_ROOT");
@@ -2198,13 +2254,8 @@ mod tests {
// active fs_stub_roots list is passed through unchanged. This
// keeps the rewrite from accidentally clobbering an emitter-
// supplied placeholder.
- let extra = vec![
- ("NYX_FS_ROOT".to_owned(), "/some/host/path".to_owned()),
- ];
- let out = rewrite_extra_env_for_container(
- &extra,
- &[PathBuf::from("/different/host/path")],
- );
+ let extra = vec![("NYX_FS_ROOT".to_owned(), "/some/host/path".to_owned())];
+ let out = rewrite_extra_env_for_container(&extra, &[PathBuf::from("/different/host/path")]);
assert_eq!(out, extra);
}
@@ -2212,9 +2263,10 @@ mod tests {
fn rewrite_extra_env_indexes_multiple_fs_roots() {
let root_a = PathBuf::from("/tmp/fs-a");
let root_b = PathBuf::from("/tmp/fs-b");
- let extra = vec![
- ("NYX_FS_ROOT".to_owned(), root_b.to_string_lossy().into_owned()),
- ];
+ let extra = vec![(
+ "NYX_FS_ROOT".to_owned(),
+ root_b.to_string_lossy().into_owned(),
+ )];
let out = rewrite_extra_env_for_container(&extra, &[root_a, root_b]);
assert_eq!(out[0].1, format!("{}/1", docker::STUB_MOUNT_ROOT));
}
@@ -2229,11 +2281,9 @@ mod tests {
fn collect_fs_stub_roots_returns_paths_for_filesystem_stubs() {
use crate::dynamic::stubs::StubKind;
let dir = tempfile::TempDir::new().expect("tempdir");
- let harness = crate::dynamic::stubs::StubHarness::start(
- &[StubKind::Filesystem],
- dir.path(),
- )
- .expect("start stub harness");
+ let harness =
+ crate::dynamic::stubs::StubHarness::start(&[StubKind::Filesystem], dir.path())
+ .expect("start stub harness");
let endpoint = harness.stubs()[0].endpoint();
let opts = SandboxOptions {
stub_harness: Some(Arc::new(harness)),
@@ -2248,11 +2298,9 @@ mod tests {
fn collect_fs_stub_roots_skips_network_stubs() {
use crate::dynamic::stubs::StubKind;
let dir = tempfile::TempDir::new().expect("tempdir");
- let harness = crate::dynamic::stubs::StubHarness::start(
- &[StubKind::Http, StubKind::Sql],
- dir.path(),
- )
- .expect("start stub harness");
+ let harness =
+ crate::dynamic::stubs::StubHarness::start(&[StubKind::Http, StubKind::Sql], dir.path())
+ .expect("start stub harness");
let opts = SandboxOptions {
stub_harness: Some(Arc::new(harness)),
..SandboxOptions::default()
diff --git a/src/dynamic/sandbox/process_linux.rs b/src/dynamic/sandbox/process_linux.rs
index e386f55b..2f62f960 100644
--- a/src/dynamic/sandbox/process_linux.rs
+++ b/src/dynamic/sandbox/process_linux.rs
@@ -119,8 +119,14 @@ impl HardeningOutcome {
self.chroot,
self.seccomp,
];
- let applied = primitives.iter().filter(|s| matches!(s, PrimitiveStatus::Applied)).count();
- let failed = primitives.iter().filter(|s| matches!(s, PrimitiveStatus::Failed(_))).count();
+ let applied = primitives
+ .iter()
+ .filter(|s| matches!(s, PrimitiveStatus::Applied))
+ .count();
+ let failed = primitives
+ .iter()
+ .filter(|s| matches!(s, PrimitiveStatus::Failed(_)))
+ .count();
match (applied, failed) {
(_, 0) => HardeningLevel::Full,
(0, _) => HardeningLevel::None,
@@ -147,7 +153,10 @@ impl StatusPipe {
if ret != 0 {
return Err(std::io::Error::last_os_error());
}
- Ok(Self { write_fd: fds[1], read_fd: fds[0] })
+ Ok(Self {
+ write_fd: fds[1],
+ read_fd: fds[0],
+ })
}
}
@@ -289,7 +298,10 @@ fn last_errno() -> i32 {
}
fn apply_rlimit(resource: i32, bytes: u64) -> PrimitiveStatus {
- let rl = Rlimit { cur: bytes, max: bytes };
+ let rl = Rlimit {
+ cur: bytes,
+ max: bytes,
+ };
let ret = unsafe { setrlimit(resource, &rl) };
if ret == 0 {
PrimitiveStatus::Applied
@@ -498,7 +510,9 @@ impl OutcomeCollector {
close_fd(self.write_fd);
let read_fd = self.read_fd;
let handle = std::thread::spawn(move || drain_outcome(read_fd));
- OutcomeJoiner { handle: Some(handle) }
+ OutcomeJoiner {
+ handle: Some(handle),
+ }
}
/// Call when `cmd.spawn()` failed. Closes both ends so neither fd
@@ -607,10 +621,8 @@ fn build_plan(opts: &SandboxOptions, workdir: &Path) -> PreExecPlan {
// prove that the corresponding seccomp slice carries its weight.
let ablation = opts.ablation;
let extras: Vec<&'static str> = ablation_extras(ablation);
- let nrs = seccomp::allowed_syscall_numbers_with_extras(
- opts.seccomp_caps,
- extras.iter().copied(),
- );
+ let nrs =
+ seccomp::allowed_syscall_numbers_with_extras(opts.seccomp_caps, extras.iter().copied());
let program = seccomp::bpf::compile(&nrs, seccomp::syscalls::AUDIT_ARCH);
let profile = match opts.process_hardening {
@@ -718,7 +730,8 @@ fn nul_terminate(bytes: &[u8]) -> Vec {
}
fn canonicalize_workdir(workdir: &Path) -> Vec {
- let canonical: PathBuf = std::fs::canonicalize(workdir).unwrap_or_else(|_| workdir.to_path_buf());
+ let canonical: PathBuf =
+ std::fs::canonicalize(workdir).unwrap_or_else(|_| workdir.to_path_buf());
let mut bytes = canonical.into_os_string().into_encoded_bytes();
if !bytes.ends_with(&[0]) {
bytes.push(0);
@@ -797,20 +810,30 @@ mod tests {
let plan = build_plan(&opts, std::path::Path::new("/tmp"));
// The arch check + ld nr + KILL + ALLOW alone are 5 instructions;
// the BASE allowlist adds dozens more.
- assert!(plan.seccomp_program.len() > 5, "BPF program too small: {}", plan.seccomp_program.len());
+ assert!(
+ plan.seccomp_program.len() > 5,
+ "BPF program too small: {}",
+ plan.seccomp_program.len()
+ );
assert_eq!(plan.profile, ProcessHardeningProfileTag::Strict);
}
#[test]
fn rlimit_as_bytes_floors_at_4_gib() {
- let opts = SandboxOptions { memory_mib: 1, ..SandboxOptions::default() };
+ let opts = SandboxOptions {
+ memory_mib: 1,
+ ..SandboxOptions::default()
+ };
let plan = build_plan(&opts, std::path::Path::new("/tmp"));
assert_eq!(plan.rlimit_as_bytes, 4096_u64 * 1024 * 1024);
}
#[test]
fn rlimit_as_bytes_scales_with_memory_mib() {
- let opts = SandboxOptions { memory_mib: 1024, ..SandboxOptions::default() };
+ let opts = SandboxOptions {
+ memory_mib: 1024,
+ ..SandboxOptions::default()
+ };
let plan = build_plan(&opts, std::path::Path::new("/tmp"));
// 1024 MiB * 8 = 8192 MiB
assert_eq!(plan.rlimit_as_bytes, 8192_u64 * 1024 * 1024);
@@ -865,8 +888,14 @@ mod tests {
// Every entry's source must be NUL-terminated for the `mount(2)`
// call, and every dest must exist on disk.
for m in &plan.bind_mounts {
- assert!(m.source_nul.ends_with(&[0]), "source path must be NUL-terminated");
- assert!(m.dest_nul.ends_with(&[0]), "dest path must be NUL-terminated");
+ assert!(
+ m.source_nul.ends_with(&[0]),
+ "source path must be NUL-terminated"
+ );
+ assert!(
+ m.dest_nul.ends_with(&[0]),
+ "dest path must be NUL-terminated"
+ );
let dest_str = std::str::from_utf8(&m.dest_nul[..m.dest_nul.len() - 1])
.expect("dest path must be valid UTF-8");
assert!(
@@ -920,8 +949,16 @@ mod tests {
..AblationMask::default()
}));
assert_eq!(flags & CLONE_NEWUSER, 0, "CLONE_NEWUSER must be dropped");
- assert_eq!(flags & CLONE_NEWPID, CLONE_NEWPID, "CLONE_NEWPID must persist");
- assert_eq!(flags & CLONE_NEWNS, CLONE_NEWNS, "CLONE_NEWNS must persist (bind-mount target)");
+ assert_eq!(
+ flags & CLONE_NEWPID,
+ CLONE_NEWPID,
+ "CLONE_NEWPID must persist"
+ );
+ assert_eq!(
+ flags & CLONE_NEWNS,
+ CLONE_NEWNS,
+ "CLONE_NEWNS must persist (bind-mount target)"
+ );
}
#[test]
@@ -931,7 +968,11 @@ mod tests {
..AblationMask::default()
}));
assert_eq!(flags & CLONE_NEWPID, 0, "CLONE_NEWPID must be dropped");
- assert_eq!(flags & CLONE_NEWUSER, CLONE_NEWUSER, "CLONE_NEWUSER must persist");
+ assert_eq!(
+ flags & CLONE_NEWUSER,
+ CLONE_NEWUSER,
+ "CLONE_NEWUSER must persist"
+ );
}
#[test]
@@ -1054,8 +1095,8 @@ mod tests {
..SandboxOptions::default()
};
let plan = build_plan(&opts, std::path::Path::new("/tmp"));
- let socket_nr = seccomp::syscalls::syscall_number("socket")
- .expect("socket in per-arch syscall map");
+ let socket_nr =
+ seccomp::syscalls::syscall_number("socket").expect("socket in per-arch syscall map");
// BPF compile emits one JEQ per allowed syscall (+ a fixed arch
// prelude + a default-deny tail), so encoding socket as a JEQ
// instruction's k-field is the load-bearing signal.
@@ -1080,8 +1121,8 @@ mod tests {
..SandboxOptions::default()
};
let plan = build_plan(&opts, std::path::Path::new("/tmp"));
- let setuid_nr = seccomp::syscalls::syscall_number("setuid")
- .expect("setuid in per-arch syscall map");
+ let setuid_nr =
+ seccomp::syscalls::syscall_number("setuid").expect("setuid in per-arch syscall map");
let program = plan.seccomp_program.as_slice();
let landed = program.iter().any(|insn| insn.k == setuid_nr);
assert!(
@@ -1104,8 +1145,8 @@ mod tests {
..SandboxOptions::default()
};
let plan = build_plan(&opts, std::path::Path::new("/tmp"));
- let socket_nr = seccomp::syscalls::syscall_number("socket")
- .expect("socket in per-arch syscall map");
+ let socket_nr =
+ seccomp::syscalls::syscall_number("socket").expect("socket in per-arch syscall map");
let landed = plan.seccomp_program.iter().any(|insn| insn.k == socket_nr);
assert!(
!landed,
@@ -1148,5 +1189,4 @@ mod tests {
outcome.no_new_privs,
);
}
-
}
diff --git a/src/dynamic/sandbox/process_macos.rs b/src/dynamic/sandbox/process_macos.rs
index 704e0f3a..8be80d3b 100644
--- a/src/dynamic/sandbox/process_macos.rs
+++ b/src/dynamic/sandbox/process_macos.rs
@@ -124,7 +124,10 @@ const PROFILE_SOURCES: &[(&str, &str)] = &[
include_str!("../sandbox_profiles/path_traversal.sb"),
),
("ssrf", include_str!("../sandbox_profiles/ssrf.sb")),
- ("deserialize", include_str!("../sandbox_profiles/deserialize.sb")),
+ (
+ "deserialize",
+ include_str!("../sandbox_profiles/deserialize.sb"),
+ ),
("xxe", include_str!("../sandbox_profiles/xxe.sb")),
(
"open_redirect",
@@ -305,9 +308,7 @@ pub fn splice_deny_default(source: &str, seed: &str) -> String {
rewritten.push('\n');
}
rewritten.push('\n');
- rewritten.push_str(
- ";; ── deny-default seed (spliced by NYX_SB_DENY_DEFAULT=1) ──────────\n",
- );
+ rewritten.push_str(";; ── deny-default seed (spliced by NYX_SB_DENY_DEFAULT=1) ──────────\n");
rewritten.push_str(seed.trim_end());
rewritten.push('\n');
rewritten
@@ -378,7 +379,9 @@ pub fn wrap_plan(input: &WrapInput<'_>) -> WrapResult {
},
};
}
- let profile = input.profile_override.unwrap_or_else(|| profile_for_caps(input.caps));
+ let profile = input
+ .profile_override
+ .unwrap_or_else(|| profile_for_caps(input.caps));
// Profile keys must be `&'static str` (from `PROFILE_SOURCES`); reject
// unknown overrides up-front so we don't accidentally wrap with a
// profile we have no source for.
@@ -411,7 +414,8 @@ pub fn wrap_plan(input: &WrapInput<'_>) -> WrapResult {
}
};
- let workdir_abs = std::fs::canonicalize(input.workdir).unwrap_or_else(|_| input.workdir.to_path_buf());
+ let workdir_abs =
+ std::fs::canonicalize(input.workdir).unwrap_or_else(|_| input.workdir.to_path_buf());
let mut args: Vec = Vec::with_capacity(6 + input.cmd_args.len());
args.push("-f".to_owned());
@@ -573,7 +577,10 @@ mod tests {
// resetting the env var below restores the default for subsequent
// tests in the same process.
unsafe { std::env::set_var(SANDBOX_EXEC_BIN_ENV, "/nonexistent/sandbox-exec") };
- assert_eq!(sandbox_exec_bin(), PathBuf::from("/nonexistent/sandbox-exec"));
+ assert_eq!(
+ sandbox_exec_bin(),
+ PathBuf::from("/nonexistent/sandbox-exec")
+ );
assert!(!sandbox_exec_available());
unsafe { std::env::remove_var(SANDBOX_EXEC_BIN_ENV) };
}
diff --git a/src/dynamic/sandbox/seccomp/bpf.rs b/src/dynamic/sandbox/seccomp/bpf.rs
index 039b5f3d..f7ded070 100644
--- a/src/dynamic/sandbox/seccomp/bpf.rs
+++ b/src/dynamic/sandbox/seccomp/bpf.rs
@@ -71,7 +71,12 @@ pub fn compile(allowed_nrs: &[u32], audit_arch: u32) -> Vec {
// (1) jeq audit_arch ? next : KILL
// KILL is at the very end; computed below after we know the size.
let arch_check_idx = program.len();
- program.push(SockFilter { code: BPF_JMP | BPF_JEQ | BPF_K, jt: 0, jf: 0, k: audit_arch });
+ program.push(SockFilter {
+ code: BPF_JMP | BPF_JEQ | BPF_K,
+ jt: 0,
+ jf: 0,
+ k: audit_arch,
+ });
// (2) ld [nr]
program.push(SockFilter {
@@ -90,7 +95,12 @@ pub fn compile(allowed_nrs: &[u32], audit_arch: u32) -> Vec {
// plus the KILL ret) to land on the ALLOW ret. Computed below.
let first_check_idx = program.len();
for &nr in allowed_nrs {
- program.push(SockFilter { code: BPF_JMP | BPF_JEQ | BPF_K, jt: 0, jf: 0, k: nr });
+ program.push(SockFilter {
+ code: BPF_JMP | BPF_JEQ | BPF_K,
+ jt: 0,
+ jf: 0,
+ k: nr,
+ });
}
// (KILL) ret KILL_PROCESS
@@ -103,7 +113,12 @@ pub fn compile(allowed_nrs: &[u32], audit_arch: u32) -> Vec {
});
// (ALLOW) ret ALLOW
let allow_idx = program.len();
- program.push(SockFilter { code: BPF_RET | BPF_K, jt: 0, jf: 0, k: SECCOMP_RET_ALLOW });
+ program.push(SockFilter {
+ code: BPF_RET | BPF_K,
+ jt: 0,
+ jf: 0,
+ k: SECCOMP_RET_ALLOW,
+ });
// Patch arch check: jt=0 (next on match), jf=N (KILL on mismatch).
let arch_jf = (kill_idx - arch_check_idx - 1) as u8;
diff --git a/src/dynamic/sandbox/seccomp/mod.rs b/src/dynamic/sandbox/seccomp/mod.rs
index c4cbd248..d5687e05 100644
--- a/src/dynamic/sandbox/seccomp/mod.rs
+++ b/src/dynamic/sandbox/seccomp/mod.rs
@@ -34,7 +34,7 @@ pub mod syscalls;
use std::collections::BTreeSet;
use crate::dynamic::sandbox::seccomp::bpf::{SockFilter, SockFprog};
-use crate::dynamic::sandbox::seccomp::syscalls::{syscall_number, AUDIT_ARCH};
+use crate::dynamic::sandbox::seccomp::syscalls::{AUDIT_ARCH, syscall_number};
include!(concat!(env!("OUT_DIR"), "/seccomp_policy.rs"));
@@ -174,15 +174,15 @@ mod tests {
#[test]
fn base_table_is_non_empty() {
- assert!(!BASE.is_empty(), "seccomp BASE allowlist must include stdio + startup syscalls");
+ assert!(
+ !BASE.is_empty(),
+ "seccomp BASE allowlist must include stdio + startup syscalls"
+ );
}
#[test]
fn cap_table_includes_known_caps() {
- let known: Vec<&str> = CAP
- .iter()
- .map(|(_, _)| "_")
- .collect();
+ let known: Vec<&str> = CAP.iter().map(|(_, _)| "_").collect();
// We declared SQL_QUERY, FILE_IO, SSRF, CODE_EXEC, HTML_ESCAPE,
// DESERIALIZE, HEADER_INJECTION, OPEN_REDIRECT in the toml; the
// build script emits one entry per `[cap.X]` table. The exact
diff --git a/src/dynamic/spec.rs b/src/dynamic/spec.rs
index 7582ba8b..4140759a 100644
--- a/src/dynamic/spec.rs
+++ b/src/dynamic/spec.rs
@@ -298,7 +298,10 @@ impl HarnessSpec {
}
}
- let evidence = diag.evidence.as_ref().ok_or(UnsupportedReason::NoFlowSteps)?;
+ let evidence = diag
+ .evidence
+ .as_ref()
+ .ok_or(UnsupportedReason::NoFlowSteps)?;
// Phase 04 pre-step: when both callgraph *and* summaries are
// present, walk reverse edges to a framework-bound ancestor.
@@ -313,9 +316,10 @@ impl HarnessSpec {
// strategies (FromFlowSteps / FromRuleNamespace / FromFuncSummaryAuto)
// whenever the rule id happens to contain `.http.` / `.cli.`.
if let (Some(s), Some(cg)) = (summaries, callgraph)
- && let Some(spec) = derive_from_callgraph_walk_only(diag, evidence, s, cg) {
- return Ok(spec);
- }
+ && let Some(spec) = derive_from_callgraph_walk_only(diag, evidence, s, cg)
+ {
+ return Ok(spec);
+ }
// Try each strategy in priority order; first non-None wins.
if let Some(spec) = derive_from_flow_steps(diag, evidence, summaries) {
@@ -327,8 +331,7 @@ impl HarnessSpec {
if let Some(spec) = derive_from_func_summary_auto(diag, evidence, summaries) {
return Ok(spec);
}
- if let Some(spec) = derive_from_callgraph_entry_full(diag, evidence, summaries, callgraph)
- {
+ if let Some(spec) = derive_from_callgraph_entry_full(diag, evidence, summaries, callgraph) {
return Ok(spec);
}
@@ -520,9 +523,10 @@ pub fn derive_from_rule_namespace_with(
// language prefix when both are available. Disagreement is a stronger
// signal of a mis-rooted finding than a missing extension.
if let Some(path_lang) = lang_from_path(&diag.path)
- && path_lang != lang {
- return None;
- }
+ && path_lang != lang
+ {
+ return None;
+ }
let entry_function = resolve_enclosing_function(diag, evidence, summaries, lang)
.unwrap_or_else(|| "".to_owned());
@@ -749,33 +753,34 @@ pub fn derive_from_callgraph_entry_full(
// Step 0: callgraph-aware reverse-edge walk to the nearest entry-point
// ancestor. Only fires when both summaries *and* callgraph are present.
if let (Some(s), Some(cg)) = (summaries, callgraph)
- && let Some(found) = find_entry_via_callgraph(diag, evidence, s, cg, lang) {
- let entry_kind = found
- .summary
- .entry_kind
- .as_ref()
- .map(entry_kind_from_summary)
- .unwrap_or_else(|| name_to_entry_kind(&found.summary.name));
- let entry_file = if !found.summary.file_path.is_empty() {
- found.summary.file_path.clone()
- } else {
- diag.path.clone()
- };
- let mut spec = finalize_spec(
- diag,
- entry_file,
- found.summary.name.clone(),
- lang,
- expected_cap,
- diag.path.clone(),
- diag.line as u32,
- SpecDerivationStrategy::FromCallgraphEntry,
- Some(s),
- );
- spec.entry_kind = entry_kind;
- spec.spec_hash = compute_spec_hash(&spec);
- return Some(spec);
- }
+ && let Some(found) = find_entry_via_callgraph(diag, evidence, s, cg, lang)
+ {
+ let entry_kind = found
+ .summary
+ .entry_kind
+ .as_ref()
+ .map(entry_kind_from_summary)
+ .unwrap_or_else(|| name_to_entry_kind(&found.summary.name));
+ let entry_file = if !found.summary.file_path.is_empty() {
+ found.summary.file_path.clone()
+ } else {
+ diag.path.clone()
+ };
+ let mut spec = finalize_spec(
+ diag,
+ entry_file,
+ found.summary.name.clone(),
+ lang,
+ expected_cap,
+ diag.path.clone(),
+ diag.line as u32,
+ SpecDerivationStrategy::FromCallgraphEntry,
+ Some(s),
+ );
+ spec.entry_kind = entry_kind;
+ spec.spec_hash = compute_spec_hash(&spec);
+ return Some(spec);
+ }
// Step 1: try summary-based classification of the enclosing function.
let summary_kind = enclosing_function_from_flow_steps(evidence)
@@ -934,12 +939,13 @@ fn find_entry_via_callgraph<'a>(
}
let caller_key = &callgraph.graph[caller_node];
if let Some(caller_summary) = summaries.get(caller_key)
- && is_entry_point(caller_summary, callgraph) {
- return Some(EntryHit {
- key: caller_key.clone(),
- summary: caller_summary,
- });
- }
+ && is_entry_point(caller_summary, callgraph)
+ {
+ return Some(EntryHit {
+ key: caller_key.clone(),
+ summary: caller_summary,
+ });
+ }
queue.push_back(caller_node);
}
}
@@ -970,9 +976,10 @@ fn entry_kind_from_summary(_kind: &crate::entry_points::EntryKind) -> EntryKind
fn lang_from_path(path: &str) -> Option {
let p = Path::new(path);
if let Some(ext) = p.extension().and_then(|e| e.to_str())
- && let Some(lang) = Lang::from_extension(ext) {
- return Some(lang);
- }
+ && let Some(lang) = Lang::from_extension(ext)
+ {
+ return Some(lang);
+ }
// Fall back to a shebang / content sniff over the file head.
let head = read_file_head(p, 200);
if head.is_empty() {
@@ -1305,12 +1312,13 @@ pub fn outermost_entry(steps: &[crate::evidence::FlowStep]) -> Option
for step in steps {
if matches!(step.kind, FlowStepKind::Source)
&& let Some(ref func) = step.function
- && !func.is_empty() {
- return Some(EntryRef {
- file: step.file.clone(),
- function: func.clone(),
- });
- }
+ && !func.is_empty()
+ {
+ return Some(EntryRef {
+ file: step.file.clone(),
+ function: func.clone(),
+ });
+ }
}
None
}
@@ -1401,7 +1409,10 @@ fn compute_spec_hash(spec: &HarnessSpec) -> String {
let out = h.finalize();
let bytes = out.as_bytes();
- format!("{:016x}", u64::from_le_bytes(bytes[..8].try_into().unwrap()))
+ format!(
+ "{:016x}",
+ u64::from_le_bytes(bytes[..8].try_into().unwrap())
+ )
}
#[cfg(test)]
@@ -1441,7 +1452,10 @@ mod tests {
#[test]
fn outermost_entry_picks_source_step() {
- let steps = vec![source_step("src/main.rs", "handle_request"), sink_step("src/main.rs")];
+ let steps = vec![
+ source_step("src/main.rs", "handle_request"),
+ sink_step("src/main.rs"),
+ ];
let entry = outermost_entry(&steps).unwrap();
assert_eq!(entry.file, "src/main.rs");
assert_eq!(entry.function, "handle_request");
@@ -1580,7 +1594,10 @@ mod tests {
let mut s2 = s1.clone();
s2.entry_file = "src/other.rs".into();
s2.spec_hash = compute_spec_hash(&s2);
- assert_ne!(s1.spec_hash, s2.spec_hash, "entry_file mutation must change spec_hash");
+ assert_ne!(
+ s1.spec_hash, s2.spec_hash,
+ "entry_file mutation must change spec_hash"
+ );
}
#[test]
@@ -1589,7 +1606,10 @@ mod tests {
let mut s2 = s1.clone();
s2.entry_name = "other_handler".into();
s2.spec_hash = compute_spec_hash(&s2);
- assert_ne!(s1.spec_hash, s2.spec_hash, "entry_name mutation must change spec_hash");
+ assert_ne!(
+ s1.spec_hash, s2.spec_hash,
+ "entry_name mutation must change spec_hash"
+ );
}
#[test]
@@ -1598,17 +1618,26 @@ mod tests {
let mut s2 = s1.clone();
s2.payload_slot = PayloadSlot::Param(1);
s2.spec_hash = compute_spec_hash(&s2);
- assert_ne!(s1.spec_hash, s2.spec_hash, "payload_slot mutation must change spec_hash");
+ assert_ne!(
+ s1.spec_hash, s2.spec_hash,
+ "payload_slot mutation must change spec_hash"
+ );
let mut s3 = s1.clone();
s3.payload_slot = PayloadSlot::HttpBody;
s3.spec_hash = compute_spec_hash(&s3);
- assert_ne!(s1.spec_hash, s3.spec_hash, "payload_slot tag change must change spec_hash");
+ assert_ne!(
+ s1.spec_hash, s3.spec_hash,
+ "payload_slot tag change must change spec_hash"
+ );
let mut s4 = s1.clone();
s4.payload_slot = PayloadSlot::EnvVar("NYX_INPUT".into());
s4.spec_hash = compute_spec_hash(&s4);
- assert_ne!(s1.spec_hash, s4.spec_hash, "EnvVar payload_slot must change spec_hash");
+ assert_ne!(
+ s1.spec_hash, s4.spec_hash,
+ "EnvVar payload_slot must change spec_hash"
+ );
}
#[test]
@@ -1618,7 +1647,10 @@ mod tests {
let mut s2 = s1.clone();
s2.expected_cap = Cap::CODE_EXEC;
s2.spec_hash = compute_spec_hash(&s2);
- assert_ne!(s1.spec_hash, s2.spec_hash, "expected_cap mutation must change spec_hash");
+ assert_ne!(
+ s1.spec_hash, s2.spec_hash,
+ "expected_cap mutation must change spec_hash"
+ );
}
#[test]
@@ -1627,7 +1659,10 @@ mod tests {
let mut s2 = s1.clone();
s2.constraint_hints = vec!["prefix:admin/".into()];
s2.spec_hash = compute_spec_hash(&s2);
- assert_ne!(s1.spec_hash, s2.spec_hash, "constraint_hints mutation must change spec_hash");
+ assert_ne!(
+ s1.spec_hash, s2.spec_hash,
+ "constraint_hints mutation must change spec_hash"
+ );
}
#[test]
@@ -1636,7 +1671,10 @@ mod tests {
let mut s2 = s1.clone();
s2.toolchain_id = "rust-nightly".into();
s2.spec_hash = compute_spec_hash(&s2);
- assert_ne!(s1.spec_hash, s2.spec_hash, "toolchain_id mutation must change spec_hash");
+ assert_ne!(
+ s1.spec_hash, s2.spec_hash,
+ "toolchain_id mutation must change spec_hash"
+ );
}
// ── Phase 01: derivation strategies ──────────────────────────────────────
@@ -1691,7 +1729,11 @@ mod tests {
#[test]
fn rule_namespace_strategy_fires_without_flow_steps() {
use crate::labels::Cap;
- let diag = diag_with_rule_id("py.cmdi.os_system", "app/handler.py", Cap::SHELL_ESCAPE.bits());
+ let diag = diag_with_rule_id(
+ "py.cmdi.os_system",
+ "app/handler.py",
+ Cap::SHELL_ESCAPE.bits(),
+ );
let spec = HarnessSpec::from_finding(&diag).unwrap();
assert_eq!(spec.derivation, SpecDerivationStrategy::FromRuleNamespace);
assert_eq!(spec.lang, Lang::Python);
@@ -1713,11 +1755,7 @@ mod tests {
fn rule_namespace_strategy_pins_rs_auth_mapping() {
// Regression: `rs.auth.*` must map to `Lang::Rust` + `Cap::UNAUTHORIZED_ID`.
// The plan calls out this exemplar but had no test coverage.
- let diag = diag_with_rule_id(
- "rs.auth.missing_ownership_check.taint",
- "src/handler.rs",
- 0,
- );
+ let diag = diag_with_rule_id("rs.auth.missing_ownership_check.taint", "src/handler.rs", 0);
let spec = HarnessSpec::from_finding(&diag).unwrap();
assert_eq!(spec.derivation, SpecDerivationStrategy::FromRuleNamespace);
assert_eq!(spec.lang, Lang::Rust);
@@ -1729,7 +1767,11 @@ mod tests {
fn rule_namespace_strategy_rejects_path_lang_mismatch() {
use crate::labels::Cap;
// `py.*` rule id, but a `.java` file — the cross-check refuses.
- let diag = diag_with_rule_id("py.cmdi.os_system", "src/Main.java", Cap::SHELL_ESCAPE.bits());
+ let diag = diag_with_rule_id(
+ "py.cmdi.os_system",
+ "src/Main.java",
+ Cap::SHELL_ESCAPE.bits(),
+ );
assert_eq!(
HarnessSpec::from_finding(&diag).unwrap_err(),
UnsupportedReason::SpecDerivationFailed
@@ -1752,8 +1794,11 @@ mod tests {
// Unregistered `taint-*` rule slugs (e.g. the legacy generic
// `taint-unsanitised-flow`) are not in `CAP_RULE_REGISTRY`; the
// shortcut must skip them so downstream strategies can try.
- let diag =
- diag_with_rule_id("taint-unsanitised-flow", "app/handler.py", Cap::SHELL_ESCAPE.bits());
+ let diag = diag_with_rule_id(
+ "taint-unsanitised-flow",
+ "app/handler.py",
+ Cap::SHELL_ESCAPE.bits(),
+ );
// No flow_steps, no http/cli marker → ends in SpecDerivationFailed.
assert_eq!(
HarnessSpec::from_finding(&diag).unwrap_err(),
@@ -1793,8 +1838,11 @@ mod tests {
fn rule_namespace_strategy_taint_id_lang_follows_path_extension() {
use crate::labels::Cap;
// Same rule slug, different file extension → derives a Go spec.
- let diag =
- diag_with_rule_id("taint-data-exfiltration", "cmd/leak.go", Cap::DATA_EXFIL.bits());
+ let diag = diag_with_rule_id(
+ "taint-data-exfiltration",
+ "cmd/leak.go",
+ Cap::DATA_EXFIL.bits(),
+ );
let spec = HarnessSpec::from_finding(&diag).unwrap();
assert_eq!(spec.derivation, SpecDerivationStrategy::FromRuleNamespace);
assert_eq!(spec.lang, Lang::Go);
@@ -1881,7 +1929,11 @@ mod tests {
#[test]
fn callgraph_entry_strategy_fires_on_cli_rule_id() {
use crate::labels::Cap;
- let diag = diag_with_rule_id("rs.cli.parse_subcommand", "src/main.rs", Cap::SHELL_ESCAPE.bits());
+ let diag = diag_with_rule_id(
+ "rs.cli.parse_subcommand",
+ "src/main.rs",
+ Cap::SHELL_ESCAPE.bits(),
+ );
let spec = HarnessSpec::from_finding(&diag).unwrap();
assert_eq!(spec.derivation, SpecDerivationStrategy::FromCallgraphEntry);
assert!(matches!(spec.entry_kind, EntryKind::CliSubcommand));
@@ -1928,7 +1980,14 @@ mod tests {
}
}
- fn build_summary(name: &str, file: &str, lang: &str, sink_caps: u32, tainted_params: Vec, entry_kind: Option) -> FuncSummary {
+ fn build_summary(
+ name: &str,
+ file: &str,
+ lang: &str,
+ sink_caps: u32,
+ tainted_params: Vec,
+ entry_kind: Option,
+ ) -> FuncSummary {
FuncSummary {
name: name.into(),
file_path: file.into(),
@@ -1962,10 +2021,7 @@ mod tests {
// enclosing function name.
use crate::labels::Cap;
let ev = Evidence {
- flow_steps: vec![sink_only_step_with_function(
- "app/handler.py",
- "do_request",
- )],
+ flow_steps: vec![sink_only_step_with_function("app/handler.py", "do_request")],
sink_caps: Cap::SHELL_ESCAPE.bits(),
..Default::default()
};
@@ -2004,10 +2060,7 @@ mod tests {
gs.insert(key, summary);
let ev = Evidence {
- flow_steps: vec![sink_only_step_with_function(
- "app/handler.py",
- "do_request",
- )],
+ flow_steps: vec![sink_only_step_with_function("app/handler.py", "do_request")],
sink_caps: Cap::SHELL_ESCAPE.bits(),
..Default::default()
};
@@ -2041,7 +2094,9 @@ mod tests {
"python",
Cap::SSRF.bits(),
vec![],
- Some(StaticEntryKind::FlaskRoute { method: HttpMethod::GET }),
+ Some(StaticEntryKind::FlaskRoute {
+ method: HttpMethod::GET,
+ }),
);
let key = FuncKey::new_function(Lang::Python, "app/views.py", "index", Some(1));
gs.insert(key, summary);
@@ -2302,7 +2357,10 @@ mod tests {
};
stamp_framework_binding(&mut spec, binding);
- assert_eq!(spec.entry_kind.tag(), crate::evidence::EntryKindTag::Function);
+ assert_eq!(
+ spec.entry_kind.tag(),
+ crate::evidence::EntryKindTag::Function
+ );
assert_eq!(spec.spec_hash, pre_hash);
assert!(spec.framework.is_some());
}
diff --git a/src/dynamic/stubs/filesystem.rs b/src/dynamic/stubs/filesystem.rs
index 0211019a..59bcb20c 100644
--- a/src/dynamic/stubs/filesystem.rs
+++ b/src/dynamic/stubs/filesystem.rs
@@ -53,8 +53,7 @@ impl FilesystemStub {
/// in restricted environments (e.g. CI sandboxes that share a
/// read-only workdir).
pub fn start(workdir: &Path) -> std::io::Result {
- let tempdir = TempDir::new_in(workdir)
- .or_else(|_| TempDir::new())?;
+ let tempdir = TempDir::new_in(workdir).or_else(|_| TempDir::new())?;
let root = tempdir.path().to_owned();
Ok(Self {
tempdir: Some(tempdir),
@@ -88,7 +87,8 @@ impl FilesystemStub {
// Canonicalise both sides where possible so symlinks /
// relative path segments do not fool the prefix check.
let resolved_root = std::fs::canonicalize(&self.root).unwrap_or_else(|_| self.root.clone());
- let resolved_cand = std::fs::canonicalize(candidate).unwrap_or_else(|_| candidate.to_owned());
+ let resolved_cand =
+ std::fs::canonicalize(candidate).unwrap_or_else(|_| candidate.to_owned());
resolved_cand.starts_with(&resolved_root)
}
}
@@ -145,10 +145,7 @@ mod tests {
assert_eq!(events.len(), 1);
assert_eq!(events[0].kind, StubKind::Filesystem);
assert!(events[0].summary.contains("/etc/passwd"));
- assert_eq!(
- events[0].detail.get("op").map(String::as_str),
- Some("read")
- );
+ assert_eq!(events[0].detail.get("op").map(String::as_str), Some("read"));
}
#[test]
diff --git a/src/dynamic/stubs/http.rs b/src/dynamic/stubs/http.rs
index eea1d556..7dfe5033 100644
--- a/src/dynamic/stubs/http.rs
+++ b/src/dynamic/stubs/http.rs
@@ -31,7 +31,7 @@
//! recording log lives under the workdir-rooted tempdir which is
//! cleaned up by the verifier's tempdir handle.
-use super::{monotonic_ns, StubEvent, StubKind, StubProvider};
+use super::{StubEvent, StubKind, StubProvider, monotonic_ns};
use std::collections::BTreeMap;
use std::io::{BufRead, BufReader, Read, Write};
use std::net::{TcpListener, TcpStream};
@@ -182,7 +182,10 @@ impl StubProvider for HttpStub {
}
fn recording_endpoint(&self) -> Option<(&'static str, String)> {
- Some((HTTP_STUB_LOG_ENV_VAR, self.log_path.to_string_lossy().into_owned()))
+ Some((
+ HTTP_STUB_LOG_ENV_VAR,
+ self.log_path.to_string_lossy().into_owned(),
+ ))
}
fn drain_events(&self) -> Vec {
@@ -227,9 +230,10 @@ fn accept_loop(
let _ = stream.set_write_timeout(Some(Duration::from_secs(2)));
if let Some(ev) = handle_connection(stream, MAX_REQUEST_BYTES)
- && let Ok(mut g) = events.lock() {
- g.push(ev);
- }
+ && let Ok(mut g) = events.lock()
+ {
+ g.push(ev);
+ }
}
}
@@ -257,21 +261,19 @@ fn handle_connection(mut stream: TcpStream, max_bytes: usize) -> Option() {
- content_length = n.min(max_bytes);
- }
+ if let Some(rest) = trimmed.to_ascii_lowercase().strip_prefix("content-length:")
+ && let Ok(n) = rest.trim().parse::()
+ {
+ content_length = n.min(max_bytes);
+ }
headers.push(trimmed.to_owned());
}
// Body, capped at content_length (already clamped to max_bytes).
let mut body = vec![0u8; content_length];
- if content_length > 0
- && reader.read_exact(&mut body).is_err() {
- body.clear();
- }
+ if content_length > 0 && reader.read_exact(&mut body).is_err() {
+ body.clear();
+ }
// Always reply 200 OK with no body.
let _ = stream.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
@@ -419,8 +421,10 @@ mod tests {
.append(true)
.open(stub.log_path())
.unwrap();
- f.write_all(b"# method: POST\n# url: http://example.com/login\nPOST http://example.com/login\n")
- .unwrap();
+ f.write_all(
+ b"# method: POST\n# url: http://example.com/login\nPOST http://example.com/login\n",
+ )
+ .unwrap();
drop(f);
let events = stub.drain_events();
diff --git a/src/dynamic/stubs/ldap_server.rs b/src/dynamic/stubs/ldap_server.rs
index 3c70103a..223bb09c 100644
--- a/src/dynamic/stubs/ldap_server.rs
+++ b/src/dynamic/stubs/ldap_server.rs
@@ -41,7 +41,7 @@
//! Signals the accept thread to shut down and connects to itself to
//! wake the blocking `accept()`.
-use super::{monotonic_ns, StubEvent, StubKind, StubProvider};
+use super::{StubEvent, StubKind, StubProvider, monotonic_ns};
use std::collections::BTreeMap;
use std::io::{BufRead, BufReader, Write};
use std::net::{TcpListener, TcpStream};
@@ -105,10 +105,7 @@ impl LdapStub {
detail: {
let mut d = BTreeMap::new();
d.insert("filter".to_owned(), filter.to_owned());
- d.insert(
- "entries_returned".to_owned(),
- entries_returned.to_string(),
- );
+ d.insert("entries_returned".to_owned(), entries_returned.to_string());
d
},
};
@@ -170,11 +167,7 @@ fn accept_loop(
}
}
-fn handle_connection(
- mut stream: TcpStream,
- max_bytes: usize,
- events: &Arc>>,
-) {
+fn handle_connection(mut stream: TcpStream, max_bytes: usize, events: &Arc>>) {
let mut reader = match stream.try_clone() {
Ok(s) => BufReader::new(s),
Err(_) => return,
@@ -240,7 +233,10 @@ fn match_filter(filter: &str) -> Vec<&'static str> {
#[derive(Debug)]
enum Filter<'a> {
- Eq { attr: &'a str, pattern: &'a str },
+ Eq {
+ attr: &'a str,
+ pattern: &'a str,
+ },
And(Vec>),
Or(Vec>),
/// Anything we did not recognise — treated as match-everything by
diff --git a/src/dynamic/stubs/mod.rs b/src/dynamic/stubs/mod.rs
index 74d5d71c..6267f603 100644
--- a/src/dynamic/stubs/mod.rs
+++ b/src/dynamic/stubs/mod.rs
@@ -64,15 +64,15 @@ pub mod redis;
pub mod sql;
pub mod xpath_document;
-pub use broker_kafka::{kafka_source, KAFKA_PUBLISH_MARKER};
-pub use broker_nats::{nats_source, NATS_PUBLISH_MARKER};
-pub use broker_pubsub::{pubsub_source, PUBSUB_PUBLISH_MARKER};
-pub use broker_rabbit::{rabbit_source, RABBIT_PUBLISH_MARKER};
-pub use broker_sqs::{sqs_source, SQS_PUBLISH_MARKER};
+pub use broker_kafka::{KAFKA_PUBLISH_MARKER, kafka_source};
+pub use broker_nats::{NATS_PUBLISH_MARKER, nats_source};
+pub use broker_pubsub::{PUBSUB_PUBLISH_MARKER, pubsub_source};
+pub use broker_rabbit::{RABBIT_PUBLISH_MARKER, rabbit_source};
+pub use broker_sqs::{SQS_PUBLISH_MARKER, sqs_source};
pub use filesystem::FilesystemStub;
pub use http::HttpStub;
pub use ldap_server::LdapStub;
-pub use mocks::{mock_source, MockKind};
+pub use mocks::{MockKind, mock_source};
pub use redis::RedisStub;
pub use sql::SqlStub;
@@ -330,8 +330,8 @@ impl StubHarness {
/// so a per-stub event log keeps insertion order even when multiple
/// stubs interleave writes.
pub(crate) fn monotonic_ns() -> u64 {
- use std::time::Instant;
use std::sync::OnceLock;
+ use std::time::Instant;
static ORIGIN: OnceLock = OnceLock::new();
let origin = *ORIGIN.get_or_init(Instant::now);
origin.elapsed().as_nanos() as u64
@@ -407,11 +407,8 @@ mod tests {
#[test]
fn dedup_repeated_kinds_during_start() {
let dir = TempDir::new().unwrap();
- let h = StubHarness::start(
- &[StubKind::Sql, StubKind::Sql, StubKind::Sql],
- dir.path(),
- )
- .unwrap();
+ let h =
+ StubHarness::start(&[StubKind::Sql, StubKind::Sql, StubKind::Sql], dir.path()).unwrap();
assert_eq!(h.len(), 1, "repeated kinds must be deduped");
}
diff --git a/src/dynamic/stubs/redis.rs b/src/dynamic/stubs/redis.rs
index d2c0dd8c..498c9c86 100644
--- a/src/dynamic/stubs/redis.rs
+++ b/src/dynamic/stubs/redis.rs
@@ -46,7 +46,11 @@ impl RedisStub {
let shutdown_clone = Arc::clone(&shutdown);
std::thread::spawn(move || accept_loop(listener, events_clone, shutdown_clone));
- Ok(Self { port, events, shutdown })
+ Ok(Self {
+ port,
+ events,
+ shutdown,
+ })
}
/// Port the listener is bound to.
@@ -181,7 +185,10 @@ fn read_command(reader: &mut BufReader) -> Option> {
}
fn command_to_event(parts: &[String]) -> StubEvent {
- let (cmd, args) = parts.split_first().map(|(c, a)| (c.as_str(), a)).unwrap_or(("", &[][..]));
+ let (cmd, args) = parts
+ .split_first()
+ .map(|(c, a)| (c.as_str(), a))
+ .unwrap_or(("", &[][..]));
let summary = if args.is_empty() {
cmd.to_owned()
} else {
@@ -250,7 +257,8 @@ mod tests {
let stub = RedisStub::start().unwrap();
let mut s = TcpStream::connect(format!("127.0.0.1:{}", stub.port())).unwrap();
// `GET sessions`
- s.write_all(b"*2\r\n$3\r\nGET\r\n$8\r\nsessions\r\n").unwrap();
+ s.write_all(b"*2\r\n$3\r\nGET\r\n$8\r\nsessions\r\n")
+ .unwrap();
s.flush().unwrap();
let mut reply = [0u8; 5];
let _ = s.read_exact(&mut reply);
diff --git a/src/dynamic/stubs/sql.rs b/src/dynamic/stubs/sql.rs
index 877df929..ff574cb7 100644
--- a/src/dynamic/stubs/sql.rs
+++ b/src/dynamic/stubs/sql.rs
@@ -30,7 +30,7 @@
//! On drop the DB file and the log file are deleted along with the
//! enclosing tempdir handle.
-use super::{monotonic_ns, StubEvent, StubKind, StubProvider};
+use super::{StubEvent, StubKind, StubProvider, monotonic_ns};
use std::fs::OpenOptions;
use std::io::{BufRead, BufReader, Write};
use std::path::{Path, PathBuf};
@@ -60,8 +60,7 @@ impl SqlStub {
/// files. When `workdir` is not writable, falls back to the
/// process-wide temp directory.
pub fn start(workdir: &Path) -> std::io::Result {
- let tempdir = TempDir::new_in(workdir)
- .or_else(|_| TempDir::new())?;
+ let tempdir = TempDir::new_in(workdir).or_else(|_| TempDir::new())?;
let db_path = tempdir.path().join("nyx_sql_stub.db");
let log_path = tempdir.path().join("nyx_sql_stub.queries.log");
@@ -126,7 +125,10 @@ impl StubProvider for SqlStub {
}
fn recording_endpoint(&self) -> Option<(&'static str, String)> {
- Some((SQL_STUB_LOG_ENV_VAR, self.log_path.to_string_lossy().into_owned()))
+ Some((
+ SQL_STUB_LOG_ENV_VAR,
+ self.log_path.to_string_lossy().into_owned(),
+ ))
}
fn drain_events(&self) -> Vec {
@@ -214,7 +216,8 @@ mod tests {
fn record_query_lands_in_drain_events() {
let dir = TempDir::new().unwrap();
let stub = SqlStub::start(dir.path()).unwrap();
- stub.record_query("SELECT * FROM users WHERE id = 1").unwrap();
+ stub.record_query("SELECT * FROM users WHERE id = 1")
+ .unwrap();
let events = stub.drain_events();
assert_eq!(events.len(), 1);
assert_eq!(events[0].kind, StubKind::Sql);
@@ -230,7 +233,8 @@ mod tests {
.append(true)
.open(stub.log_path())
.unwrap();
- f.write_all(b"# driver: psycopg2\nSELECT * FROM accounts\n").unwrap();
+ f.write_all(b"# driver: psycopg2\nSELECT * FROM accounts\n")
+ .unwrap();
drop(f);
let events = stub.drain_events();
diff --git a/src/dynamic/stubs/xpath_document.rs b/src/dynamic/stubs/xpath_document.rs
index 9669de00..04a0926d 100644
--- a/src/dynamic/stubs/xpath_document.rs
+++ b/src/dynamic/stubs/xpath_document.rs
@@ -47,7 +47,10 @@ pub const XPATH_CORPUS_NODE_COUNT: u32 = 3;
/// `(filename, bytes)` pair the harness emitter folds into its
/// [`crate::dynamic::lang::HarnessSource::extra_files`].
pub fn extra_file_pair() -> (String, String) {
- (XPATH_CORPUS_FILENAME.to_owned(), XPATH_CORPUS_XML.to_owned())
+ (
+ XPATH_CORPUS_FILENAME.to_owned(),
+ XPATH_CORPUS_XML.to_owned(),
+ )
}
#[cfg(test)]
diff --git a/src/dynamic/telemetry.rs b/src/dynamic/telemetry.rs
index b82e8f27..5199f1b1 100644
--- a/src/dynamic/telemetry.rs
+++ b/src/dynamic/telemetry.rs
@@ -768,7 +768,10 @@ mod tests {
);
emit(&event);
- assert!(!log.exists(), "log must not be created when NYX_NO_TELEMETRY=1");
+ assert!(
+ !log.exists(),
+ "log must not be created when NYX_NO_TELEMETRY=1"
+ );
unsafe {
std::env::remove_var("NYX_NO_TELEMETRY");
@@ -795,7 +798,9 @@ mod tests {
.unwrap();
let err = read_events(&log).expect_err("schema 0 must be rejected");
match err {
- TelemetryReadError::SchemaMismatch { expected, found, .. } => {
+ TelemetryReadError::SchemaMismatch {
+ expected, found, ..
+ } => {
assert_eq!(expected, SCHEMA_VERSION);
assert_eq!(found, 0);
}
diff --git a/src/dynamic/toolchain.rs b/src/dynamic/toolchain.rs
index 40024506..0dc307aa 100644
--- a/src/dynamic/toolchain.rs
+++ b/src/dynamic/toolchain.rs
@@ -115,10 +115,12 @@ fn try_rust_toolchain_toml(root: &Path) -> Option {
if line.starts_with('[') {
in_toolchain = false;
}
- if in_toolchain && line.starts_with("channel")
- && let Some(ver) = extract_version_from_toml_value(line) {
- return Some(map_rust_version(&ver, RustPinOrigin::RustToolchainToml));
- }
+ if in_toolchain
+ && line.starts_with("channel")
+ && let Some(ver) = extract_version_from_toml_value(line)
+ {
+ return Some(map_rust_version(&ver, RustPinOrigin::RustToolchainToml));
+ }
}
None
}
@@ -138,9 +140,10 @@ fn try_cargo_toml_rust_version(root: &Path) -> Option {
for line in content.lines() {
let line = line.trim();
if line.starts_with("rust-version")
- && let Some(ver) = extract_version_from_toml_value(line) {
- return Some(map_rust_version(&ver, RustPinOrigin::CargoToml));
- }
+ && let Some(ver) = extract_version_from_toml_value(line)
+ {
+ return Some(map_rust_version(&ver, RustPinOrigin::CargoToml));
+ }
}
None
}
@@ -181,7 +184,7 @@ fn map_rust_version(version: &str, origin: RustPinOrigin) -> ToolchainResolution
return ToolchainResolution {
toolchain_id: "rust-nightly".to_owned(),
pin_origin,
- toolchain_drift: true, // nightly != stable reference image
+ toolchain_drift: true, // nightly != stable reference image
version_string: version.to_owned(),
};
}
@@ -246,10 +249,14 @@ fn try_pyproject_toml(root: &Path) -> Option {
// Look for `requires-python = ">=3.11"` or `python = "3.11"`.
for line in content.lines() {
let line = line.trim();
- if (line.starts_with("requires-python") || (line.starts_with("python") && line.contains('=') && !line.starts_with("python_requires")))
- && let Some(ver) = extract_version_from_toml_value(line) {
- return Some(map_version(&ver, PinOrigin::PyprojectToml));
- }
+ if (line.starts_with("requires-python")
+ || (line.starts_with("python")
+ && line.contains('=')
+ && !line.starts_with("python_requires")))
+ && let Some(ver) = extract_version_from_toml_value(line)
+ {
+ return Some(map_version(&ver, PinOrigin::PyprojectToml));
+ }
}
None
}
@@ -266,10 +273,12 @@ fn try_pipfile(root: &Path) -> Option {
if line.starts_with('[') {
in_requires = false;
}
- if in_requires && line.starts_with("python_version")
- && let Some(ver) = extract_version_from_toml_value(line) {
- return Some(map_version(&ver, PinOrigin::Pipfile));
- }
+ if in_requires
+ && line.starts_with("python_version")
+ && let Some(ver) = extract_version_from_toml_value(line)
+ {
+ return Some(map_version(&ver, PinOrigin::Pipfile));
+ }
}
None
}
@@ -331,9 +340,7 @@ fn map_version(version: &str, origin: PinOrigin) -> ToolchainResolution {
("3", Some("12")) => ("python-3.12".to_owned(), false),
("3", Some("13")) => ("python-3.13".to_owned(), false),
// Older 3.x → nearest supported is 3.8
- ("3", Some(m)) if m.parse::().is_ok_and(|v| v < 8) => {
- ("python-3.8".to_owned(), true)
- }
+ ("3", Some(m)) if m.parse::().is_ok_and(|v| v < 8) => ("python-3.8".to_owned(), true),
// Newer 3.x beyond catalog → use 3.13 as closest
("3", Some(_)) => ("python-3.13".to_owned(), true),
("3", None) => ("python-3".to_owned(), false),
@@ -531,12 +538,8 @@ fn map_go_version(version: &str, origin: PinOrigin) -> ToolchainResolution {
("1", Some("21")) => ("go-1.21".to_owned(), false),
("1", Some("22")) => ("go-1.22".to_owned(), false),
("1", Some("23")) => ("go-1.23".to_owned(), false),
- ("1", Some(m)) if m.parse::().is_ok_and(|v| v >= 24) => {
- (format!("go-1.{m}"), true)
- }
- ("1", Some(m)) if m.parse::().is_ok_and(|v| v < 21) => {
- (format!("go-1.{m}"), true)
- }
+ ("1", Some(m)) if m.parse::().is_ok_and(|v| v >= 24) => (format!("go-1.{m}"), true),
+ ("1", Some(m)) if m.parse::().is_ok_and(|v| v < 21) => (format!("go-1.{m}"), true),
_ => ("go-stable".to_owned(), false),
};
@@ -570,14 +573,19 @@ fn try_pom_xml(root: &Path) -> Option {
// Look for 21 or 21
for line in content.lines() {
let trimmed = line.trim();
- for tag in &["", "", ""] {
+ for tag in &[
+ "",
+ "",
+ "",
+ ] {
if trimmed.starts_with(tag)
- && let Some(inner) = trimmed.strip_prefix(tag) {
- let version = inner.split('<').next().unwrap_or("").trim();
- if !version.is_empty() {
- return Some(map_java_version(version, PinOrigin::PomXml));
- }
+ && let Some(inner) = trimmed.strip_prefix(tag)
+ {
+ let version = inner.split('<').next().unwrap_or("").trim();
+ if !version.is_empty() {
+ return Some(map_java_version(version, PinOrigin::PomXml));
}
+ }
}
}
None
@@ -592,10 +600,12 @@ fn try_build_gradle(root: &Path) -> Option {
let trimmed = line.trim();
// Groovy: sourceCompatibility = '21' or JavaVersion.VERSION_21
// Kotlin: sourceCompatibility = JavaVersion.VERSION_21
- if (trimmed.starts_with("sourceCompatibility") || trimmed.starts_with("languageVersion"))
- && let Some(ver) = extract_java_version_from_gradle_line(trimmed) {
- return Some(map_java_version(&ver, PinOrigin::BuildGradle));
- }
+ if (trimmed.starts_with("sourceCompatibility")
+ || trimmed.starts_with("languageVersion"))
+ && let Some(ver) = extract_java_version_from_gradle_line(trimmed)
+ {
+ return Some(map_java_version(&ver, PinOrigin::BuildGradle));
+ }
}
}
None
@@ -606,7 +616,8 @@ fn extract_java_version_from_gradle_line(line: &str) -> Option {
// and: languageVersion.set(JavaLanguageVersion.of(21))
let after_eq = line.split_once('=').map(|x| x.1).unwrap_or(line);
// Try to find a number in the value.
- let digits: String = after_eq.chars()
+ let digits: String = after_eq
+ .chars()
.skip_while(|c| !c.is_ascii_digit())
.take_while(|c| c.is_ascii_digit())
.collect();
@@ -614,9 +625,7 @@ fn extract_java_version_from_gradle_line(line: &str) -> Option {
// Try "VERSION_21" pattern.
if let Some(pos) = after_eq.find("VERSION_") {
let rest = &after_eq[pos + 8..];
- let digits: String = rest.chars()
- .take_while(|c| c.is_ascii_digit())
- .collect();
+ let digits: String = rest.chars().take_while(|c| c.is_ascii_digit()).collect();
if !digits.is_empty() {
return Some(digits);
}
@@ -681,10 +690,12 @@ fn try_composer_json(root: &Path) -> Option {
if json_line_has_key(trimmed, "require") {
in_require = true;
}
- if in_require && trimmed.contains("\"php\"")
- && let Some(ver) = extract_version_from_json_value(trimmed) {
- return Some(map_php_version(&ver, PinOrigin::ComposerJson));
- }
+ if in_require
+ && trimmed.contains("\"php\"")
+ && let Some(ver) = extract_version_from_json_value(trimmed)
+ {
+ return Some(map_php_version(&ver, PinOrigin::ComposerJson));
+ }
// Stop at closing brace of require block.
if in_require && (trimmed == "}," || trimmed == "}") {
in_require = false;
@@ -763,7 +774,11 @@ mod tests {
#[test]
fn pyproject_requires_python() {
let dir = TempDir::new().unwrap();
- fs::write(dir.path().join("pyproject.toml"), "[project]\nrequires-python = \">=3.11\"\n").unwrap();
+ fs::write(
+ dir.path().join("pyproject.toml"),
+ "[project]\nrequires-python = \">=3.11\"\n",
+ )
+ .unwrap();
let r = resolve_python(dir.path());
assert_eq!(r.toolchain_id, "python-3.11");
assert_eq!(r.pin_origin, PinOrigin::PyprojectToml);
@@ -772,7 +787,11 @@ mod tests {
#[test]
fn pipfile_python_version() {
let dir = TempDir::new().unwrap();
- fs::write(dir.path().join("Pipfile"), "[requires]\npython_version = \"3.10\"\n").unwrap();
+ fs::write(
+ dir.path().join("Pipfile"),
+ "[requires]\npython_version = \"3.10\"\n",
+ )
+ .unwrap();
let r = resolve_python(dir.path());
assert_eq!(r.toolchain_id, "python-3.10");
assert_eq!(r.pin_origin, PinOrigin::Pipfile);
@@ -793,7 +812,8 @@ mod tests {
fs::write(
dir.path().join("rust-toolchain.toml"),
"[toolchain]\nchannel = \"stable\"\n",
- ).unwrap();
+ )
+ .unwrap();
let r = resolve_rust(dir.path());
assert_eq!(r.toolchain_id, "rust-stable");
assert!(!r.toolchain_drift);
@@ -816,7 +836,8 @@ mod tests {
fs::write(
dir.path().join("Cargo.toml"),
"[package]\nname = \"foo\"\nrust-version = \"1.75\"\n",
- ).unwrap();
+ )
+ .unwrap();
let r = resolve_rust(dir.path());
assert_eq!(r.pin_origin, PinOrigin::CargoToml);
assert!(r.toolchain_id.starts_with("rust-1"));
@@ -848,7 +869,8 @@ mod tests {
fs::write(
dir.path().join("package.json"),
r#"{"engines": {"node": ">=18.0.0"}}"#,
- ).unwrap();
+ )
+ .unwrap();
let r = resolve_node(dir.path());
assert_eq!(r.toolchain_id, "node-18");
}
@@ -866,7 +888,11 @@ mod tests {
#[test]
fn go_mod_version() {
let dir = TempDir::new().unwrap();
- fs::write(dir.path().join("go.mod"), "module example.com/app\n\ngo 1.22\n").unwrap();
+ fs::write(
+ dir.path().join("go.mod"),
+ "module example.com/app\n\ngo 1.22\n",
+ )
+ .unwrap();
let r = resolve_go(dir.path());
assert_eq!(r.toolchain_id, "go-1.22");
assert!(!r.toolchain_drift);
@@ -902,7 +928,8 @@ mod tests {
fs::write(
dir.path().join("build.gradle"),
"sourceCompatibility = '17'\ntargetCompatibility = '17'\n",
- ).unwrap();
+ )
+ .unwrap();
let r = resolve_java(dir.path());
assert_eq!(r.toolchain_id, "java-17");
assert_eq!(r.pin_origin, PinOrigin::BuildGradle);
@@ -924,7 +951,8 @@ mod tests {
fs::write(
dir.path().join("composer.json"),
r#"{"require": {"php": ">=8.1"}}"#,
- ).unwrap();
+ )
+ .unwrap();
let r = resolve_php(dir.path());
assert_eq!(r.toolchain_id, "php-8.1");
assert_eq!(r.pin_origin, PinOrigin::ComposerJson);
@@ -982,7 +1010,10 @@ mod tests {
#[test]
fn json_line_has_key_rejects_key_in_value() {
assert!(!json_line_has_key(r#" "type": "require","#, "require"));
- assert!(!json_line_has_key(r#" "desc": "engines config","#, "engines"));
+ assert!(!json_line_has_key(
+ r#" "desc": "engines config","#,
+ "engines"
+ ));
}
#[test]
diff --git a/src/dynamic/trace.rs b/src/dynamic/trace.rs
index b4a45dc7..94d4fe6d 100644
--- a/src/dynamic/trace.rs
+++ b/src/dynamic/trace.rs
@@ -208,7 +208,10 @@ mod tests {
#[test]
fn jsonl_round_trips_through_serde() {
let t = VerifyTrace::new();
- t.record(TraceStage::SandboxStarted, Some("payload=sqli-tautology".to_owned()));
+ t.record(
+ TraceStage::SandboxStarted,
+ Some("payload=sqli-tautology".to_owned()),
+ );
t.record(TraceStage::OracleObserved, Some("fired=true".to_owned()));
let jsonl = t.to_jsonl();
let mut parsed = Vec::new();
diff --git a/src/dynamic/verify.rs b/src/dynamic/verify.rs
index 44febb6c..0d4f5c68 100644
--- a/src/dynamic/verify.rs
+++ b/src/dynamic/verify.rs
@@ -5,18 +5,20 @@
use crate::callgraph::CallGraph;
use crate::commands::scan::Diag;
-use crate::dynamic::corpus::{payloads_for, CORPUS_VERSION};
+use crate::dynamic::corpus::{CORPUS_VERSION, payloads_for};
use crate::dynamic::oob::OobListener;
use crate::dynamic::report::{AttemptSummary, VerifyResult, VerifyStatus};
-use crate::dynamic::runner::{run_spec, RunError};
-use crate::dynamic::sandbox::{toolchain_id_with_digest, SandboxOptions};
+use crate::dynamic::runner::{RunError, run_spec};
+use crate::dynamic::sandbox::{SandboxOptions, toolchain_id_with_digest};
use crate::dynamic::spec::{HarnessSpec, SPEC_FORMAT_VERSION};
use crate::dynamic::stubs::StubHarness;
use crate::dynamic::telemetry::{self, SamplingPolicy, TelemetryEvent};
use crate::dynamic::toolchain;
-use crate::evidence::{HardeningSummary, InconclusiveReason, SpecDerivationStrategy, UnsupportedReason};
#[cfg(target_os = "linux")]
use crate::evidence::HardeningPrimitive;
+use crate::evidence::{
+ HardeningSummary, InconclusiveReason, SpecDerivationStrategy, UnsupportedReason,
+};
use crate::summary::GlobalSummaries;
use crate::utils::config::Config;
use std::path::Path;
@@ -208,10 +210,7 @@ impl VerifyOptions {
/// [`verify_finding`].
fn lang_needs_host_libs(lang: crate::symbol::Lang) -> bool {
use crate::symbol::Lang::*;
- matches!(
- lang,
- Python | JavaScript | TypeScript | Java | Ruby | Php
- )
+ matches!(lang, Python | JavaScript | TypeScript | Java | Ruby | Php)
}
// ── Dynamic verdict cache helpers (§12 Q5) ───────────────────────────────────
@@ -391,8 +390,7 @@ fn spec_derivation_failed_verdict(
policy: &SamplingPolicy,
) -> VerifyResult {
if matches!(reason, UnsupportedReason::SpecDerivationFailed) && should_be_inconclusive(diag) {
- let strategies: Vec =
- HarnessSpec::derivation_strategies().to_vec();
+ let strategies: Vec = HarnessSpec::derivation_strategies().to_vec();
let hint = derivation_failure_hint(diag);
let inconclusive_reason = InconclusiveReason::SpecDerivationFailed {
tried: strategies,
@@ -542,9 +540,7 @@ pub fn verify_finding(diag: &Diag, opts: &VerifyOptions) -> VerifyResult {
triggered_payload: None,
reason: None,
inconclusive_reason: Some(inconclusive_reason),
- detail: Some(format!(
- "dynamic execution refused by policy rule {rule}"
- )),
+ detail: Some(format!("dynamic execution refused by policy rule {rule}")),
attempts: vec![],
toolchain_match: None,
differential: None,
@@ -626,9 +622,7 @@ pub fn verify_finding(diag: &Diag, opts: &VerifyOptions) -> VerifyResult {
// structured `Inconclusive(BackendInsufficient)` so operators see
// the backend gap instead of a quiet `Confirmed` against an
// unhardened host.
- if opts.refuse_filesystem_confirm
- && spec.expected_cap.contains(crate::labels::Cap::FILE_IO)
- {
+ if opts.refuse_filesystem_confirm && spec.expected_cap.contains(crate::labels::Cap::FILE_IO) {
let backend = if cfg!(target_os = "macos") {
"macos-process-without-sandbox-exec"
} else {
@@ -701,7 +695,11 @@ pub fn verify_finding(diag: &Diag, opts: &VerifyOptions) -> VerifyResult {
Lang::Php => toolchain::resolve_php(Path::new(".")),
_ => toolchain::resolve_python(Path::new(".")),
};
- let toolchain_match = if toolchain_res.toolchain_drift { "drift" } else { "exact" };
+ let toolchain_match = if toolchain_res.toolchain_drift {
+ "drift"
+ } else {
+ "exact"
+ };
// Enrich the resolved toolchain_id with the Docker image digest (§22.1).
// The enriched ID is used as the toolchain_id component of the verdict cache
// key so that image updates always invalidate stale cache entries.
@@ -717,9 +715,10 @@ pub fn verify_finding(diag: &Diag, opts: &VerifyOptions) -> VerifyResult {
&entry_hash,
import_digest,
&effective_toolchain_id,
- ) {
- return cached;
- }
+ )
+ {
+ return cached;
+ }
// Phase 10 (Track D.3): spawn the boundary stubs the spec
// demands *before* the sandbox runs. When `stubs_required` is
@@ -787,14 +786,7 @@ pub fn verify_finding(diag: &Diag, opts: &VerifyOptions) -> VerifyResult {
_ => 1,
};
- let mut verdict = build_verdict(
- &finding_id,
- &spec,
- result,
- toolchain_match,
- opts,
- elapsed,
- );
+ let mut verdict = build_verdict(&finding_id, &spec, result, toolchain_match, opts, elapsed);
// Phase 29 follow-up: stamp `replay_stable` from a `reproduce.sh` rerun
// against the freshly written bundle. Opt-in (see
@@ -807,7 +799,11 @@ pub fn verify_finding(diag: &Diag, opts: &VerifyOptions) -> VerifyResult {
&& let Some(bundle) = crate::dynamic::repro::bundle_root_for(&spec.spec_hash)
&& bundle.join("reproduce.sh").exists()
{
- let replay_args: &[&str] = if opts.replay_use_docker { &["--docker"] } else { &[] };
+ let replay_args: &[&str] = if opts.replay_use_docker {
+ &["--docker"]
+ } else {
+ &[]
+ };
let replay = crate::dynamic::repro::replay_bundle(&bundle, replay_args);
verdict.replay_stable = crate::dynamic::repro::replay_stability(&replay);
}
@@ -849,7 +845,6 @@ pub fn verify_finding(diag: &Diag, opts: &VerifyOptions) -> VerifyResult {
verdict
}
-
/// Project the platform-cfg'd [`crate::dynamic::sandbox::HardeningRecord`]
/// into the portable [`HardeningSummary`] that lands on
/// [`VerifyResult::hardening_outcome`]. Returns `None` when the run did
@@ -961,10 +956,7 @@ fn build_verdict(
let triggered_payload = run.attempts[i].payload_label.to_string();
let payloads = payloads_for(spec.expected_cap);
let vuln_payloads: Vec<_> = payloads.iter().filter(|p| !p.is_benign).collect();
- let payload_bytes = vuln_payloads
- .get(i)
- .map(|p| p.bytes)
- .unwrap_or(b"");
+ let payload_bytes = vuln_payloads.get(i).map(|p| p.bytes).unwrap_or(b"");
let hardening_outcome = summarize_hardening(&run.attempts[i].outcome);
// Emit repro artifact.
@@ -1223,7 +1215,10 @@ fn build_verdict(
// (cf. §10 decision 14 and the verify_result_json_shape contract).
let (reason, detail) = match &e {
crate::dynamic::harness::HarnessError::Unsupported(r) => (Some(r.clone()), None),
- _ => (Some(UnsupportedReason::BackendUnavailable), Some(format!("{e}"))),
+ _ => (
+ Some(UnsupportedReason::BackendUnavailable),
+ Some(format!("{e}")),
+ ),
};
VerifyResult {
finding_id: finding_id.to_owned(),
@@ -1240,7 +1235,10 @@ fn build_verdict(
hardening_outcome: None,
}
}
- Err(RunError::BuildFailed { stderr, attempts: build_att }) => VerifyResult {
+ Err(RunError::BuildFailed {
+ stderr,
+ attempts: build_att,
+ }) => VerifyResult {
finding_id: finding_id.to_owned(),
status: VerifyStatus::Inconclusive,
triggered_payload: None,
@@ -1385,7 +1383,10 @@ mod tests {
use crate::dynamic::sandbox::ProcessHardeningProfile;
let opts = VerifyOptions::from_config(&Config::default());
assert!(
- matches!(opts.sandbox.process_hardening, ProcessHardeningProfile::Standard),
+ matches!(
+ opts.sandbox.process_hardening,
+ ProcessHardeningProfile::Standard
+ ),
"back-compat: missing harden_profile must keep the Standard baseline so \
existing call sites (process backend without `--harden=strict`) keep \
their pre-Phase-17 hardening matrix"
@@ -1399,7 +1400,10 @@ mod tests {
config.scanner.harden_profile = "strict".to_owned();
let opts = VerifyOptions::from_config(&config);
assert!(
- matches!(opts.sandbox.process_hardening, ProcessHardeningProfile::Strict),
+ matches!(
+ opts.sandbox.process_hardening,
+ ProcessHardeningProfile::Strict
+ ),
"harden_profile=strict must engage the full Phase-17/18 lockdown so \
`--harden=strict` actually wraps the harness with sandbox-exec on macOS \
and layers chroot + seccomp on Linux"
@@ -1451,7 +1455,10 @@ mod tests {
config.scanner.harden_profile = "lockdown".to_owned();
let opts = VerifyOptions::from_config(&config);
assert!(
- matches!(opts.sandbox.process_hardening, ProcessHardeningProfile::Standard),
+ matches!(
+ opts.sandbox.process_hardening,
+ ProcessHardeningProfile::Standard
+ ),
"unknown harden_profile values must degrade to Standard so a typo in \
nyx.toml does not silently leave the operator without the baseline \
hardening they were already paying for"
@@ -1680,7 +1687,14 @@ mod tests {
);
// Insert with current CORPUS_VERSION → must be a HIT.
- insert_verdict_cache(&db_path, "spec_stale", "hash_stale", "", "python-3.11", &result);
+ insert_verdict_cache(
+ &db_path,
+ "spec_stale",
+ "hash_stale",
+ "",
+ "python-3.11",
+ &result,
+ );
let hit = lookup_verdict_cache(&db_path, "spec_stale", "hash_stale", "", "python-3.11");
assert!(
hit.is_some(),
diff --git a/src/evidence.rs b/src/evidence.rs
index 74f411f6..a56278ba 100644
--- a/src/evidence.rs
+++ b/src/evidence.rs
@@ -311,10 +311,7 @@ pub enum EntryKind {
/// class name and the method to drive so the lang emitter can build
/// a `Cls().method()` invocation. Land in
/// Phase 19.
- ClassMethod {
- class: String,
- method: String,
- },
+ ClassMethod { class: String, method: String },
/// Message-queue subscriber / consumer. `queue` is the topic /
/// stream / channel name; `message_schema`, when present, is a
/// free-form JSON description of the expected message body that the
@@ -335,23 +332,16 @@ pub enum EntryKind {
},
/// GraphQL resolver — `type_name.field` pair the harness drives via
/// an in-process GraphQL execution layer. Land in Phase 21.
- GraphQLResolver {
- type_name: String,
- field: String,
- },
+ GraphQLResolver { type_name: String, field: String },
/// WebSocket handler — `path` is the canonical mount point; the
/// harness opens a loopback ws connection and sends the payload as
/// the first message frame. Land in Phase 21.
- WebSocket {
- path: String,
- },
+ WebSocket { path: String },
/// HTTP / framework middleware — `name` is the middleware identifier
/// (class name, function name, registration key) the harness mounts
/// on a synthetic pipeline before invoking it with a crafted
/// request. Land in Phase 21.
- Middleware {
- name: String,
- },
+ Middleware { name: String },
/// Database migration / schema-change script — `version`, when
/// present, is the migration revision identifier (Alembic / Flyway /
/// Rails string) so the harness can pin the apply step. Land in
@@ -408,8 +398,7 @@ impl<'de> Deserialize<'de> for EntryKind {
{
use serde::de::Error as _;
- let value = serde_json::Value::deserialize(deserializer)
- .map_err(D::Error::custom)?;
+ let value = serde_json::Value::deserialize(deserializer).map_err(D::Error::custom)?;
// Bare-string form (legacy unit variants).
if let Some(tag) = value.as_str() {
@@ -440,10 +429,12 @@ impl<'de> Deserialize<'de> for EntryKind {
class: String,
method: String,
}
- serde_json::from_value::(body).ok().map(|f| Self::ClassMethod {
- class: f.class,
- method: f.method,
- })
+ serde_json::from_value::(body)
+ .ok()
+ .map(|f| Self::ClassMethod {
+ class: f.class,
+ method: f.method,
+ })
}
"MessageHandler" => {
#[derive(Deserialize)]
@@ -452,10 +443,12 @@ impl<'de> Deserialize<'de> for EntryKind {
#[serde(default)]
message_schema: Option,
}
- serde_json::from_value::(body).ok().map(|f| Self::MessageHandler {
- queue: f.queue,
- message_schema: f.message_schema,
- })
+ serde_json::from_value::(body)
+ .ok()
+ .map(|f| Self::MessageHandler {
+ queue: f.queue,
+ message_schema: f.message_schema,
+ })
}
"ScheduledJob" => {
#[derive(Deserialize)]
@@ -465,7 +458,9 @@ impl<'de> Deserialize<'de> for EntryKind {
}
serde_json::from_value::(body)
.ok()
- .map(|f| Self::ScheduledJob { schedule: f.schedule })
+ .map(|f| Self::ScheduledJob {
+ schedule: f.schedule,
+ })
}
"GraphQLResolver" => {
#[derive(Deserialize)]
@@ -473,10 +468,12 @@ impl<'de> Deserialize<'de> for EntryKind {
type_name: String,
field: String,
}
- serde_json::from_value::(body).ok().map(|f| Self::GraphQLResolver {
- type_name: f.type_name,
- field: f.field,
- })
+ serde_json::from_value::(body)
+ .ok()
+ .map(|f| Self::GraphQLResolver {
+ type_name: f.type_name,
+ field: f.field,
+ })
}
"WebSocket" => {
#[derive(Deserialize)]
@@ -692,9 +689,7 @@ impl fmt::Display for InconclusiveReason {
Self::ReversedDifferential => f.write_str(
"reversed differential (benign payload fired, vulnerable payload did not)",
),
- Self::UnrelatedCrash => {
- f.write_str("harness crashed outside the instrumented sink")
- }
+ Self::UnrelatedCrash => f.write_str("harness crashed outside the instrumented sink"),
Self::BackendInsufficient {
backend,
oracle_kind,
@@ -2248,8 +2243,12 @@ mod tests {
type_name: "Query".into(),
field: "user".into(),
},
- EntryKind::WebSocket { path: "/ws/feed".into() },
- EntryKind::Middleware { name: "auth_filter".into() },
+ EntryKind::WebSocket {
+ path: "/ws/feed".into(),
+ },
+ EntryKind::Middleware {
+ name: "auth_filter".into(),
+ },
EntryKind::Migration {
version: Some("0042_user_table".into()),
},
diff --git a/src/fmt.rs b/src/fmt.rs
index 4072e793..aeeba356 100644
--- a/src/fmt.rs
+++ b/src/fmt.rs
@@ -260,7 +260,9 @@ fn render_chains(chains: &[ChainFinding], _width: usize) -> String {
let mut out = String::new();
out.push_str(&format!(
"{}\n",
- style(format!("Chains ({})", chains.len())).bold().underlined()
+ style(format!("Chains ({})", chains.len()))
+ .bold()
+ .underlined()
));
for c in chains {
let sev = chain_severity_tag(c.severity);
@@ -301,7 +303,11 @@ fn render_chains(chains: &[ChainFinding], _width: usize) -> String {
fn chain_severity_tag(s: crate::chain::finding::ChainSeverity) -> String {
use crate::chain::finding::ChainSeverity;
match s {
- ChainSeverity::Critical => format!("{} {}", style("✖").red().bold(), style("[CRITICAL]").red().bold()),
+ ChainSeverity::Critical => format!(
+ "{} {}",
+ style("✖").red().bold(),
+ style("[CRITICAL]").red().bold()
+ ),
ChainSeverity::High => format!("{} {}", style("✖").red(), style("[HIGH]").red()),
ChainSeverity::Medium => format!("{} {}", style("⚠").yellow(), style("[MEDIUM]").yellow()),
ChainSeverity::Low => format!("{} {}", style("●").dim(), style("[LOW]").dim()),
@@ -609,14 +615,15 @@ fn format_inconclusive_reason(r: &crate::evidence::InconclusiveReason) -> String
supported,
..
} => {
- format!(
- "entry kind {attempted} unsupported for {lang:?} (supported: {supported:?})"
- )
+ format!("entry kind {attempted} unsupported for {lang:?} (supported: {supported:?})")
}
InconclusiveReason::NoBenignControl => "no benign control payload".to_string(),
InconclusiveReason::ReversedDifferential => "reversed differential".to_string(),
InconclusiveReason::UnrelatedCrash => "unrelated crash (not sink-site)".to_string(),
- InconclusiveReason::BackendInsufficient { backend, oracle_kind } => {
+ InconclusiveReason::BackendInsufficient {
+ backend,
+ oracle_kind,
+ } => {
format!("backend {backend} cannot enforce {oracle_kind} oracle")
}
InconclusiveReason::PolicyDeniedDynamic { rule, .. } => {
diff --git a/src/output/sarif.rs b/src/output/sarif.rs
index 58f8e6c5..8c9ce82f 100644
--- a/src/output/sarif.rs
+++ b/src/output/sarif.rs
@@ -117,11 +117,7 @@ pub fn build_sarif(diags: &[Diag], scan_root: &Path) -> Value {
/// process the diagnostics. When the slice is empty the
/// `properties.chains` array is still emitted (as `[]`) so consumers
/// can rely on the key existing.
-pub fn build_sarif_with_chains(
- diags: &[Diag],
- chains: &[ChainFinding],
- scan_root: &Path,
-) -> Value {
+pub fn build_sarif_with_chains(diags: &[Diag], chains: &[ChainFinding], scan_root: &Path) -> Value {
let mut rule_ids: Vec = Vec::new();
let mut rule_index_map: HashMap = HashMap::new();
@@ -270,7 +266,11 @@ pub fn build_sarif_with_chains(
}
}
- if let Some(dv) = d.evidence.as_ref().and_then(|ev| ev.dynamic_verdict.as_ref()) {
+ if let Some(dv) = d
+ .evidence
+ .as_ref()
+ .and_then(|ev| ev.dynamic_verdict.as_ref())
+ {
result["partialFingerprints"] = json!({
"dynamic_verdict_status": serde_json::to_value(dv.status)
.unwrap_or(Value::Null)
@@ -316,9 +316,10 @@ pub fn build_sarif_with_chains(
// reruns because both the finding's `stable_hash` and the
// chain's `stable_hash` are byte-deterministic.
if d.stable_hash != 0
- && let Some(chain_hash) = chain_member_of.get(&d.stable_hash) {
- props.insert("chain_member_of".into(), json!(chain_hash));
- }
+ && let Some(chain_hash) = chain_member_of.get(&d.stable_hash)
+ {
+ props.insert("chain_member_of".into(), json!(chain_hash));
+ }
result["properties"] = Value::Object(props);
@@ -448,13 +449,19 @@ mod tests {
#[test]
fn rule_description_taint_prefix_returns_fallback() {
let desc = rule_description("taint-unsanitised-flow");
- assert!(desc.contains("Unsanitised"), "expected taint fallback, got: {desc}");
+ assert!(
+ desc.contains("Unsanitised"),
+ "expected taint fallback, got: {desc}"
+ );
}
#[test]
fn rule_description_taint_with_suffix_normalises_to_base() {
let desc = rule_description("taint-unsanitised-flow:foo.rs:42");
- assert!(desc.contains("Unsanitised"), "expected taint fallback, got: {desc}");
+ assert!(
+ desc.contains("Unsanitised"),
+ "expected taint fallback, got: {desc}"
+ );
}
#[test]
diff --git a/src/output/severity.rs b/src/output/severity.rs
index 854993c5..0c1aa614 100644
--- a/src/output/severity.rs
+++ b/src/output/severity.rs
@@ -98,14 +98,20 @@ mod tests {
#[test]
fn browser_local_rce_is_critical() {
assert_eq!(
- chain_severity(ImpactCategory::BrowserToLocalRce, &[edge(Feasibility::Confirmed)]),
+ chain_severity(
+ ImpactCategory::BrowserToLocalRce,
+ &[edge(Feasibility::Confirmed)]
+ ),
ChainSeverity::Critical,
);
}
#[test]
fn session_hijack_downgrades_on_all_unverified() {
- let confirmed = chain_severity(ImpactCategory::SessionHijack, &[edge(Feasibility::Confirmed)]);
+ let confirmed = chain_severity(
+ ImpactCategory::SessionHijack,
+ &[edge(Feasibility::Confirmed)],
+ );
assert_eq!(confirmed, ChainSeverity::High);
let unverified = chain_severity(
ImpactCategory::SessionHijack,
diff --git a/src/rank.rs b/src/rank.rs
index 3dd8e095..4d0ef69f 100644
--- a/src/rank.rs
+++ b/src/rank.rs
@@ -222,11 +222,7 @@ pub fn rank_diags(diags: &mut [Diag]) {
.and_then(|ev| ev.dynamic_verdict.as_ref())
.map(|dv| format!("{:?}", dv.status))
.unwrap_or_default();
- telemetry::emit_rank_delta(RankDeltaEvent::new(
- d.finding_id.clone(),
- status,
- delta,
- ));
+ telemetry::emit_rank_delta(RankDeltaEvent::new(d.finding_id.clone(), status, delta));
}
}
diags.sort_by(|a, b| {
diff --git a/src/server/routes/surface.rs b/src/server/routes/surface.rs
index fd35490f..155ca42e 100644
--- a/src/server/routes/surface.rs
+++ b/src/server/routes/surface.rs
@@ -26,14 +26,13 @@ async fn get_surface(State(state): State) -> ApiResult> {
// Building the surface map can do filesystem IO + tree-sitter
// parsing; keep it off the async runtime.
- let join_result = tokio::task::spawn_blocking(move || {
- load_or_build(&scan_root, &database_dir, &cfg)
- })
- .await
- .map_err(|e| ApiError::internal(format!("surface map task failed: {e}")))?;
+ let join_result =
+ tokio::task::spawn_blocking(move || load_or_build(&scan_root, &database_dir, &cfg))
+ .await
+ .map_err(|e| ApiError::internal(format!("surface map task failed: {e}")))?;
- let mut map = join_result
- .map_err(|e| ApiError::internal(format!("failed to build surface map: {e}")))?;
+ let mut map =
+ join_result.map_err(|e| ApiError::internal(format!("failed to build surface map: {e}")))?;
let bytes = map
.to_json()
.map_err(|e| ApiError::internal(format!("encode surface map: {e}")))?;
diff --git a/src/surface/build.rs b/src/surface/build.rs
index 89fb7605..dffa9676 100644
--- a/src/surface/build.rs
+++ b/src/surface/build.rs
@@ -29,9 +29,9 @@ use crate::summary::GlobalSummaries;
use crate::surface::{
SurfaceMap, dangerous, datastore, external,
lang::{
- go_gin, go_http, java_quarkus, java_servlet, java_spring, js_express, js_koa,
- php_laravel, php_slim, python_django, python_fastapi, python_flask,
- ruby_rails, ruby_sinatra, rust_actix, rust_axum, ts_next,
+ go_gin, go_http, java_quarkus, java_servlet, java_spring, js_express, js_koa, php_laravel,
+ php_slim, python_django, python_fastapi, python_flask, ruby_rails, ruby_sinatra,
+ rust_actix, rust_axum, ts_next,
},
reachability,
};
@@ -63,12 +63,8 @@ pub fn build_surface_map(inputs: &SurfaceBuildInputs<'_>) -> SurfaceMap {
.as_mut()
.and_then(|p| p.parse(&bytes, None))
.map(|tree| {
- let mut all = python_flask::detect_flask_routes(
- &tree,
- &bytes,
- path,
- inputs.scan_root,
- );
+ let mut all =
+ python_flask::detect_flask_routes(&tree, &bytes, path, inputs.scan_root);
all.extend(python_fastapi::detect_fastapi_routes(
&tree,
&bytes,
@@ -165,12 +161,8 @@ pub fn build_surface_map(inputs: &SurfaceBuildInputs<'_>) -> SurfaceMap {
.as_mut()
.and_then(|p| p.parse(&bytes, None))
.map(|tree| {
- let mut all = php_laravel::detect_laravel_routes(
- &tree,
- &bytes,
- path,
- inputs.scan_root,
- );
+ let mut all =
+ php_laravel::detect_laravel_routes(&tree, &bytes, path, inputs.scan_root);
all.extend(php_slim::detect_slim_routes(
&tree,
&bytes,
@@ -185,12 +177,8 @@ pub fn build_surface_map(inputs: &SurfaceBuildInputs<'_>) -> SurfaceMap {
.as_mut()
.and_then(|p| p.parse(&bytes, None))
.map(|tree| {
- let mut all = ruby_sinatra::detect_sinatra_routes(
- &tree,
- &bytes,
- path,
- inputs.scan_root,
- );
+ let mut all =
+ ruby_sinatra::detect_sinatra_routes(&tree, &bytes, path, inputs.scan_root);
all.extend(ruby_rails::detect_rails_routes(
&tree,
&bytes,
@@ -435,13 +423,15 @@ def evaluator():
let files = vec![py];
let inputs = empty_inputs(&files, Some(dir.path()), &gs, &cg, &cfg);
let map = build_surface_map(&inputs);
- assert!(map
- .nodes
- .iter()
- .any(|n| matches!(n, SurfaceNode::DangerousLocal(_))));
- assert!(map
- .edges
- .iter()
- .any(|e| matches!(e.kind, crate::surface::EdgeKind::Reaches)));
+ assert!(
+ map.nodes
+ .iter()
+ .any(|n| matches!(n, SurfaceNode::DangerousLocal(_)))
+ );
+ assert!(
+ map.edges
+ .iter()
+ .any(|e| matches!(e.kind, crate::surface::EdgeKind::Reaches))
+ );
}
}
diff --git a/src/surface/datastore.rs b/src/surface/datastore.rs
index 574a829e..b1368bc3 100644
--- a/src/surface/datastore.rs
+++ b/src/surface/datastore.rs
@@ -28,86 +28,314 @@ struct DriverRule {
const DRIVER_RULES: &[DriverRule] = &[
// Python — relational
- DriverRule { leaf: "psycopg2.connect", kind: DataStoreKind::Sql, label: "PostgreSQL (psycopg2)" },
- DriverRule { leaf: "psycopg.connect", kind: DataStoreKind::Sql, label: "PostgreSQL (psycopg3)" },
- DriverRule { leaf: "mysql.connector.connect", kind: DataStoreKind::Sql, label: "MySQL (mysql.connector)" },
- DriverRule { leaf: "MySQLdb.connect", kind: DataStoreKind::Sql, label: "MySQL (MySQLdb)" },
- DriverRule { leaf: "pymysql.connect", kind: DataStoreKind::Sql, label: "MySQL (PyMySQL)" },
- DriverRule { leaf: "sqlite3.connect", kind: DataStoreKind::Sql, label: "SQLite (sqlite3)" },
- DriverRule { leaf: "sqlalchemy.create_engine", kind: DataStoreKind::Sql, label: "SQLAlchemy" },
- DriverRule { leaf: "django.db.connection", kind: DataStoreKind::Sql, label: "Django ORM" },
+ DriverRule {
+ leaf: "psycopg2.connect",
+ kind: DataStoreKind::Sql,
+ label: "PostgreSQL (psycopg2)",
+ },
+ DriverRule {
+ leaf: "psycopg.connect",
+ kind: DataStoreKind::Sql,
+ label: "PostgreSQL (psycopg3)",
+ },
+ DriverRule {
+ leaf: "mysql.connector.connect",
+ kind: DataStoreKind::Sql,
+ label: "MySQL (mysql.connector)",
+ },
+ DriverRule {
+ leaf: "MySQLdb.connect",
+ kind: DataStoreKind::Sql,
+ label: "MySQL (MySQLdb)",
+ },
+ DriverRule {
+ leaf: "pymysql.connect",
+ kind: DataStoreKind::Sql,
+ label: "MySQL (PyMySQL)",
+ },
+ DriverRule {
+ leaf: "sqlite3.connect",
+ kind: DataStoreKind::Sql,
+ label: "SQLite (sqlite3)",
+ },
+ DriverRule {
+ leaf: "sqlalchemy.create_engine",
+ kind: DataStoreKind::Sql,
+ label: "SQLAlchemy",
+ },
+ DriverRule {
+ leaf: "django.db.connection",
+ kind: DataStoreKind::Sql,
+ label: "Django ORM",
+ },
// Python — kv / doc
- DriverRule { leaf: "redis.Redis", kind: DataStoreKind::KeyValue, label: "Redis" },
- DriverRule { leaf: "redis.from_url", kind: DataStoreKind::KeyValue, label: "Redis" },
- DriverRule { leaf: "pymongo.MongoClient", kind: DataStoreKind::Document, label: "MongoDB" },
- DriverRule { leaf: "boto3.client", kind: DataStoreKind::BlobStore, label: "AWS (boto3)" },
- DriverRule { leaf: "boto3.resource", kind: DataStoreKind::BlobStore, label: "AWS (boto3)" },
-
+ DriverRule {
+ leaf: "redis.Redis",
+ kind: DataStoreKind::KeyValue,
+ label: "Redis",
+ },
+ DriverRule {
+ leaf: "redis.from_url",
+ kind: DataStoreKind::KeyValue,
+ label: "Redis",
+ },
+ DriverRule {
+ leaf: "pymongo.MongoClient",
+ kind: DataStoreKind::Document,
+ label: "MongoDB",
+ },
+ DriverRule {
+ leaf: "boto3.client",
+ kind: DataStoreKind::BlobStore,
+ label: "AWS (boto3)",
+ },
+ DriverRule {
+ leaf: "boto3.resource",
+ kind: DataStoreKind::BlobStore,
+ label: "AWS (boto3)",
+ },
// JavaScript / TypeScript — relational
- DriverRule { leaf: "knex", kind: DataStoreKind::Sql, label: "Knex.js" },
- DriverRule { leaf: "createConnection", kind: DataStoreKind::Sql, label: "MySQL/Postgres (mysql/pg)" },
- DriverRule { leaf: "Sequelize", kind: DataStoreKind::Sql, label: "Sequelize" },
- DriverRule { leaf: "TypeORM.createConnection", kind: DataStoreKind::Sql, label: "TypeORM" },
- DriverRule { leaf: "PrismaClient", kind: DataStoreKind::Sql, label: "Prisma" },
- DriverRule { leaf: "pool.query", kind: DataStoreKind::Sql, label: "pg/mysql pool" },
- DriverRule { leaf: "client.query", kind: DataStoreKind::Sql, label: "pg client" },
- DriverRule { leaf: "db.query", kind: DataStoreKind::Sql, label: "Generic SQL driver" },
+ DriverRule {
+ leaf: "knex",
+ kind: DataStoreKind::Sql,
+ label: "Knex.js",
+ },
+ DriverRule {
+ leaf: "createConnection",
+ kind: DataStoreKind::Sql,
+ label: "MySQL/Postgres (mysql/pg)",
+ },
+ DriverRule {
+ leaf: "Sequelize",
+ kind: DataStoreKind::Sql,
+ label: "Sequelize",
+ },
+ DriverRule {
+ leaf: "TypeORM.createConnection",
+ kind: DataStoreKind::Sql,
+ label: "TypeORM",
+ },
+ DriverRule {
+ leaf: "PrismaClient",
+ kind: DataStoreKind::Sql,
+ label: "Prisma",
+ },
+ DriverRule {
+ leaf: "pool.query",
+ kind: DataStoreKind::Sql,
+ label: "pg/mysql pool",
+ },
+ DriverRule {
+ leaf: "client.query",
+ kind: DataStoreKind::Sql,
+ label: "pg client",
+ },
+ DriverRule {
+ leaf: "db.query",
+ kind: DataStoreKind::Sql,
+ label: "Generic SQL driver",
+ },
// JS — kv / doc
- DriverRule { leaf: "redis.createClient", kind: DataStoreKind::KeyValue, label: "Redis (node-redis)" },
- DriverRule { leaf: "ioredis", kind: DataStoreKind::KeyValue, label: "ioredis" },
- DriverRule { leaf: "MongoClient.connect", kind: DataStoreKind::Document, label: "MongoDB (node)" },
- DriverRule { leaf: "AWS.S3", kind: DataStoreKind::BlobStore, label: "AWS S3" },
-
+ DriverRule {
+ leaf: "redis.createClient",
+ kind: DataStoreKind::KeyValue,
+ label: "Redis (node-redis)",
+ },
+ DriverRule {
+ leaf: "ioredis",
+ kind: DataStoreKind::KeyValue,
+ label: "ioredis",
+ },
+ DriverRule {
+ leaf: "MongoClient.connect",
+ kind: DataStoreKind::Document,
+ label: "MongoDB (node)",
+ },
+ DriverRule {
+ leaf: "AWS.S3",
+ kind: DataStoreKind::BlobStore,
+ label: "AWS S3",
+ },
// Java — JDBC / Hibernate
- DriverRule { leaf: "DriverManager.getConnection", kind: DataStoreKind::Sql, label: "JDBC" },
- DriverRule { leaf: "JdbcTemplate", kind: DataStoreKind::Sql, label: "Spring JdbcTemplate" },
- DriverRule { leaf: "EntityManager", kind: DataStoreKind::Sql, label: "JPA EntityManager" },
- DriverRule { leaf: "SessionFactory.openSession", kind: DataStoreKind::Sql, label: "Hibernate" },
- DriverRule { leaf: "Jedis", kind: DataStoreKind::KeyValue, label: "Jedis (Redis)" },
- DriverRule { leaf: "MongoClients.create", kind: DataStoreKind::Document, label: "MongoDB (java-driver)" },
-
+ DriverRule {
+ leaf: "DriverManager.getConnection",
+ kind: DataStoreKind::Sql,
+ label: "JDBC",
+ },
+ DriverRule {
+ leaf: "JdbcTemplate",
+ kind: DataStoreKind::Sql,
+ label: "Spring JdbcTemplate",
+ },
+ DriverRule {
+ leaf: "EntityManager",
+ kind: DataStoreKind::Sql,
+ label: "JPA EntityManager",
+ },
+ DriverRule {
+ leaf: "SessionFactory.openSession",
+ kind: DataStoreKind::Sql,
+ label: "Hibernate",
+ },
+ DriverRule {
+ leaf: "Jedis",
+ kind: DataStoreKind::KeyValue,
+ label: "Jedis (Redis)",
+ },
+ DriverRule {
+ leaf: "MongoClients.create",
+ kind: DataStoreKind::Document,
+ label: "MongoDB (java-driver)",
+ },
// Go — sql + ORM
- DriverRule { leaf: "sql.Open", kind: DataStoreKind::Sql, label: "database/sql" },
- DriverRule { leaf: "gorm.Open", kind: DataStoreKind::Sql, label: "GORM" },
- DriverRule { leaf: "sqlx.Connect", kind: DataStoreKind::Sql, label: "sqlx" },
- DriverRule { leaf: "sqlx.Open", kind: DataStoreKind::Sql, label: "sqlx" },
- DriverRule { leaf: "redis.NewClient", kind: DataStoreKind::KeyValue, label: "go-redis" },
- DriverRule { leaf: "mongo.Connect", kind: DataStoreKind::Document, label: "MongoDB (go-driver)" },
-
+ DriverRule {
+ leaf: "sql.Open",
+ kind: DataStoreKind::Sql,
+ label: "database/sql",
+ },
+ DriverRule {
+ leaf: "gorm.Open",
+ kind: DataStoreKind::Sql,
+ label: "GORM",
+ },
+ DriverRule {
+ leaf: "sqlx.Connect",
+ kind: DataStoreKind::Sql,
+ label: "sqlx",
+ },
+ DriverRule {
+ leaf: "sqlx.Open",
+ kind: DataStoreKind::Sql,
+ label: "sqlx",
+ },
+ DriverRule {
+ leaf: "redis.NewClient",
+ kind: DataStoreKind::KeyValue,
+ label: "go-redis",
+ },
+ DriverRule {
+ leaf: "mongo.Connect",
+ kind: DataStoreKind::Document,
+ label: "MongoDB (go-driver)",
+ },
// PHP — Eloquent / PDO
- DriverRule { leaf: "PDO", kind: DataStoreKind::Sql, label: "PDO" },
- DriverRule { leaf: "Eloquent::find", kind: DataStoreKind::Sql, label: "Laravel Eloquent" },
- DriverRule { leaf: "Eloquent::where", kind: DataStoreKind::Sql, label: "Laravel Eloquent" },
- DriverRule { leaf: "DB::connection", kind: DataStoreKind::Sql, label: "Laravel DB" },
- DriverRule { leaf: "Doctrine", kind: DataStoreKind::Sql, label: "Doctrine ORM" },
-
+ DriverRule {
+ leaf: "PDO",
+ kind: DataStoreKind::Sql,
+ label: "PDO",
+ },
+ DriverRule {
+ leaf: "Eloquent::find",
+ kind: DataStoreKind::Sql,
+ label: "Laravel Eloquent",
+ },
+ DriverRule {
+ leaf: "Eloquent::where",
+ kind: DataStoreKind::Sql,
+ label: "Laravel Eloquent",
+ },
+ DriverRule {
+ leaf: "DB::connection",
+ kind: DataStoreKind::Sql,
+ label: "Laravel DB",
+ },
+ DriverRule {
+ leaf: "Doctrine",
+ kind: DataStoreKind::Sql,
+ label: "Doctrine ORM",
+ },
// Ruby — ActiveRecord
- DriverRule { leaf: "ActiveRecord::Base.connection", kind: DataStoreKind::Sql, label: "ActiveRecord" },
- DriverRule { leaf: "ActiveRecord::Base.find", kind: DataStoreKind::Sql, label: "ActiveRecord" },
- DriverRule { leaf: ".find_by_sql", kind: DataStoreKind::Sql, label: "ActiveRecord raw SQL" },
-
+ DriverRule {
+ leaf: "ActiveRecord::Base.connection",
+ kind: DataStoreKind::Sql,
+ label: "ActiveRecord",
+ },
+ DriverRule {
+ leaf: "ActiveRecord::Base.find",
+ kind: DataStoreKind::Sql,
+ label: "ActiveRecord",
+ },
+ DriverRule {
+ leaf: ".find_by_sql",
+ kind: DataStoreKind::Sql,
+ label: "ActiveRecord raw SQL",
+ },
// Rust — sqlx / diesel
- DriverRule { leaf: "sqlx::query", kind: DataStoreKind::Sql, label: "sqlx" },
- DriverRule { leaf: "sqlx::query_as", kind: DataStoreKind::Sql, label: "sqlx" },
- DriverRule { leaf: "diesel::sql_query", kind: DataStoreKind::Sql, label: "Diesel" },
- DriverRule { leaf: "PgConnection::establish", kind: DataStoreKind::Sql, label: "Diesel" },
-
+ DriverRule {
+ leaf: "sqlx::query",
+ kind: DataStoreKind::Sql,
+ label: "sqlx",
+ },
+ DriverRule {
+ leaf: "sqlx::query_as",
+ kind: DataStoreKind::Sql,
+ label: "sqlx",
+ },
+ DriverRule {
+ leaf: "diesel::sql_query",
+ kind: DataStoreKind::Sql,
+ label: "Diesel",
+ },
+ DriverRule {
+ leaf: "PgConnection::establish",
+ kind: DataStoreKind::Sql,
+ label: "Diesel",
+ },
// Type-qualified — fires when the SSA type-fact engine resolves a
// receiver to `TypeKind::DatabaseConnection` regardless of the bare
// callee name (e.g. `conn = psycopg2.connect(); conn.cursor()` →
// typed_call_receivers maps the `.cursor` ordinal to "DatabaseConnection").
- DriverRule { leaf: "DatabaseConnection.cursor", kind: DataStoreKind::Sql, label: "Database connection" },
- DriverRule { leaf: "DatabaseConnection.execute", kind: DataStoreKind::Sql, label: "Database connection" },
- DriverRule { leaf: "DatabaseConnection.query", kind: DataStoreKind::Sql, label: "Database connection" },
- DriverRule { leaf: "DatabaseConnection.exec", kind: DataStoreKind::Sql, label: "Database connection" },
- DriverRule { leaf: "DatabaseConnection.prepare", kind: DataStoreKind::Sql, label: "Database connection" },
- DriverRule { leaf: "DatabaseConnection.commit", kind: DataStoreKind::Sql, label: "Database connection" },
- DriverRule { leaf: "FileHandle.read", kind: DataStoreKind::Filesystem, label: "Filesystem" },
- DriverRule { leaf: "FileHandle.write", kind: DataStoreKind::Filesystem, label: "Filesystem" },
- DriverRule { leaf: "FileHandle.close", kind: DataStoreKind::Filesystem, label: "Filesystem" },
-
+ DriverRule {
+ leaf: "DatabaseConnection.cursor",
+ kind: DataStoreKind::Sql,
+ label: "Database connection",
+ },
+ DriverRule {
+ leaf: "DatabaseConnection.execute",
+ kind: DataStoreKind::Sql,
+ label: "Database connection",
+ },
+ DriverRule {
+ leaf: "DatabaseConnection.query",
+ kind: DataStoreKind::Sql,
+ label: "Database connection",
+ },
+ DriverRule {
+ leaf: "DatabaseConnection.exec",
+ kind: DataStoreKind::Sql,
+ label: "Database connection",
+ },
+ DriverRule {
+ leaf: "DatabaseConnection.prepare",
+ kind: DataStoreKind::Sql,
+ label: "Database connection",
+ },
+ DriverRule {
+ leaf: "DatabaseConnection.commit",
+ kind: DataStoreKind::Sql,
+ label: "Database connection",
+ },
+ DriverRule {
+ leaf: "FileHandle.read",
+ kind: DataStoreKind::Filesystem,
+ label: "Filesystem",
+ },
+ DriverRule {
+ leaf: "FileHandle.write",
+ kind: DataStoreKind::Filesystem,
+ label: "Filesystem",
+ },
+ DriverRule {
+ leaf: "FileHandle.close",
+ kind: DataStoreKind::Filesystem,
+ label: "Filesystem",
+ },
// Filesystem (best-effort: language-agnostic open()-family)
- DriverRule { leaf: "open", kind: DataStoreKind::Filesystem, label: "Filesystem" },
+ DriverRule {
+ leaf: "open",
+ kind: DataStoreKind::Filesystem,
+ label: "Filesystem",
+ },
];
/// Walk every function summary's callee list and emit one
@@ -127,7 +355,9 @@ pub fn detect_data_stores(summaries: &GlobalSummaries) -> Vec {
let mut seen: std::collections::HashSet<(String, u32, String)> =
std::collections::HashSet::new();
for (key, summary) in summaries.iter() {
- let typed = summaries.get_ssa(key).map(|s| s.typed_call_receivers.as_slice());
+ let typed = summaries
+ .get_ssa(key)
+ .map(|s| s.typed_call_receivers.as_slice());
for callee in &summary.callees {
let rule = match_rule(&callee.name).or_else(|| {
typed
@@ -136,11 +366,7 @@ pub fn detect_data_stores(summaries: &GlobalSummaries) -> Vec {
});
let Some(rule) = rule else { continue };
let location = call_site_location(summary, callee);
- let dedup = (
- location.file.clone(),
- location.line,
- rule.label.to_string(),
- );
+ let dedup = (location.file.clone(), location.line, rule.label.to_string());
if !seen.insert(dedup) {
continue;
}
@@ -170,7 +396,10 @@ fn qualify(container: &str, callee_name: &str) -> String {
/// `Vec<(ordinal, container)>` per function. Typical lengths are 0 to a
/// few dozen; a HashMap-per-summary would be wasteful.
fn container_for_ordinal(typed: &[(u32, String)], ordinal: u32) -> Option<&str> {
- typed.iter().find(|(o, _)| *o == ordinal).map(|(_, c)| c.as_str())
+ typed
+ .iter()
+ .find(|(o, _)| *o == ordinal)
+ .map(|(_, c)| c.as_str())
}
fn match_rule(callee: &str) -> Option<&'static DriverRule> {
@@ -285,11 +514,8 @@ mod tests {
#[test]
fn dedup_collapses_repeats_in_same_file() {
let mut gs = GlobalSummaries::new();
- let (k, s) = summary_with_callees(
- "init",
- "app.py",
- &["psycopg2.connect", "psycopg2.connect"],
- );
+ let (k, s) =
+ summary_with_callees("init", "app.py", &["psycopg2.connect", "psycopg2.connect"]);
gs.insert(k, s);
let nodes = detect_data_stores(&gs);
assert_eq!(nodes.len(), 1);
@@ -352,14 +578,12 @@ mod tests {
file_path: "app.py".into(),
lang: "python".into(),
param_count: 0,
- callees: vec![
- {
- let mut c = CalleeSite::bare("conn.cursor");
- c.ordinal = 7;
- c.span = Some((4, 8));
- c
- },
- ],
+ callees: vec![{
+ let mut c = CalleeSite::bare("conn.cursor");
+ c.ordinal = 7;
+ c.span = Some((4, 8));
+ c
+ }],
..Default::default()
};
gs.insert(key.clone(), summary);
diff --git a/src/surface/external.rs b/src/surface/external.rs
index 11d7175f..b3e75b67 100644
--- a/src/surface/external.rs
+++ b/src/surface/external.rs
@@ -19,81 +19,307 @@ struct ClientRule {
const CLIENT_RULES: &[ClientRule] = &[
// HTTP
- ClientRule { leaf: "requests.get", kind: ExternalServiceKind::HttpApi, label: "requests (Python)" },
- ClientRule { leaf: "requests.post", kind: ExternalServiceKind::HttpApi, label: "requests (Python)" },
- ClientRule { leaf: "httpx.get", kind: ExternalServiceKind::HttpApi, label: "httpx (Python)" },
- ClientRule { leaf: "httpx.post", kind: ExternalServiceKind::HttpApi, label: "httpx (Python)" },
- ClientRule { leaf: "urllib.request.urlopen", kind: ExternalServiceKind::HttpApi, label: "urllib" },
- ClientRule { leaf: "fetch", kind: ExternalServiceKind::HttpApi, label: "fetch (JS)" },
- ClientRule { leaf: "axios.get", kind: ExternalServiceKind::HttpApi, label: "axios" },
- ClientRule { leaf: "axios.post", kind: ExternalServiceKind::HttpApi, label: "axios" },
- ClientRule { leaf: "http.request", kind: ExternalServiceKind::HttpApi, label: "node http" },
- ClientRule { leaf: "got", kind: ExternalServiceKind::HttpApi, label: "got (JS)" },
- ClientRule { leaf: "HttpClient.send", kind: ExternalServiceKind::HttpApi, label: "Java HttpClient" },
- ClientRule { leaf: "HttpClient.execute", kind: ExternalServiceKind::HttpApi, label: "Java HttpClient" },
- ClientRule { leaf: "RestTemplate.exchange", kind: ExternalServiceKind::HttpApi, label: "Spring RestTemplate" },
- ClientRule { leaf: "RestTemplate.getForObject", kind: ExternalServiceKind::HttpApi, label: "Spring RestTemplate" },
- ClientRule { leaf: "OkHttpClient.newCall", kind: ExternalServiceKind::HttpApi, label: "OkHttp" },
- ClientRule { leaf: "http.Get", kind: ExternalServiceKind::HttpApi, label: "net/http (Go)" },
- ClientRule { leaf: "http.Post", kind: ExternalServiceKind::HttpApi, label: "net/http (Go)" },
- ClientRule { leaf: "http.NewRequest", kind: ExternalServiceKind::HttpApi, label: "net/http (Go)" },
- ClientRule { leaf: "client.Do", kind: ExternalServiceKind::HttpApi, label: "go http client" },
- ClientRule { leaf: "reqwest::get", kind: ExternalServiceKind::HttpApi, label: "reqwest (Rust)" },
- ClientRule { leaf: "reqwest::Client", kind: ExternalServiceKind::HttpApi, label: "reqwest (Rust)" },
- ClientRule { leaf: "Net::HTTP", kind: ExternalServiceKind::HttpApi, label: "Net::HTTP (Ruby)" },
- ClientRule { leaf: "HTTParty.get", kind: ExternalServiceKind::HttpApi, label: "HTTParty" },
- ClientRule { leaf: "Faraday", kind: ExternalServiceKind::HttpApi, label: "Faraday (Ruby)" },
- ClientRule { leaf: "curl_exec", kind: ExternalServiceKind::HttpApi, label: "PHP curl" },
- ClientRule { leaf: "file_get_contents", kind: ExternalServiceKind::HttpApi, label: "PHP file_get_contents" },
- ClientRule { leaf: "Guzzle", kind: ExternalServiceKind::HttpApi, label: "Guzzle (PHP)" },
-
+ ClientRule {
+ leaf: "requests.get",
+ kind: ExternalServiceKind::HttpApi,
+ label: "requests (Python)",
+ },
+ ClientRule {
+ leaf: "requests.post",
+ kind: ExternalServiceKind::HttpApi,
+ label: "requests (Python)",
+ },
+ ClientRule {
+ leaf: "httpx.get",
+ kind: ExternalServiceKind::HttpApi,
+ label: "httpx (Python)",
+ },
+ ClientRule {
+ leaf: "httpx.post",
+ kind: ExternalServiceKind::HttpApi,
+ label: "httpx (Python)",
+ },
+ ClientRule {
+ leaf: "urllib.request.urlopen",
+ kind: ExternalServiceKind::HttpApi,
+ label: "urllib",
+ },
+ ClientRule {
+ leaf: "fetch",
+ kind: ExternalServiceKind::HttpApi,
+ label: "fetch (JS)",
+ },
+ ClientRule {
+ leaf: "axios.get",
+ kind: ExternalServiceKind::HttpApi,
+ label: "axios",
+ },
+ ClientRule {
+ leaf: "axios.post",
+ kind: ExternalServiceKind::HttpApi,
+ label: "axios",
+ },
+ ClientRule {
+ leaf: "http.request",
+ kind: ExternalServiceKind::HttpApi,
+ label: "node http",
+ },
+ ClientRule {
+ leaf: "got",
+ kind: ExternalServiceKind::HttpApi,
+ label: "got (JS)",
+ },
+ ClientRule {
+ leaf: "HttpClient.send",
+ kind: ExternalServiceKind::HttpApi,
+ label: "Java HttpClient",
+ },
+ ClientRule {
+ leaf: "HttpClient.execute",
+ kind: ExternalServiceKind::HttpApi,
+ label: "Java HttpClient",
+ },
+ ClientRule {
+ leaf: "RestTemplate.exchange",
+ kind: ExternalServiceKind::HttpApi,
+ label: "Spring RestTemplate",
+ },
+ ClientRule {
+ leaf: "RestTemplate.getForObject",
+ kind: ExternalServiceKind::HttpApi,
+ label: "Spring RestTemplate",
+ },
+ ClientRule {
+ leaf: "OkHttpClient.newCall",
+ kind: ExternalServiceKind::HttpApi,
+ label: "OkHttp",
+ },
+ ClientRule {
+ leaf: "http.Get",
+ kind: ExternalServiceKind::HttpApi,
+ label: "net/http (Go)",
+ },
+ ClientRule {
+ leaf: "http.Post",
+ kind: ExternalServiceKind::HttpApi,
+ label: "net/http (Go)",
+ },
+ ClientRule {
+ leaf: "http.NewRequest",
+ kind: ExternalServiceKind::HttpApi,
+ label: "net/http (Go)",
+ },
+ ClientRule {
+ leaf: "client.Do",
+ kind: ExternalServiceKind::HttpApi,
+ label: "go http client",
+ },
+ ClientRule {
+ leaf: "reqwest::get",
+ kind: ExternalServiceKind::HttpApi,
+ label: "reqwest (Rust)",
+ },
+ ClientRule {
+ leaf: "reqwest::Client",
+ kind: ExternalServiceKind::HttpApi,
+ label: "reqwest (Rust)",
+ },
+ ClientRule {
+ leaf: "Net::HTTP",
+ kind: ExternalServiceKind::HttpApi,
+ label: "Net::HTTP (Ruby)",
+ },
+ ClientRule {
+ leaf: "HTTParty.get",
+ kind: ExternalServiceKind::HttpApi,
+ label: "HTTParty",
+ },
+ ClientRule {
+ leaf: "Faraday",
+ kind: ExternalServiceKind::HttpApi,
+ label: "Faraday (Ruby)",
+ },
+ ClientRule {
+ leaf: "curl_exec",
+ kind: ExternalServiceKind::HttpApi,
+ label: "PHP curl",
+ },
+ ClientRule {
+ leaf: "file_get_contents",
+ kind: ExternalServiceKind::HttpApi,
+ label: "PHP file_get_contents",
+ },
+ ClientRule {
+ leaf: "Guzzle",
+ kind: ExternalServiceKind::HttpApi,
+ label: "Guzzle (PHP)",
+ },
// Message brokers
- ClientRule { leaf: "kafka.send", kind: ExternalServiceKind::MessageBroker, label: "Kafka" },
- ClientRule { leaf: "KafkaProducer.send", kind: ExternalServiceKind::MessageBroker, label: "Kafka" },
- ClientRule { leaf: "rabbitmq.publish", kind: ExternalServiceKind::MessageBroker, label: "RabbitMQ" },
- ClientRule { leaf: "amqp.publish", kind: ExternalServiceKind::MessageBroker, label: "AMQP" },
- ClientRule { leaf: "sqs.send_message", kind: ExternalServiceKind::MessageBroker, label: "AWS SQS" },
- ClientRule { leaf: "sns.publish", kind: ExternalServiceKind::MessageBroker, label: "AWS SNS" },
-
+ ClientRule {
+ leaf: "kafka.send",
+ kind: ExternalServiceKind::MessageBroker,
+ label: "Kafka",
+ },
+ ClientRule {
+ leaf: "KafkaProducer.send",
+ kind: ExternalServiceKind::MessageBroker,
+ label: "Kafka",
+ },
+ ClientRule {
+ leaf: "rabbitmq.publish",
+ kind: ExternalServiceKind::MessageBroker,
+ label: "RabbitMQ",
+ },
+ ClientRule {
+ leaf: "amqp.publish",
+ kind: ExternalServiceKind::MessageBroker,
+ label: "AMQP",
+ },
+ ClientRule {
+ leaf: "sqs.send_message",
+ kind: ExternalServiceKind::MessageBroker,
+ label: "AWS SQS",
+ },
+ ClientRule {
+ leaf: "sns.publish",
+ kind: ExternalServiceKind::MessageBroker,
+ label: "AWS SNS",
+ },
// Search indices
- ClientRule { leaf: "Elasticsearch", kind: ExternalServiceKind::SearchIndex, label: "Elasticsearch" },
- ClientRule { leaf: "elasticsearch.search", kind: ExternalServiceKind::SearchIndex, label: "Elasticsearch" },
- ClientRule { leaf: "OpenSearch", kind: ExternalServiceKind::SearchIndex, label: "OpenSearch" },
- ClientRule { leaf: "Algolia", kind: ExternalServiceKind::SearchIndex, label: "Algolia" },
-
+ ClientRule {
+ leaf: "Elasticsearch",
+ kind: ExternalServiceKind::SearchIndex,
+ label: "Elasticsearch",
+ },
+ ClientRule {
+ leaf: "elasticsearch.search",
+ kind: ExternalServiceKind::SearchIndex,
+ label: "Elasticsearch",
+ },
+ ClientRule {
+ leaf: "OpenSearch",
+ kind: ExternalServiceKind::SearchIndex,
+ label: "OpenSearch",
+ },
+ ClientRule {
+ leaf: "Algolia",
+ kind: ExternalServiceKind::SearchIndex,
+ label: "Algolia",
+ },
// Auth providers
- ClientRule { leaf: "auth0", kind: ExternalServiceKind::AuthProvider, label: "Auth0" },
- ClientRule { leaf: "passport.authenticate", kind: ExternalServiceKind::AuthProvider, label: "Passport.js" },
- ClientRule { leaf: "OAuth2Client", kind: ExternalServiceKind::AuthProvider, label: "OAuth2 client" },
- ClientRule { leaf: "google.oauth2", kind: ExternalServiceKind::AuthProvider, label: "Google OAuth2" },
-
+ ClientRule {
+ leaf: "auth0",
+ kind: ExternalServiceKind::AuthProvider,
+ label: "Auth0",
+ },
+ ClientRule {
+ leaf: "passport.authenticate",
+ kind: ExternalServiceKind::AuthProvider,
+ label: "Passport.js",
+ },
+ ClientRule {
+ leaf: "OAuth2Client",
+ kind: ExternalServiceKind::AuthProvider,
+ label: "OAuth2 client",
+ },
+ ClientRule {
+ leaf: "google.oauth2",
+ kind: ExternalServiceKind::AuthProvider,
+ label: "Google OAuth2",
+ },
// SMTP
- ClientRule { leaf: "smtplib.SMTP", kind: ExternalServiceKind::HttpApi, label: "SMTP (Python)" },
- ClientRule { leaf: "Mail::send", kind: ExternalServiceKind::HttpApi, label: "Laravel Mail" },
- ClientRule { leaf: "ActionMailer", kind: ExternalServiceKind::HttpApi, label: "Rails ActionMailer" },
-
+ ClientRule {
+ leaf: "smtplib.SMTP",
+ kind: ExternalServiceKind::HttpApi,
+ label: "SMTP (Python)",
+ },
+ ClientRule {
+ leaf: "Mail::send",
+ kind: ExternalServiceKind::HttpApi,
+ label: "Laravel Mail",
+ },
+ ClientRule {
+ leaf: "ActionMailer",
+ kind: ExternalServiceKind::HttpApi,
+ label: "Rails ActionMailer",
+ },
// DNS
- ClientRule { leaf: "socket.gethostbyname", kind: ExternalServiceKind::HttpApi, label: "DNS resolver" },
- ClientRule { leaf: "dns.lookup", kind: ExternalServiceKind::HttpApi, label: "DNS resolver" },
- ClientRule { leaf: "net.LookupIP", kind: ExternalServiceKind::HttpApi, label: "DNS resolver" },
-
+ ClientRule {
+ leaf: "socket.gethostbyname",
+ kind: ExternalServiceKind::HttpApi,
+ label: "DNS resolver",
+ },
+ ClientRule {
+ leaf: "dns.lookup",
+ kind: ExternalServiceKind::HttpApi,
+ label: "DNS resolver",
+ },
+ ClientRule {
+ leaf: "net.LookupIP",
+ kind: ExternalServiceKind::HttpApi,
+ label: "DNS resolver",
+ },
// Type-qualified — fires when the SSA type-fact engine resolves a
// receiver to `TypeKind::HttpClient` regardless of the bare callee
// name (`session = requests.Session(); session.get(url)` →
// typed_call_receivers maps the `.get` ordinal to "HttpClient", so
// the bound-receiver call surfaces as an outbound HTTP node even
// though `requests.get` is the only direct-import rule above).
- ClientRule { leaf: "HttpClient.get", kind: ExternalServiceKind::HttpApi, label: "HTTP client" },
- ClientRule { leaf: "HttpClient.post", kind: ExternalServiceKind::HttpApi, label: "HTTP client" },
- ClientRule { leaf: "HttpClient.put", kind: ExternalServiceKind::HttpApi, label: "HTTP client" },
- ClientRule { leaf: "HttpClient.delete", kind: ExternalServiceKind::HttpApi, label: "HTTP client" },
- ClientRule { leaf: "HttpClient.patch", kind: ExternalServiceKind::HttpApi, label: "HTTP client" },
- ClientRule { leaf: "HttpClient.request", kind: ExternalServiceKind::HttpApi, label: "HTTP client" },
- ClientRule { leaf: "HttpClient.head", kind: ExternalServiceKind::HttpApi, label: "HTTP client" },
- ClientRule { leaf: "HttpClient.options", kind: ExternalServiceKind::HttpApi, label: "HTTP client" },
- ClientRule { leaf: "RequestBuilder.send", kind: ExternalServiceKind::HttpApi, label: "HTTP request builder" },
- ClientRule { leaf: "URL.openConnection", kind: ExternalServiceKind::HttpApi, label: "URL connection" },
- ClientRule { leaf: "URL.openStream", kind: ExternalServiceKind::HttpApi, label: "URL connection" },
+ ClientRule {
+ leaf: "HttpClient.get",
+ kind: ExternalServiceKind::HttpApi,
+ label: "HTTP client",
+ },
+ ClientRule {
+ leaf: "HttpClient.post",
+ kind: ExternalServiceKind::HttpApi,
+ label: "HTTP client",
+ },
+ ClientRule {
+ leaf: "HttpClient.put",
+ kind: ExternalServiceKind::HttpApi,
+ label: "HTTP client",
+ },
+ ClientRule {
+ leaf: "HttpClient.delete",
+ kind: ExternalServiceKind::HttpApi,
+ label: "HTTP client",
+ },
+ ClientRule {
+ leaf: "HttpClient.patch",
+ kind: ExternalServiceKind::HttpApi,
+ label: "HTTP client",
+ },
+ ClientRule {
+ leaf: "HttpClient.request",
+ kind: ExternalServiceKind::HttpApi,
+ label: "HTTP client",
+ },
+ ClientRule {
+ leaf: "HttpClient.head",
+ kind: ExternalServiceKind::HttpApi,
+ label: "HTTP client",
+ },
+ ClientRule {
+ leaf: "HttpClient.options",
+ kind: ExternalServiceKind::HttpApi,
+ label: "HTTP client",
+ },
+ ClientRule {
+ leaf: "RequestBuilder.send",
+ kind: ExternalServiceKind::HttpApi,
+ label: "HTTP request builder",
+ },
+ ClientRule {
+ leaf: "URL.openConnection",
+ kind: ExternalServiceKind::HttpApi,
+ label: "URL connection",
+ },
+ ClientRule {
+ leaf: "URL.openStream",
+ kind: ExternalServiceKind::HttpApi,
+ label: "URL connection",
+ },
];
/// Walk every function summary's callee list and emit one
@@ -109,10 +335,11 @@ const CLIENT_RULES: &[ClientRule] = &[
/// client.get(url)`) that the name-only matcher misses.
pub fn detect_external_services(summaries: &GlobalSummaries) -> Vec {
let mut out: Vec = Vec::new();
- let mut seen: std::collections::HashSet<(String, String)> =
- std::collections::HashSet::new();
+ let mut seen: std::collections::HashSet<(String, String)> = std::collections::HashSet::new();
for (key, summary) in summaries.iter() {
- let typed = summaries.get_ssa(key).map(|s| s.typed_call_receivers.as_slice());
+ let typed = summaries
+ .get_ssa(key)
+ .map(|s| s.typed_call_receivers.as_slice());
for callee in &summary.callees {
let rule = match_rule(&callee.name).or_else(|| {
typed
@@ -161,7 +388,10 @@ fn qualify(container: &str, callee_name: &str) -> String {
}
fn container_for_ordinal(typed: &[(u32, String)], ordinal: u32) -> Option<&str> {
- typed.iter().find(|(o, _)| *o == ordinal).map(|(_, c)| c.as_str())
+ typed
+ .iter()
+ .find(|(o, _)| *o == ordinal)
+ .map(|(_, c)| c.as_str())
}
fn match_rule(callee: &str) -> Option<&'static ClientRule> {
diff --git a/src/surface/lang/common.rs b/src/surface/lang/common.rs
index 22ef07da..a97d72b4 100644
--- a/src/surface/lang/common.rs
+++ b/src/surface/lang/common.rs
@@ -106,10 +106,7 @@ pub fn python_imports_any(bytes: &[u8], modules: &[&str]) -> bool {
let pkg = if let Some(rest) = line.strip_prefix("from ") {
rest.split_whitespace().next().unwrap_or("")
} else if let Some(rest) = line.strip_prefix("import ") {
- rest.split([',', ' ', ';'])
- .next()
- .unwrap_or("")
- .trim()
+ rest.split([',', ' ', ';']).next().unwrap_or("").trim()
} else {
continue;
};
@@ -237,7 +234,10 @@ mod tests {
#[test]
fn leaf_matches_handles_dot_and_colon_paths() {
- assert!(leaf_matches("flask_login.login_required", &["login_required"]));
+ assert!(leaf_matches(
+ "flask_login.login_required",
+ &["login_required"]
+ ));
assert!(leaf_matches("Auth::JwtRequired", &["JwtRequired"]));
assert!(!leaf_matches("OtherDecorator", &["login_required"]));
}
@@ -246,7 +246,10 @@ mod tests {
fn python_imports_any_matches_actual_imports() {
assert!(python_imports_any(b"from flask import Flask\n", &["flask"]));
assert!(python_imports_any(b"import flask\n", &["flask"]));
- assert!(python_imports_any(b"from flask.app import Flask\n", &["flask"]));
+ assert!(python_imports_any(
+ b"from flask.app import Flask\n",
+ &["flask"]
+ ));
assert!(python_imports_any(b"import django.urls\n", &["django"]));
// Comment-only mention must not match.
assert!(!python_imports_any(b"# flask is great\n", &["flask"]));
@@ -260,10 +263,7 @@ mod tests {
fn rust_uses_any_matches_use_statements() {
assert!(rust_uses_any(b"use actix_web::web;\n", &["actix_web"]));
assert!(rust_uses_any(b"use actix_web;\n", &["actix_web"]));
- assert!(rust_uses_any(
- b"pub use axum::Router;\n",
- &["axum"]
- ));
+ assert!(rust_uses_any(b"pub use axum::Router;\n", &["axum"]));
assert!(rust_uses_any(
b"pub(crate) use axum::extract::Path;\n",
&["axum"]
diff --git a/src/surface/lang/go_gin.rs b/src/surface/lang/go_gin.rs
index a2614964..db27c4ba 100644
--- a/src/surface/lang/go_gin.rs
+++ b/src/surface/lang/go_gin.rs
@@ -21,8 +21,8 @@ use tree_sitter::{Node, Tree};
pub use crate::auth_analysis::auth_markers::GIN_MIDDLEWARES as AUTH_MIDDLEWARES;
const VERBS: &[&str] = &[
- "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "Any",
- "Get", "Post", "Put", "Delete", "Patch", "Options", "Head",
+ "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "Any", "Get", "Post", "Put",
+ "Delete", "Patch", "Options", "Head",
];
pub fn detect_gin_routes(
@@ -73,7 +73,9 @@ fn match_gin_call(call: Node, bytes: &[u8], file_rel: &str) -> Option bool {
}
if let Some(name) = annotation_name(ann, bytes) {
let leaf = name.rsplit('.').next().unwrap_or(&name);
- if AUTH_ANNOTATIONS.iter().any(|a| leaf.eq_ignore_ascii_case(a)) {
+ if AUTH_ANNOTATIONS
+ .iter()
+ .any(|a| leaf.eq_ignore_ascii_case(a))
+ {
return true;
}
}
@@ -149,7 +152,11 @@ fn class_has_auth_annotation(class: Node, bytes: &[u8]) -> bool {
false
}
-fn method_mapping(method: Node, bytes: &[u8], class_path: &str) -> Option<(HttpMethod, String, bool)> {
+fn method_mapping(
+ method: Node,
+ bytes: &[u8],
+ class_path: &str,
+) -> Option<(HttpMethod, String, bool)> {
let modifiers = crate::surface::lang::common::child_or_named(method, "modifiers")?;
let mut cursor = modifiers.walk();
let mut verb: Option = None;
@@ -163,7 +170,10 @@ fn method_mapping(method: Node, bytes: &[u8], class_path: &str) -> Option<(HttpM
continue;
};
let leaf = name.rsplit('.').next().unwrap_or(&name);
- if let Some((_, m)) = JAXRS_VERBS.iter().find(|(n, _)| n.eq_ignore_ascii_case(leaf)) {
+ if let Some((_, m)) = JAXRS_VERBS
+ .iter()
+ .find(|(n, _)| n.eq_ignore_ascii_case(leaf))
+ {
verb = Some(*m);
}
if leaf == "Path"
@@ -171,7 +181,10 @@ fn method_mapping(method: Node, bytes: &[u8], class_path: &str) -> Option<(HttpM
{
method_path = p;
}
- if AUTH_ANNOTATIONS.iter().any(|a| leaf.eq_ignore_ascii_case(a)) {
+ if AUTH_ANNOTATIONS
+ .iter()
+ .any(|a| leaf.eq_ignore_ascii_case(a))
+ {
auth = true;
}
}
@@ -181,7 +194,11 @@ fn method_mapping(method: Node, bytes: &[u8], class_path: &str) -> Option<(HttpM
} else if method_path.is_empty() {
class_path.to_string()
} else {
- format!("{}/{}", class_path.trim_end_matches('/'), method_path.trim_start_matches('/'))
+ format!(
+ "{}/{}",
+ class_path.trim_end_matches('/'),
+ method_path.trim_start_matches('/')
+ )
};
Some((v, combined, auth))
}
@@ -258,7 +275,8 @@ public class GreetResource {
}
"#;
let (tree, bytes) = parse(src);
- let nodes = detect_quarkus_routes(&tree, &bytes, &PathBuf::from("GreetResource.java"), None);
+ let nodes =
+ detect_quarkus_routes(&tree, &bytes, &PathBuf::from("GreetResource.java"), None);
assert_eq!(nodes.len(), 1);
let SurfaceNode::EntryPoint(ep) = &nodes[0] else {
panic!()
diff --git a/src/surface/lang/java_servlet.rs b/src/surface/lang/java_servlet.rs
index 1a48e42a..00a0f9f0 100644
--- a/src/surface/lang/java_servlet.rs
+++ b/src/surface/lang/java_servlet.rs
@@ -139,7 +139,10 @@ fn class_has_auth_annotation(class: Node, bytes: &[u8]) -> bool {
}
if let Some(name) = annotation_name(ann, bytes)
&& AUTH_ANNOTATIONS.iter().any(|a| {
- name.rsplit('.').next().unwrap_or(&name).eq_ignore_ascii_case(a)
+ name.rsplit('.')
+ .next()
+ .unwrap_or(&name)
+ .eq_ignore_ascii_case(a)
})
{
return true;
@@ -148,7 +151,11 @@ fn class_has_auth_annotation(class: Node, bytes: &[u8]) -> bool {
false
}
-fn jaxrs_method_mapping(method: Node, bytes: &[u8], class_path: &str) -> Option<(HttpMethod, String, bool)> {
+fn jaxrs_method_mapping(
+ method: Node,
+ bytes: &[u8],
+ class_path: &str,
+) -> Option<(HttpMethod, String, bool)> {
let modifiers = crate::surface::lang::common::child_or_named(method, "modifiers")?;
let mut cursor = modifiers.walk();
let mut verb: Option = None;
@@ -162,7 +169,10 @@ fn jaxrs_method_mapping(method: Node, bytes: &[u8], class_path: &str) -> Option<
continue;
};
let leaf = name.rsplit('.').next().unwrap_or(&name);
- if let Some((_, m)) = JAXRS_VERBS.iter().find(|(n, _)| n.eq_ignore_ascii_case(leaf)) {
+ if let Some((_, m)) = JAXRS_VERBS
+ .iter()
+ .find(|(n, _)| n.eq_ignore_ascii_case(leaf))
+ {
verb = Some(*m);
}
if leaf == "Path"
@@ -183,7 +193,11 @@ fn jaxrs_method_mapping(method: Node, bytes: &[u8], class_path: &str) -> Option<
} else if method_path.is_empty() {
class_path.to_string()
} else {
- format!("{}/{}", class_path.trim_end_matches('/'), method_path.trim_start_matches('/'))
+ format!(
+ "{}/{}",
+ class_path.trim_end_matches('/'),
+ method_path.trim_start_matches('/')
+ )
};
Some((v, combined, auth))
}
@@ -255,7 +269,8 @@ public class UsersResource {
}
"#;
let (tree, bytes) = parse(src);
- let nodes = detect_servlet_routes(&tree, &bytes, &PathBuf::from("UsersResource.java"), None);
+ let nodes =
+ detect_servlet_routes(&tree, &bytes, &PathBuf::from("UsersResource.java"), None);
assert!(!nodes.is_empty());
let SurfaceNode::EntryPoint(ep) = &nodes[0] else {
panic!()
diff --git a/src/surface/lang/java_spring.rs b/src/surface/lang/java_spring.rs
index 9d85379a..03f4479b 100644
--- a/src/surface/lang/java_spring.rs
+++ b/src/surface/lang/java_spring.rs
@@ -46,9 +46,7 @@ pub fn detect_spring_routes(
if member.kind() != "method_declaration" {
continue;
}
- if let Some((method, route_path, auth)) =
- method_mapping(member, bytes, &class_path)
- {
+ if let Some((method, route_path, auth)) = method_mapping(member, bytes, &class_path) {
let auth_required = class_auth || auth;
let handler_name = method_name(member, bytes).unwrap_or_default();
out.push(SurfaceNode::EntryPoint(EntryPoint {
@@ -114,9 +112,7 @@ fn class_has_auth_annotation(class: Node, bytes: &[u8]) -> bool {
continue;
}
if let Some((name, _)) = annotation_name_and_args(ann, bytes)
- && AUTH_ANNOTATIONS
- .iter()
- .any(|a| leaf_matches(&name, &[a]))
+ && AUTH_ANNOTATIONS.iter().any(|a| leaf_matches(&name, &[a]))
{
return true;
}
@@ -140,10 +136,7 @@ fn method_mapping(
let Some((name, args_text)) = annotation_name_and_args(ann, bytes) else {
continue;
};
- if AUTH_ANNOTATIONS
- .iter()
- .any(|a| leaf_matches(&name, &[a]))
- {
+ if AUTH_ANNOTATIONS.iter().any(|a| leaf_matches(&name, &[a])) {
auth = true;
}
if found.is_some() {
@@ -156,7 +149,11 @@ fn method_mapping(
// Class-only mapping; method has no path.
method_route = class_path.to_string();
} else if !class_path.is_empty() {
- method_route = format!("{}/{}", class_path.trim_end_matches('/'), method_route.trim_start_matches('/'));
+ method_route = format!(
+ "{}/{}",
+ class_path.trim_end_matches('/'),
+ method_route.trim_start_matches('/')
+ );
}
let method = default_method
.or_else(|| extract_request_method_from_args(&args_text))
@@ -171,10 +168,7 @@ fn method_mapping(
}
fn is_annotation(node: Node) -> bool {
- matches!(
- node.kind(),
- "annotation" | "marker_annotation"
- )
+ matches!(node.kind(), "annotation" | "marker_annotation")
}
/// Returns `(annotation_name, raw_args_text)` for an annotation node.
@@ -253,7 +247,8 @@ public class UserController {
}
"#;
let (tree, bytes) = parse(src);
- let nodes = detect_spring_routes(&tree, &bytes, &PathBuf::from("UserController.java"), None);
+ let nodes =
+ detect_spring_routes(&tree, &bytes, &PathBuf::from("UserController.java"), None);
assert_eq!(nodes.len(), 1);
let SurfaceNode::EntryPoint(ep) = &nodes[0] else {
panic!()
diff --git a/src/surface/lang/js_express.rs b/src/surface/lang/js_express.rs
index 725891a5..791e05c1 100644
--- a/src/surface/lang/js_express.rs
+++ b/src/surface/lang/js_express.rs
@@ -153,10 +153,7 @@ fn arg_is_auth_marker(node: Node, bytes: &[u8]) -> bool {
fn receiver_is_express(object: Node, bytes: &[u8], has_express_witness: bool) -> bool {
fn name_matches_strong(text: &str) -> bool {
let lower = text.to_ascii_lowercase();
- lower == "app"
- || lower == "server"
- || lower.ends_with("_app")
- || lower.ends_with("api")
+ lower == "app" || lower == "server" || lower.ends_with("_app") || lower.ends_with("api")
}
fn name_matches_router(text: &str) -> bool {
let lower = text.to_ascii_lowercase();
@@ -239,7 +236,10 @@ mod tests {
let src = "const Router = require('@koa/router');\nconst router = new Router();\nrouter.get('/users', async ctx => {});\n";
let (tree, bytes) = parse(src);
let nodes = detect_express_routes(&tree, &bytes, &PathBuf::from("server.js"), None);
- assert!(nodes.is_empty(), "express probe FP'd on koa-only file: {nodes:?}");
+ assert!(
+ nodes.is_empty(),
+ "express probe FP'd on koa-only file: {nodes:?}"
+ );
}
#[test]
diff --git a/src/surface/lang/mod.rs b/src/surface/lang/mod.rs
index 864ea3b5..243a317c 100644
--- a/src/surface/lang/mod.rs
+++ b/src/surface/lang/mod.rs
@@ -12,26 +12,26 @@
pub mod common;
-pub mod python_flask;
-pub mod python_fastapi;
pub mod python_django;
+pub mod python_fastapi;
+pub mod python_flask;
pub mod js_express;
pub mod js_koa;
pub mod ts_next;
-pub mod java_spring;
-pub mod java_servlet;
pub mod java_quarkus;
+pub mod java_servlet;
+pub mod java_spring;
-pub mod go_http;
pub mod go_gin;
+pub mod go_http;
pub mod php_laravel;
pub mod php_slim;
-pub mod ruby_sinatra;
pub mod ruby_rails;
+pub mod ruby_sinatra;
pub mod rust_actix;
pub mod rust_axum;
diff --git a/src/surface/lang/php_laravel.rs b/src/surface/lang/php_laravel.rs
index 924ca3d5..3e172384 100644
--- a/src/surface/lang/php_laravel.rs
+++ b/src/surface/lang/php_laravel.rs
@@ -119,7 +119,9 @@ fn check_chained_middleware(call: Node, bytes: &[u8]) -> bool {
&& name_text == "middleware"
&& let Some(args) = p.child_by_field_name("arguments")
&& let Ok(args_text) = args.utf8_text(bytes)
- && (args_text.contains("auth") || args_text.contains("jwt") || args_text.contains("authenticated"))
+ && (args_text.contains("auth")
+ || args_text.contains("jwt")
+ || args_text.contains("authenticated"))
{
return true;
}
diff --git a/src/surface/lang/python_django.rs b/src/surface/lang/python_django.rs
index c81226b4..ea8d68f9 100644
--- a/src/surface/lang/python_django.rs
+++ b/src/surface/lang/python_django.rs
@@ -60,7 +60,13 @@ pub fn detect_django_routes(
let file_rel = rel_file(path, scan_root);
let mut out = Vec::new();
let function_index = collect_function_definitions(tree.root_node(), bytes);
- detect_url_dispatch(tree.root_node(), bytes, &file_rel, &function_index, &mut out);
+ detect_url_dispatch(
+ tree.root_node(),
+ bytes,
+ &file_rel,
+ &function_index,
+ &mut out,
+ );
detect_class_based_views(tree.root_node(), bytes, &file_rel, &mut out);
out
}
@@ -178,16 +184,9 @@ fn parse_url_call(call: Node, bytes: &[u8]) -> Option<(String, String)> {
Some((route?, handler?))
}
-fn detect_class_based_views(
- root: Node,
- bytes: &[u8],
- file_rel: &str,
- out: &mut Vec,
-) {
+fn detect_class_based_views(root: Node, bytes: &[u8], file_rel: &str, out: &mut Vec) {
fn recurse(node: Node, bytes: &[u8], file_rel: &str, out: &mut Vec) {
- if node.kind() == "class_definition"
- && class_is_django_view(node, bytes)
- {
+ if node.kind() == "class_definition" && class_is_django_view(node, bytes) {
let class_auth = class_has_auth_permission(node, bytes);
// Walk the body for HTTP-named methods.
if let Some(body) = node.child_by_field_name("body") {
diff --git a/src/surface/lang/python_flask.rs b/src/surface/lang/python_flask.rs
index acfb3b05..6e38e79b 100644
--- a/src/surface/lang/python_flask.rs
+++ b/src/surface/lang/python_flask.rs
@@ -17,9 +17,7 @@
use crate::entry_points::HttpMethod;
use crate::surface::lang::common::python_imports_any;
-use crate::surface::{
- EntryPoint, Framework, SourceLocation, SurfaceNode, relative_path_string,
-};
+use crate::surface::{EntryPoint, Framework, SourceLocation, SurfaceNode, relative_path_string};
use std::path::Path;
use tree_sitter::{Node, Tree};
@@ -273,9 +271,7 @@ fn decorator_is_auth_marker(decorator: Node, bytes: &[u8]) -> bool {
return false;
};
let leaf = text.rsplit('.').next().unwrap_or(text).trim();
- AUTH_DECORATORS
- .iter()
- .any(|d| leaf.eq_ignore_ascii_case(d))
+ AUTH_DECORATORS.iter().any(|d| leaf.eq_ignore_ascii_case(d))
}
/// Read the function name from a `function_definition` node.
diff --git a/src/surface/lang/ruby_rails.rs b/src/surface/lang/ruby_rails.rs
index cc2d8147..8e58321a 100644
--- a/src/surface/lang/ruby_rails.rs
+++ b/src/surface/lang/ruby_rails.rs
@@ -42,37 +42,35 @@ fn detect_routes_dsl(root: Node, bytes: &[u8], file_rel: &str, out: &mut Vec) {
if matches!(node.kind(), "call" | "method_call")
&& let Some(method_node) = node.child_by_field_name("method")
- && let Ok(method_text) = method_node.utf8_text(bytes)
- && let Some((_, method)) = VERBS.iter().find(|(v, _)| *v == method_text)
- {
- let args_opt = node
- .child_by_field_name("arguments")
- .or_else(|| {
- let mut c = node.walk();
- node.children(&mut c).find(|n| n.kind() == "argument_list")
- });
- if let Some(args) = args_opt {
- let mut cursor = args.walk();
- let positional: Vec = args.named_children(&mut cursor).collect();
- if let Some(route_node) = positional.first()
- && let Some(route) = string_node_value(*route_node, bytes)
- {
- let handler_name = positional
- .iter()
- .find_map(|n| extract_to_handler(*n, bytes))
- .unwrap_or_default();
- out.push(SurfaceNode::EntryPoint(EntryPoint {
- location: loc_for(node, file_rel),
- framework: Framework::Rails,
- method: *method,
- route,
- handler_name,
- handler_location: loc_for(node, file_rel),
- auth_required: false,
- }));
- }
+ && let Ok(method_text) = method_node.utf8_text(bytes)
+ && let Some((_, method)) = VERBS.iter().find(|(v, _)| *v == method_text)
+ {
+ let args_opt = node.child_by_field_name("arguments").or_else(|| {
+ let mut c = node.walk();
+ node.children(&mut c).find(|n| n.kind() == "argument_list")
+ });
+ if let Some(args) = args_opt {
+ let mut cursor = args.walk();
+ let positional: Vec = args.named_children(&mut cursor).collect();
+ if let Some(route_node) = positional.first()
+ && let Some(route) = string_node_value(*route_node, bytes)
+ {
+ let handler_name = positional
+ .iter()
+ .find_map(|n| extract_to_handler(*n, bytes))
+ .unwrap_or_default();
+ out.push(SurfaceNode::EntryPoint(EntryPoint {
+ location: loc_for(node, file_rel),
+ framework: Framework::Rails,
+ method: *method,
+ route,
+ handler_name,
+ handler_location: loc_for(node, file_rel),
+ auth_required: false,
+ }));
}
}
+ }
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
recurse(child, bytes, file_rel, out);
@@ -109,9 +107,7 @@ fn extract_to_handler(node: Node, bytes: &[u8]) -> Option {
fn detect_controllers(root: Node, bytes: &[u8], file_rel: &str, out: &mut Vec) {
fn recurse(node: Node, bytes: &[u8], file_rel: &str, out: &mut Vec) {
- if node.kind() == "class"
- && class_is_controller(node, bytes)
- {
+ if node.kind() == "class" && class_is_controller(node, bytes) {
let class_auth = class_has_before_authenticate(node, bytes);
walk_methods(node, bytes, &mut |method_node, name| {
out.push(SurfaceNode::EntryPoint(EntryPoint {
diff --git a/src/surface/lang/ruby_sinatra.rs b/src/surface/lang/ruby_sinatra.rs
index 8a083099..1623c344 100644
--- a/src/surface/lang/ruby_sinatra.rs
+++ b/src/surface/lang/ruby_sinatra.rs
@@ -50,24 +50,18 @@ fn walk_calls<'tree, F: FnMut(Node<'tree>)>(node: Node<'tree>, visit: &mut F) {
fn match_sinatra_call(call: Node, bytes: &[u8], file_rel: &str) -> Option {
let method_name_node = call.child_by_field_name("method")?;
let method_text = method_name_node.utf8_text(bytes).ok()?;
- let (_, method) = VERBS
- .iter()
- .find(|(v, _)| *v == method_text)?;
+ let (_, method) = VERBS.iter().find(|(v, _)| *v == method_text)?;
// Must have a block to be a Sinatra route.
- let block = call
- .child_by_field_name("block")
- .or_else(|| {
- let mut c = call.walk();
- call.children(&mut c)
- .find(|n| matches!(n.kind(), "do_block" | "block"))
- })?;
+ let block = call.child_by_field_name("block").or_else(|| {
+ let mut c = call.walk();
+ call.children(&mut c)
+ .find(|n| matches!(n.kind(), "do_block" | "block"))
+ })?;
// Args: Sinatra accepts a string literal as the first positional arg.
- let args = call
- .child_by_field_name("arguments")
- .or_else(|| {
- let mut c = call.walk();
- call.children(&mut c).find(|n| n.kind() == "argument_list")
- })?;
+ let args = call.child_by_field_name("arguments").or_else(|| {
+ let mut c = call.walk();
+ call.children(&mut c).find(|n| n.kind() == "argument_list")
+ })?;
let mut cursor = args.walk();
let route_node = args.named_children(&mut cursor).next()?;
let route = string_node_value(route_node, bytes)?;
diff --git a/src/surface/lang/rust_actix.rs b/src/surface/lang/rust_actix.rs
index 13a6f802..51a553b0 100644
--- a/src/surface/lang/rust_actix.rs
+++ b/src/surface/lang/rust_actix.rs
@@ -68,9 +68,7 @@ fn match_actix_function(func: Node, bytes: &[u8], file_rel: &str) -> Option) -> String {
if let Some(root) = scan_root
- && let Ok(rel) = path.strip_prefix(root) {
- return rel.to_string_lossy().replace('\\', "/");
- }
+ && let Ok(rel) = path.strip_prefix(root)
+ {
+ return rel.to_string_lossy().replace('\\', "/");
+ }
path.to_string_lossy().replace('\\', "/")
}
diff --git a/src/surface/reachability.rs b/src/surface/reachability.rs
index 89ce3535..d57b0d15 100644
--- a/src/surface/reachability.rs
+++ b/src/surface/reachability.rs
@@ -77,9 +77,7 @@ pub fn populate_reaches_edges(
.index
.iter()
.filter(|(k, _)| k.name == ep.handler_name)
- .filter(|(k, _)| {
- file_part_of_namespace(&k.namespace) == ep.handler_location.file
- })
+ .filter(|(k, _)| file_part_of_namespace(&k.namespace) == ep.handler_location.file)
.map(|(_, idx)| *idx)
.collect::>();
@@ -217,9 +215,6 @@ mod tests {
"src/file.ts"
);
// Last `::` wins, matching `namespace_with_package`'s shape.
- assert_eq!(
- file_part_of_namespace("@a/b::@c/d::lib/x.ts"),
- "lib/x.ts"
- );
+ assert_eq!(file_part_of_namespace("@a/b::@c/d::lib/x.ts"), "lib/x.ts");
}
}
diff --git a/src/symbol/mod.rs b/src/symbol/mod.rs
index ae2bb6b5..cbc3d730 100644
--- a/src/symbol/mod.rs
+++ b/src/symbol/mod.rs
@@ -115,9 +115,10 @@ impl Lang {
/// CLI entry points and other extensionless / non-canonical files.
pub fn from_path_or_content(path: &Path, head_bytes: &[u8]) -> Option {
if let Some(ext) = path.extension().and_then(|e| e.to_str())
- && let Some(lang) = Self::from_extension(ext) {
- return Some(lang);
- }
+ && let Some(lang) = Self::from_extension(ext)
+ {
+ return Some(lang);
+ }
if let Some(lang) = lang_from_shebang(head_bytes) {
return Some(lang);
}
@@ -352,10 +353,7 @@ fn lang_from_shebang(head: &[u8]) -> Option {
return None;
}
let cap = head.len().min(SNIFF_HEAD_LIMIT);
- let line_end = head[..cap]
- .iter()
- .position(|&b| b == b'\n')
- .unwrap_or(cap);
+ let line_end = head[..cap].iter().position(|&b| b == b'\n').unwrap_or(cap);
let line = std::str::from_utf8(&head[..line_end]).ok()?;
let line = line.trim_end_matches('\r').trim();
let rest = line.strip_prefix("#!")?.trim();
diff --git a/src/utils/redact.rs b/src/utils/redact.rs
index f4e31b57..f61cf76b 100644
--- a/src/utils/redact.rs
+++ b/src/utils/redact.rs
@@ -74,16 +74,25 @@ static PATTERNS: &[Pattern] = &[
// AWS access key IDs: AKIA[A-Z0-9]{16}
Pattern {
prefix: "AKIA",
- replace_fn: |s| replace_pattern(s, |c: &str| {
- if let Some(start) = c.find("AKIA") {
- let rest = &c[start + 4..];
- let end = rest.find(|ch: char| !ch.is_ascii_alphanumeric()).unwrap_or(rest.len());
- if end >= 12 {
- return true;
- }
- }
- false
- }, "AKIA", 20),
+ replace_fn: |s| {
+ replace_pattern(
+ s,
+ |c: &str| {
+ if let Some(start) = c.find("AKIA") {
+ let rest = &c[start + 4..];
+ let end = rest
+ .find(|ch: char| !ch.is_ascii_alphanumeric())
+ .unwrap_or(rest.len());
+ if end >= 12 {
+ return true;
+ }
+ }
+ false
+ },
+ "AKIA",
+ 20,
+ )
+ },
matches_fn: |s| akia_matches(s),
},
// GitHub personal access tokens: ghp_, github_pat_, ghs_, ghr_
@@ -255,7 +264,9 @@ fn replace_pem_blocks(s: &str) -> String {
fn akia_matches(s: &str) -> bool {
if let Some(pos) = s.find("AKIA") {
let rest = &s[pos + 4..];
- let end = rest.find(|ch: char| !ch.is_ascii_alphanumeric()).unwrap_or(rest.len());
+ let end = rest
+ .find(|ch: char| !ch.is_ascii_alphanumeric())
+ .unwrap_or(rest.len());
return end >= 12;
}
false
@@ -266,7 +277,9 @@ fn contains_sk_token(s: &str) -> bool {
let mut rest = s;
while let Some(pos) = rest.find("sk-") {
let after = &rest[pos + 3..];
- let end = after.find(|ch: char| !ch.is_ascii_alphanumeric() && ch != '-').unwrap_or(after.len());
+ let end = after
+ .find(|ch: char| !ch.is_ascii_alphanumeric() && ch != '-')
+ .unwrap_or(after.len());
if end >= 20 {
return true;
}
@@ -285,7 +298,9 @@ fn replace_pattern(
let mut rest = s;
while let Some(pos) = rest.find(prefix) {
let after = &rest[pos + prefix.len()..];
- let end = after.find(|ch: char| !ch.is_ascii_alphanumeric()).unwrap_or(after.len());
+ let end = after
+ .find(|ch: char| !ch.is_ascii_alphanumeric())
+ .unwrap_or(after.len());
if end >= token_len - prefix.len() {
out.push_str(&rest[..pos]);
out.push_str("");
@@ -307,7 +322,10 @@ mod tests {
fn redacts_aws_key() {
let input = "key: AKIAFAKETEST00000000 in config";
let out = redact_str(input);
- assert!(!out.contains("AKIAFAKETEST00000000"), "AWS key must be redacted");
+ assert!(
+ !out.contains("AKIAFAKETEST00000000"),
+ "AWS key must be redacted"
+ );
assert!(out.contains(""));
}
@@ -338,7 +356,10 @@ mod tests {
fn passthrough_clean_bytes() {
let input = b"\x80\x81 normal text here";
let out = redact(input);
- assert!(out.windows(b"normal text".len()).any(|w| w == b"normal text"));
+ assert!(
+ out.windows(b"normal text".len())
+ .any(|w| w == b"normal text")
+ );
}
#[test]
@@ -349,7 +370,8 @@ mod tests {
#[test]
fn redacts_pem_block() {
- let input = "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQ\n-----END RSA PRIVATE KEY-----";
+ let input =
+ "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQ\n-----END RSA PRIVATE KEY-----";
let out = redact_str(input);
assert!(!out.contains("MIIEowIBAAKCAQ"));
assert!(out.contains(""));
diff --git a/tests/c_fixtures.rs b/tests/c_fixtures.rs
index 19e52e37..d5e39426 100644
--- a/tests/c_fixtures.rs
+++ b/tests/c_fixtures.rs
@@ -15,7 +15,7 @@ mod common;
#[cfg(feature = "dynamic")]
mod c_fixture_tests {
- use crate::common::fixture_harness::{run_shape_fixture_lang_or_skip, Prerequisite};
+ use crate::common::fixture_harness::{Prerequisite, run_shape_fixture_lang_or_skip};
use nyx_scanner::dynamic::spec::PayloadSlot;
use nyx_scanner::evidence::{EntryKind, VerifyResult, VerifyStatus};
use nyx_scanner::labels::Cap;
@@ -64,7 +64,16 @@ mod c_fixture_tests {
slot: PayloadSlot,
) -> Option {
run_shape_fixture_lang_or_skip(
- CC_REQ, Lang::C, "c", shape, file, func, cap, sink_line, kind, slot,
+ CC_REQ,
+ Lang::C,
+ "c",
+ shape,
+ file,
+ func,
+ cap,
+ sink_line,
+ kind,
+ slot,
)
}
@@ -73,18 +82,32 @@ mod c_fixture_tests {
#[test]
fn main_argv_vuln_is_confirmed() {
let Some(r) = run(
- "main_argv", "vuln.c", "nyx_entry_main", Cap::CODE_EXEC, 23,
- EntryKind::CliSubcommand, PayloadSlot::Argv(0),
- ) else { return; };
+ "main_argv",
+ "vuln.c",
+ "nyx_entry_main",
+ Cap::CODE_EXEC,
+ 23,
+ EntryKind::CliSubcommand,
+ PayloadSlot::Argv(0),
+ ) else {
+ return;
+ };
assert_confirmed("main_argv", &r);
}
#[test]
fn main_argv_benign_not_confirmed() {
let Some(r) = run(
- "main_argv", "benign.c", "nyx_entry_main", Cap::CODE_EXEC, 11,
- EntryKind::CliSubcommand, PayloadSlot::Argv(0),
- ) else { return; };
+ "main_argv",
+ "benign.c",
+ "nyx_entry_main",
+ Cap::CODE_EXEC,
+ 11,
+ EntryKind::CliSubcommand,
+ PayloadSlot::Argv(0),
+ ) else {
+ return;
+ };
assert_not_confirmed("main_argv", &r);
}
@@ -93,18 +116,32 @@ mod c_fixture_tests {
#[test]
fn libfuzzer_vuln_is_confirmed() {
let Some(r) = run(
- "libfuzzer", "vuln.c", "LLVMFuzzerTestOneInput", Cap::CODE_EXEC, 16,
- EntryKind::LibraryApi, PayloadSlot::Param(0),
- ) else { return; };
+ "libfuzzer",
+ "vuln.c",
+ "LLVMFuzzerTestOneInput",
+ Cap::CODE_EXEC,
+ 16,
+ EntryKind::LibraryApi,
+ PayloadSlot::Param(0),
+ ) else {
+ return;
+ };
assert_confirmed("libfuzzer", &r);
}
#[test]
fn libfuzzer_benign_not_confirmed() {
let Some(r) = run(
- "libfuzzer", "benign.c", "LLVMFuzzerTestOneInput", Cap::CODE_EXEC, 10,
- EntryKind::LibraryApi, PayloadSlot::Param(0),
- ) else { return; };
+ "libfuzzer",
+ "benign.c",
+ "LLVMFuzzerTestOneInput",
+ Cap::CODE_EXEC,
+ 10,
+ EntryKind::LibraryApi,
+ PayloadSlot::Param(0),
+ ) else {
+ return;
+ };
assert_not_confirmed("libfuzzer", &r);
}
@@ -113,18 +150,32 @@ mod c_fixture_tests {
#[test]
fn free_fn_vuln_is_confirmed() {
let Some(r) = run(
- "free_fn", "vuln.c", "run", Cap::CODE_EXEC, 15,
- EntryKind::Function, PayloadSlot::Param(0),
- ) else { return; };
+ "free_fn",
+ "vuln.c",
+ "run",
+ Cap::CODE_EXEC,
+ 15,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
+ ) else {
+ return;
+ };
assert_confirmed("free_fn", &r);
}
#[test]
fn free_fn_benign_not_confirmed() {
let Some(r) = run(
- "free_fn", "benign.c", "run", Cap::CODE_EXEC, 10,
- EntryKind::Function, PayloadSlot::Param(0),
- ) else { return; };
+ "free_fn",
+ "benign.c",
+ "run",
+ Cap::CODE_EXEC,
+ 10,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
+ ) else {
+ return;
+ };
assert_not_confirmed("free_fn", &r);
}
}
diff --git a/tests/chain_edges.rs b/tests/chain_edges.rs
index 05e80301..bbfe1918 100644
--- a/tests/chain_edges.rs
+++ b/tests/chain_edges.rs
@@ -82,10 +82,7 @@ fn single_edge(diag: Diag, surface: &SurfaceMap) -> ChainEdge {
#[test]
fn rule_cmdi_alone_maps_to_rce() {
let surface = synthetic_surface("app.py", "/run");
- let edge = single_edge(
- diag_with_caps("app.py", 12, Cap::CODE_EXEC),
- &surface,
- );
+ let edge = single_edge(diag_with_caps("app.py", 12, Cap::CODE_EXEC), &surface);
assert_eq!(edge.primary_cap, Cap::CODE_EXEC);
assert!(matches!(edge.reach, Reach::Reachable { .. }));
assert_eq!(
@@ -97,10 +94,7 @@ fn rule_cmdi_alone_maps_to_rce() {
#[test]
fn rule_deserialize_alone_maps_to_rce() {
let surface = synthetic_surface("app.py", "/load");
- let edge = single_edge(
- diag_with_caps("app.py", 7, Cap::DESERIALIZE),
- &surface,
- );
+ let edge = single_edge(diag_with_caps("app.py", 7, Cap::DESERIALIZE), &surface);
assert_eq!(edge.primary_cap, Cap::DESERIALIZE);
assert_eq!(
lookup_impact(edge.primary_cap, None),
@@ -111,10 +105,7 @@ fn rule_deserialize_alone_maps_to_rce() {
#[test]
fn rule_ssrf_alone_maps_to_internal_network_access() {
let surface = synthetic_surface("fetch.py", "/proxy");
- let edge = single_edge(
- diag_with_caps("fetch.py", 4, Cap::SSRF),
- &surface,
- );
+ let edge = single_edge(diag_with_caps("fetch.py", 4, Cap::SSRF), &surface);
assert_eq!(edge.primary_cap, Cap::SSRF);
assert_eq!(
lookup_impact(edge.primary_cap, None),
@@ -186,9 +177,6 @@ fn finding_in_file_with_no_entry_point_is_unreachable() {
#[test]
fn feasibility_defaults_to_unverified() {
let surface = synthetic_surface("app.py", "/");
- let edge = single_edge(
- diag_with_caps("app.py", 1, Cap::CODE_EXEC),
- &surface,
- );
+ let edge = single_edge(diag_with_caps("app.py", 1, Cap::CODE_EXEC), &surface);
assert_eq!(edge.feasibility, Feasibility::Unverified);
}
diff --git a/tests/chain_emission.rs b/tests/chain_emission.rs
index 762282e8..9501c2ce 100644
--- a/tests/chain_emission.rs
+++ b/tests/chain_emission.rs
@@ -88,7 +88,12 @@ fn fixture_findings() -> Vec {
d
};
vec![
- mk(10, "cfg-cors-allow-all", Cap::HEADER_INJECTION, Severity::Medium),
+ mk(
+ 10,
+ "cfg-cors-allow-all",
+ Cap::HEADER_INJECTION,
+ Severity::Medium,
+ ),
mk(15, "cfg-auth-gap", Cap::UNAUTHORIZED_ID, Severity::Medium),
mk(25, "taint-shell-exec", Cap::CODE_EXEC, Severity::High),
]
@@ -129,7 +134,11 @@ fn cors_plus_noauth_plus_websocket_emits_one_critical_chain() {
min_score: 0.0,
},
);
- assert_eq!(chains.len(), 1, "expected exactly one chain, got {chains:?}");
+ assert_eq!(
+ chains.len(),
+ 1,
+ "expected exactly one chain, got {chains:?}"
+ );
let chain = &chains[0];
assert_eq!(chain.implied_impact, ImpactCategory::BrowserToLocalRce);
assert_eq!(chain.severity, ChainSeverity::Critical);
@@ -213,11 +222,7 @@ fn sarif_output_validates_against_v210_shape() {
min_score: 0.0,
},
);
- let sarif = build_sarif_with_chains(
- &findings,
- &chains,
- std::path::Path::new("."),
- );
+ let sarif = build_sarif_with_chains(&findings, &chains, std::path::Path::new("."));
// Surface-level v2.1.0 invariants — the SARIF schema requires
// these fields and we want a tripwire if any disappear.
diff --git a/tests/chain_emission_e2e.rs b/tests/chain_emission_e2e.rs
index 432e698d..e7fc890c 100644
--- a/tests/chain_emission_e2e.rs
+++ b/tests/chain_emission_e2e.rs
@@ -311,8 +311,8 @@ fn flask_eval_chain_dynamic_verdict_is_null_when_verify_disabled() {
.success();
let stdout = String::from_utf8(assert.get_output().stdout.clone())
.expect("nyx scan stdout is valid UTF-8");
- let value: Value = serde_json::from_str(&stdout)
- .expect("nyx scan --format json produced invalid JSON");
+ let value: Value =
+ serde_json::from_str(&stdout).expect("nyx scan --format json produced invalid JSON");
let chains = value
.get("chains")
diff --git a/tests/chain_reverify.rs b/tests/chain_reverify.rs
index 3e0ef1f2..77a47361 100644
--- a/tests/chain_reverify.rs
+++ b/tests/chain_reverify.rs
@@ -193,8 +193,14 @@ fn compose_chain_step_threads_prev_output_for_every_emitter() {
"{lang:?} emitter must thread NYX_PREV_OUTPUT via extra_env; got {:?}",
step.extra_env
);
- assert!(!step.source.is_empty(), "{lang:?} step source must be non-empty");
- assert!(!step.command.is_empty(), "{lang:?} step command must be non-empty");
+ assert!(
+ !step.source.is_empty(),
+ "{lang:?} step source must be non-empty"
+ );
+ assert!(
+ !step.command.is_empty(),
+ "{lang:?} step command must be non-empty"
+ );
assert!(
!step.source.contains(ChainStepHarness::SINK_HIT_SENTINEL),
"{lang:?} non-terminal step must NOT carry the sink-hit sentinel; got source:\n{}",
diff --git a/tests/class_method_corpus.rs b/tests/class_method_corpus.rs
index 4cbc587c..47fb34e4 100644
--- a/tests/class_method_corpus.rs
+++ b/tests/class_method_corpus.rs
@@ -16,7 +16,7 @@
use nyx_scanner::dynamic::lang;
use nyx_scanner::dynamic::spec::{EntryKind, EntryKindTag, HarnessSpec, PayloadSlot};
-use nyx_scanner::dynamic::stubs::{mock_source, MockKind};
+use nyx_scanner::dynamic::stubs::{MockKind, mock_source};
use nyx_scanner::labels::Cap;
use nyx_scanner::symbol::Lang;
diff --git a/tests/cli_unsafe_sandbox.rs b/tests/cli_unsafe_sandbox.rs
index 91e70dd3..c4e806fc 100644
--- a/tests/cli_unsafe_sandbox.rs
+++ b/tests/cli_unsafe_sandbox.rs
@@ -28,11 +28,9 @@ mod dynamic_sandbox_cli {
fn unsafe_sandbox_with_docker_backend_is_rejected() {
let mut cmd = scan_cmd_with_fresh_env();
cmd.args(["--unsafe-sandbox", "--backend", "docker"]);
- cmd.assert()
- .failure()
- .stderr(predicate::str::contains(
- "--unsafe-sandbox and --backend docker are mutually exclusive",
- ));
+ cmd.assert().failure().stderr(predicate::str::contains(
+ "--unsafe-sandbox and --backend docker are mutually exclusive",
+ ));
}
/// `--unsafe-sandbox` alone (no explicit --backend) must NOT trigger the
diff --git a/tests/common/fixture_harness.rs b/tests/common/fixture_harness.rs
index 0fdaf543..9f19101e 100644
--- a/tests/common/fixture_harness.rs
+++ b/tests/common/fixture_harness.rs
@@ -14,10 +14,10 @@
//! failure, prompting an explicit golden update.
use nyx_scanner::commands::scan::Diag;
-use nyx_scanner::dynamic::verify::{verify_finding, VerifyOptions};
+use nyx_scanner::dynamic::verify::{VerifyOptions, verify_finding};
use nyx_scanner::evidence::{
- Confidence, EntryKind, Evidence, FlowStep, FlowStepKind, InconclusiveReason,
- UnsupportedReason, VerifyResult, VerifyStatus,
+ Confidence, EntryKind, Evidence, FlowStep, FlowStepKind, InconclusiveReason, UnsupportedReason,
+ VerifyResult, VerifyStatus,
};
use nyx_scanner::labels::Cap;
use nyx_scanner::patterns::{FindingCategory, Severity};
@@ -187,10 +187,7 @@ pub fn check_prerequisites(reqs: &[Prerequisite]) -> Result<(), SkipReason> {
Err(_) => return Err(SkipReason::MissingStaticLib(lib)),
};
use std::io::Write;
- let mut handle = match std::fs::OpenOptions::new()
- .write(true)
- .open(probe.path())
- {
+ let mut handle = match std::fs::OpenOptions::new().write(true).open(probe.path()) {
Ok(h) => h,
Err(_) => return Err(SkipReason::MissingStaticLib(lib)),
};
@@ -207,7 +204,9 @@ pub fn check_prerequisites(reqs: &[Prerequisite]) -> Result<(), SkipReason> {
};
let status = std::process::Command::new("cc")
.args([
- "-x", "c", "-static",
+ "-x",
+ "c",
+ "-static",
probe.path().to_str().unwrap_or(""),
"-o",
out.to_str().unwrap_or(""),
@@ -327,9 +326,8 @@ pub fn run_fixture_and_compare_to_golden(spec: &FixtureSpec<'_>) {
current_json.push('\n');
if std::env::var("NYX_UPDATE_GOLDENS").is_ok_and(|v| v == "1") {
- std::fs::write(&golden_path, ¤t_json).unwrap_or_else(|e| {
- panic!("write golden {}: {e}", golden_path.display())
- });
+ std::fs::write(&golden_path, ¤t_json)
+ .unwrap_or_else(|e| panic!("write golden {}: {e}", golden_path.display()));
return;
}
@@ -365,7 +363,9 @@ fn fixture_dir(lang_dir: &str) -> PathBuf {
fn stage_fixture(src: &Path, tmp: &TempDir, copy: CopyStrategy) -> PathBuf {
match copy {
CopyStrategy::PreserveName => {
- let dst = tmp.path().join(src.file_name().expect("fixture has filename"));
+ let dst = tmp
+ .path()
+ .join(src.file_name().expect("fixture has filename"));
std::fs::copy(src, &dst).expect("copy fixture into tempdir");
dst
}
@@ -435,7 +435,7 @@ pub fn run_shape_fixture_lang(
entry_kind: EntryKind,
payload_slot: nyx_scanner::dynamic::spec::PayloadSlot,
) -> VerifyResult {
- use nyx_scanner::dynamic::runner::{run_spec, RunError};
+ use nyx_scanner::dynamic::runner::{RunError, run_spec};
use nyx_scanner::dynamic::sandbox::SandboxOptions;
use nyx_scanner::dynamic::spec::{HarnessSpec, SpecDerivationStrategy};
@@ -801,9 +801,8 @@ pub fn run_harness_snapshot_lang(
.replace(file, "");
if std::env::var("NYX_UPDATE_GOLDENS").is_ok_and(|v| v == "1") {
- std::fs::write(&snapshot_path, &normalised).unwrap_or_else(|e| {
- panic!("write harness snapshot {}: {e}", snapshot_path.display())
- });
+ std::fs::write(&snapshot_path, &normalised)
+ .unwrap_or_else(|e| panic!("write harness snapshot {}: {e}", snapshot_path.display()));
return;
}
diff --git a/tests/cpp_fixtures.rs b/tests/cpp_fixtures.rs
index ee430863..3f2b1229 100644
--- a/tests/cpp_fixtures.rs
+++ b/tests/cpp_fixtures.rs
@@ -15,7 +15,7 @@ mod common;
#[cfg(feature = "dynamic")]
mod cpp_fixture_tests {
- use crate::common::fixture_harness::{run_shape_fixture_lang_or_skip, Prerequisite};
+ use crate::common::fixture_harness::{Prerequisite, run_shape_fixture_lang_or_skip};
use nyx_scanner::dynamic::spec::PayloadSlot;
use nyx_scanner::evidence::{EntryKind, VerifyResult, VerifyStatus};
use nyx_scanner::labels::Cap;
@@ -64,7 +64,16 @@ mod cpp_fixture_tests {
slot: PayloadSlot,
) -> Option {
run_shape_fixture_lang_or_skip(
- CXX_REQ, Lang::Cpp, "cpp", shape, file, func, cap, sink_line, kind, slot,
+ CXX_REQ,
+ Lang::Cpp,
+ "cpp",
+ shape,
+ file,
+ func,
+ cap,
+ sink_line,
+ kind,
+ slot,
)
}
@@ -73,18 +82,32 @@ mod cpp_fixture_tests {
#[test]
fn main_argv_vuln_is_confirmed() {
let Some(r) = run(
- "main_argv", "vuln.cpp", "nyx_entry_main", Cap::CODE_EXEC, 16,
- EntryKind::CliSubcommand, PayloadSlot::Argv(0),
- ) else { return; };
+ "main_argv",
+ "vuln.cpp",
+ "nyx_entry_main",
+ Cap::CODE_EXEC,
+ 16,
+ EntryKind::CliSubcommand,
+ PayloadSlot::Argv(0),
+ ) else {
+ return;
+ };
assert_confirmed("main_argv", &r);
}
#[test]
fn main_argv_benign_not_confirmed() {
let Some(r) = run(
- "main_argv", "benign.cpp", "nyx_entry_main", Cap::CODE_EXEC, 11,
- EntryKind::CliSubcommand, PayloadSlot::Argv(0),
- ) else { return; };
+ "main_argv",
+ "benign.cpp",
+ "nyx_entry_main",
+ Cap::CODE_EXEC,
+ 11,
+ EntryKind::CliSubcommand,
+ PayloadSlot::Argv(0),
+ ) else {
+ return;
+ };
assert_not_confirmed("main_argv", &r);
}
@@ -93,18 +116,32 @@ mod cpp_fixture_tests {
#[test]
fn libfuzzer_vuln_is_confirmed() {
let Some(r) = run(
- "libfuzzer", "vuln.cpp", "LLVMFuzzerTestOneInput", Cap::CODE_EXEC, 15,
- EntryKind::LibraryApi, PayloadSlot::Param(0),
- ) else { return; };
+ "libfuzzer",
+ "vuln.cpp",
+ "LLVMFuzzerTestOneInput",
+ Cap::CODE_EXEC,
+ 15,
+ EntryKind::LibraryApi,
+ PayloadSlot::Param(0),
+ ) else {
+ return;
+ };
assert_confirmed("libfuzzer", &r);
}
#[test]
fn libfuzzer_benign_not_confirmed() {
let Some(r) = run(
- "libfuzzer", "benign.cpp", "LLVMFuzzerTestOneInput", Cap::CODE_EXEC, 10,
- EntryKind::LibraryApi, PayloadSlot::Param(0),
- ) else { return; };
+ "libfuzzer",
+ "benign.cpp",
+ "LLVMFuzzerTestOneInput",
+ Cap::CODE_EXEC,
+ 10,
+ EntryKind::LibraryApi,
+ PayloadSlot::Param(0),
+ ) else {
+ return;
+ };
assert_not_confirmed("libfuzzer", &r);
}
@@ -113,18 +150,32 @@ mod cpp_fixture_tests {
#[test]
fn free_fn_vuln_is_confirmed() {
let Some(r) = run(
- "free_fn", "vuln.cpp", "run", Cap::CODE_EXEC, 12,
- EntryKind::Function, PayloadSlot::Param(0),
- ) else { return; };
+ "free_fn",
+ "vuln.cpp",
+ "run",
+ Cap::CODE_EXEC,
+ 12,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
+ ) else {
+ return;
+ };
assert_confirmed("free_fn", &r);
}
#[test]
fn free_fn_benign_not_confirmed() {
let Some(r) = run(
- "free_fn", "benign.cpp", "run", Cap::CODE_EXEC, 10,
- EntryKind::Function, PayloadSlot::Param(0),
- ) else { return; };
+ "free_fn",
+ "benign.cpp",
+ "run",
+ Cap::CODE_EXEC,
+ 10,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
+ ) else {
+ return;
+ };
assert_not_confirmed("free_fn", &r);
}
}
diff --git a/tests/crypto_corpus.rs b/tests/crypto_corpus.rs
index 43a1a79a..a5c50172 100644
--- a/tests/crypto_corpus.rs
+++ b/tests/crypto_corpus.rs
@@ -13,20 +13,14 @@
#![cfg(feature = "dynamic")]
use nyx_scanner::dynamic::corpus::{payloads_for_lang, resolve_benign_control_lang};
-use nyx_scanner::dynamic::oracle::{oracle_fired, Oracle, ProbePredicate};
+use nyx_scanner::dynamic::oracle::{Oracle, ProbePredicate, oracle_fired};
use nyx_scanner::dynamic::probe::{ProbeKind, ProbeWitness, SinkProbe};
use nyx_scanner::dynamic::sandbox::SandboxOutcome;
use nyx_scanner::labels::Cap;
use nyx_scanner::symbol::Lang;
use std::time::Duration;
-const LANGS: &[Lang] = &[
- Lang::Java,
- Lang::Python,
- Lang::Php,
- Lang::Go,
- Lang::Rust,
-];
+const LANGS: &[Lang] = &[Lang::Java, Lang::Python, Lang::Php, Lang::Go, Lang::Rust];
fn outcome() -> SandboxOutcome {
SandboxOutcome {
@@ -72,19 +66,17 @@ fn corpus_registers_crypto_for_each_supported_lang() {
fn crypto_payloads_pair_benign_controls_per_lang() {
for lang in LANGS {
let slice = payloads_for_lang(Cap::CRYPTO, *lang);
- let vuln = slice
- .iter()
- .find(|p| !p.is_benign)
- .expect("vuln payload");
- let resolved = resolve_benign_control_lang(vuln, Cap::CRYPTO, *lang)
- .expect("benign control resolves");
+ let vuln = slice.iter().find(|p| !p.is_benign).expect("vuln payload");
+ let resolved =
+ resolve_benign_control_lang(vuln, Cap::CRYPTO, *lang).expect("benign control resolves");
assert!(resolved.is_benign);
match &vuln.oracle {
Oracle::SinkProbe { predicates } => {
- assert!(predicates.iter().any(|p| matches!(
- p,
- ProbePredicate::WeakKeyEntropy { max_bits: 16 }
- )));
+ assert!(
+ predicates
+ .iter()
+ .any(|p| matches!(p, ProbePredicate::WeakKeyEntropy { max_bits: 16 }))
+ );
}
other => panic!("expected SinkProbe, got {other:?}"),
}
@@ -119,7 +111,13 @@ fn weak_key_entropy_clears_with_no_probe() {
#[test]
fn crypto_unsupported_for_other_langs() {
- for lang in [Lang::C, Lang::Cpp, Lang::Ruby, Lang::JavaScript, Lang::TypeScript] {
+ for lang in [
+ Lang::C,
+ Lang::Cpp,
+ Lang::Ruby,
+ Lang::JavaScript,
+ Lang::TypeScript,
+ ] {
assert!(
payloads_for_lang(Cap::CRYPTO, lang).is_empty(),
"CRYPTO has unexpected payloads for {lang:?}",
diff --git a/tests/data_exfil_corpus.rs b/tests/data_exfil_corpus.rs
index a70d1915..cd180d10 100644
--- a/tests/data_exfil_corpus.rs
+++ b/tests/data_exfil_corpus.rs
@@ -14,7 +14,7 @@
#![cfg(feature = "dynamic")]
use nyx_scanner::dynamic::corpus::{payloads_for_lang, resolve_benign_control_lang};
-use nyx_scanner::dynamic::oracle::{oracle_fired, Oracle, ProbePredicate};
+use nyx_scanner::dynamic::oracle::{Oracle, ProbePredicate, oracle_fired};
use nyx_scanner::dynamic::probe::{ProbeKind, ProbeWitness, SinkProbe};
use nyx_scanner::dynamic::sandbox::SandboxOutcome;
use nyx_scanner::labels::Cap;
@@ -76,10 +76,11 @@ fn data_exfil_payloads_pair_benign_per_lang() {
.expect("benign control resolves");
assert!(resolved.is_benign);
match &vuln.oracle {
- Oracle::SinkProbe { predicates } => assert!(predicates.iter().any(|p| matches!(
- p,
- ProbePredicate::OutboundHostNotIn { .. }
- ))),
+ Oracle::SinkProbe { predicates } => assert!(
+ predicates
+ .iter()
+ .any(|p| matches!(p, ProbePredicate::OutboundHostNotIn { .. }))
+ ),
other => panic!("expected SinkProbe, got {other:?}"),
}
}
diff --git a/tests/deserialize_corpus.rs b/tests/deserialize_corpus.rs
index 98b16d8d..bb798f0f 100644
--- a/tests/deserialize_corpus.rs
+++ b/tests/deserialize_corpus.rs
@@ -13,8 +13,8 @@
mod common;
use nyx_scanner::dynamic::corpus::{
- audit_marker_collisions, benign_payload_for_lang, payloads_for_lang,
- resolve_benign_control_lang, Oracle,
+ Oracle, audit_marker_collisions, benign_payload_for_lang, payloads_for_lang,
+ resolve_benign_control_lang,
};
use nyx_scanner::dynamic::framework::registry::adapters_for;
use nyx_scanner::dynamic::lang;
@@ -105,7 +105,9 @@ fn payload_oracle_carries_deserialize_predicate() {
assert!(
predicates.iter().any(|p| matches!(
p,
- ProbePredicate::DeserializeGadgetInvoked { require_invoked: true }
+ ProbePredicate::DeserializeGadgetInvoked {
+ require_invoked: true
+ }
)),
"{lang:?} vuln payload missing DeserializeGadgetInvoked predicate",
);
@@ -166,8 +168,8 @@ fn lang_emitter_dispatches_to_deserialize_harness() {
),
] {
let spec = make_spec(lang, entry_file, entry_name);
- let harness = lang::emit(&spec)
- .unwrap_or_else(|e| panic!("emit failed for {lang:?}: {e:?}"));
+ let harness =
+ lang::emit(&spec).unwrap_or_else(|e| panic!("emit failed for {lang:?}: {e:?}"));
assert!(
harness.source.contains("NYX_GADGET_CLASS:"),
"{lang:?} deserialize harness must parse NYX_GADGET_CLASS marker",
@@ -187,10 +189,19 @@ fn framework_adapters_detect_deserialize_sink() {
// EntryKind::Function binding when the fixture contains the
// canonical sink call.
for (lang, fixture) in [
- (Lang::Java, "tests/dynamic_fixtures/deserialize/java/Vuln.java"),
- (Lang::Python, "tests/dynamic_fixtures/deserialize/python/vuln.py"),
+ (
+ Lang::Java,
+ "tests/dynamic_fixtures/deserialize/java/Vuln.java",
+ ),
+ (
+ Lang::Python,
+ "tests/dynamic_fixtures/deserialize/python/vuln.py",
+ ),
(Lang::Php, "tests/dynamic_fixtures/deserialize/php/vuln.php"),
- (Lang::Ruby, "tests/dynamic_fixtures/deserialize/ruby/vuln.rb"),
+ (
+ Lang::Ruby,
+ "tests/dynamic_fixtures/deserialize/ruby/vuln.rb",
+ ),
] {
let bytes = std::fs::read(fixture).expect("fixture exists");
let ts_lang = ts_language_for(lang);
@@ -204,19 +215,15 @@ fn framework_adapters_detect_deserialize_sink() {
..Default::default()
};
let registry_slice = adapters_for(lang);
- assert!(
- !registry_slice.is_empty(),
- "{lang:?} adapter slice empty",
- );
+ assert!(!registry_slice.is_empty(), "{lang:?} adapter slice empty",);
let binding = nyx_scanner::dynamic::framework::detect_binding(
&summary,
tree.root_node(),
&bytes,
lang,
);
- let b = binding.unwrap_or_else(|| {
- panic!("{lang:?} adapter must detect the deserialize sink fixture")
- });
+ let b = binding
+ .unwrap_or_else(|| panic!("{lang:?} adapter must detect the deserialize sink fixture"));
assert_eq!(b.kind, EntryKind::Function);
assert!(!b.adapter.is_empty());
}
@@ -262,10 +269,10 @@ fn slug(lang: Lang) -> &'static str {
mod e2e_phase_03 {
use crate::common::fixture_harness::FIXTURE_LOCK;
- use nyx_scanner::dynamic::runner::{run_spec, RunError, RunOutcome};
+ use nyx_scanner::dynamic::runner::{RunError, RunOutcome, run_spec};
use nyx_scanner::dynamic::sandbox::SandboxOptions;
use nyx_scanner::dynamic::spec::{
- default_toolchain_id, EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy,
+ EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy, default_toolchain_id,
};
use nyx_scanner::evidence::DifferentialVerdict;
use nyx_scanner::labels::Cap;
@@ -383,7 +390,9 @@ mod e2e_phase_03 {
/// an allow-listed class name and writes no probe).
#[test]
fn java_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Java, "Vuln.java", "run") else { return };
+ let Some(outcome) = run(Lang::Java, "Vuln.java", "run") else {
+ return;
+ };
assert!(
outcome.triggered_by.is_some(),
"Java DESERIALIZE vuln must Confirm via run_spec; got {outcome:?}",
@@ -401,7 +410,9 @@ mod e2e_phase_03 {
#[test]
fn python_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Python, "vuln.py", "run") else { return };
+ let Some(outcome) = run(Lang::Python, "vuln.py", "run") else {
+ return;
+ };
assert!(
outcome.triggered_by.is_some(),
"Python DESERIALIZE vuln must Confirm via run_spec; got {outcome:?}",
@@ -415,7 +426,9 @@ mod e2e_phase_03 {
#[test]
fn php_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Php, "vuln.php", "run") else { return };
+ let Some(outcome) = run(Lang::Php, "vuln.php", "run") else {
+ return;
+ };
assert!(
outcome.triggered_by.is_some(),
"PHP DESERIALIZE vuln must Confirm via run_spec; got {outcome:?}",
@@ -429,7 +442,9 @@ mod e2e_phase_03 {
#[test]
fn ruby_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Ruby, "vuln.rb", "run") else { return };
+ let Some(outcome) = run(Lang::Ruby, "vuln.rb", "run") else {
+ return;
+ };
assert!(
outcome.triggered_by.is_some(),
"Ruby DESERIALIZE vuln must Confirm via run_spec; got {outcome:?}",
diff --git a/tests/determinism_audit.rs b/tests/determinism_audit.rs
index 0d3652a5..3fbd449f 100644
--- a/tests/determinism_audit.rs
+++ b/tests/determinism_audit.rs
@@ -15,7 +15,7 @@
use nyx_scanner::commands::scan::Diag;
use nyx_scanner::dynamic::telemetry::{self, SamplingPolicy};
-use nyx_scanner::dynamic::verify::{verify_finding, VerifyOptions};
+use nyx_scanner::dynamic::verify::{VerifyOptions, verify_finding};
use nyx_scanner::evidence::{Confidence, Evidence, VerifyStatus};
use nyx_scanner::patterns::{FindingCategory, Severity};
use serde_json::Value;
@@ -99,10 +99,7 @@ fn ten_runs_produce_byte_identical_telemetry_minus_timestamps() {
// Drop `differential` and any future timestamped field by
// round-tripping through serde; structural equality is the
// contract.
- verdict_jsons.insert(
- serde_json::to_string(&result)
- .expect("VerifyResult serialises"),
- );
+ verdict_jsons.insert(serde_json::to_string(&result).expect("VerifyResult serialises"));
}
assert_eq!(
verdict_jsons.len(),
@@ -243,10 +240,7 @@ fn confirmed_run_is_byte_identical_across_runs() {
// every run reads + writes the same absolute paths (the per-run path
// would otherwise leak into VerifyResult and break determinism).
unsafe {
- std::env::set_var(
- "NYX_REPRO_BASE",
- tmp.path().join("repro").to_str().unwrap(),
- );
+ std::env::set_var("NYX_REPRO_BASE", tmp.path().join("repro").to_str().unwrap());
std::env::set_var(
"NYX_TELEMETRY_PATH",
tmp.path().join("events.jsonl").to_str().unwrap(),
@@ -370,10 +364,7 @@ fn policy_deny_excerpt_is_stable_across_runs() {
.inconclusive_reason
.expect("expected PolicyDeniedDynamic on deny path")
{
- nyx_scanner::evidence::InconclusiveReason::PolicyDeniedDynamic {
- excerpt,
- ..
- } => {
+ nyx_scanner::evidence::InconclusiveReason::PolicyDeniedDynamic { excerpt, .. } => {
excerpts.insert(excerpt);
}
other => panic!("expected PolicyDeniedDynamic, got {other:?}"),
diff --git a/tests/dynamic_parity.rs b/tests/dynamic_parity.rs
index ffb0ea07..a7ed8c46 100644
--- a/tests/dynamic_parity.rs
+++ b/tests/dynamic_parity.rs
@@ -16,8 +16,8 @@
#[cfg(feature = "dynamic")]
mod parity_tests {
use nyx_scanner::commands::scan::Diag;
- use nyx_scanner::dynamic::verify::{verify_finding, VerifyOptions};
use nyx_scanner::dynamic::sandbox::{SandboxBackend, SandboxOptions};
+ use nyx_scanner::dynamic::verify::{VerifyOptions, verify_finding};
use nyx_scanner::evidence::{Confidence, Evidence, FlowStep, FlowStepKind, VerifyStatus};
use nyx_scanner::labels::Cap;
use nyx_scanner::patterns::{FindingCategory, Severity};
@@ -118,8 +118,11 @@ mod parity_tests {
}
/// Assert two verdicts agree on status (and on reason for non-Confirmed).
- fn assert_parity(fixture: &str, process_result: &nyx_scanner::evidence::VerifyResult,
- docker_result: &nyx_scanner::evidence::VerifyResult) {
+ fn assert_parity(
+ fixture: &str,
+ process_result: &nyx_scanner::evidence::VerifyResult,
+ docker_result: &nyx_scanner::evidence::VerifyResult,
+ ) {
// Docker reachability fluctuates per host: `docker info` may exit 0
// (daemon listening) while the sandbox's container-start path still
// fails (image not pulled, socket gated by Docker Desktop's
@@ -128,16 +131,20 @@ mod parity_tests {
// where the error surfaces, so the skip predicate looks at the
// reason text, not the verdict status.
if let Some(ref r) = docker_result.reason
- && format!("{r:?}").contains("BackendUnavailable") {
- return; // Docker absent — skip comparison.
- }
+ && format!("{r:?}").contains("BackendUnavailable")
+ {
+ return; // Docker absent — skip comparison.
+ }
assert_eq!(
- process_result.status, docker_result.status,
+ process_result.status,
+ docker_result.status,
"fixture {fixture}: status mismatch: process={:?} docker={:?}\n\
process detail: {:?}\ndocker detail: {:?}",
- process_result.status, docker_result.status,
- process_result.detail, docker_result.detail,
+ process_result.status,
+ docker_result.status,
+ process_result.detail,
+ docker_result.detail,
);
// For non-Confirmed statuses, the reason must also match.
@@ -154,7 +161,9 @@ mod parity_tests {
/// Helper: run a fixture through both backends and assert parity.
fn parity_check(fixture: &str, function: &str, sink_line: u32, cap: Cap) {
- if !docker_available() { return; }
+ if !docker_available() {
+ return;
+ }
let diag = python_diag(fixture, function, sink_line, cap);
let process_result = verify_finding(&diag, &process_opts());
@@ -266,7 +275,9 @@ mod parity_tests {
/// Rust finding (lang unsupported) must return same status on both backends.
#[test]
fn parity_rust_lang_unsupported() {
- if !docker_available() { return; }
+ if !docker_available() {
+ return;
+ }
let diag = python_diag("src/handler.rs", "handle_request", 10, Cap::SQL_QUERY);
let process_result = verify_finding(&diag, &process_opts());
diff --git a/tests/dynamic_sandbox_escape.rs b/tests/dynamic_sandbox_escape.rs
index db92c59f..40ee5403 100644
--- a/tests/dynamic_sandbox_escape.rs
+++ b/tests/dynamic_sandbox_escape.rs
@@ -115,7 +115,8 @@ mod escape_tests {
let stdout = std::str::from_utf8(&outcome.stdout).unwrap_or("");
let stderr = std::str::from_utf8(&outcome.stderr).unwrap_or("");
assert!(
- !stdout.contains("NYX_ESCAPE_SUCCESS") && !stderr.contains("NYX_ESCAPE_SUCCESS"),
+ !stdout.contains("NYX_ESCAPE_SUCCESS")
+ && !stderr.contains("NYX_ESCAPE_SUCCESS"),
"fixture {fixture}: escape succeeded!\nstdout: {stdout}\nstderr: {stderr}"
);
@@ -143,7 +144,9 @@ mod escape_tests {
($name:ident, $fixture:literal) => {
#[test]
fn $name() {
- if !docker_available() { return; }
+ if !docker_available() {
+ return;
+ }
let (_tmpdir, harness) = harness_for_fixture($fixture);
let result = sandbox::run(&harness, &noop_payload(), &escape_opts());
assert_no_escape(result, $fixture, None);
@@ -157,7 +160,9 @@ mod escape_tests {
#[cfg(target_os = "linux")]
#[test]
fn $name() {
- if !docker_available() { return; }
+ if !docker_available() {
+ return;
+ }
let (_tmpdir, harness) = harness_for_fixture($fixture);
let result = sandbox::run(&harness, &noop_payload(), &escape_opts());
assert_no_escape(result, $fixture, None);
@@ -166,7 +171,9 @@ mod escape_tests {
($name:ident, $fixture:literal, marker = $marker:expr) => {
#[test]
fn $name() {
- if !docker_available() { return; }
+ if !docker_available() {
+ return;
+ }
let marker: PathBuf = PathBuf::from($marker);
// Remove stale marker before test.
let _ = fs::remove_file(&marker);
@@ -181,7 +188,9 @@ mod escape_tests {
#[cfg(target_os = "linux")]
#[test]
fn $name() {
- if !docker_available() { return; }
+ if !docker_available() {
+ return;
+ }
let marker: PathBuf = PathBuf::from($marker);
let _ = fs::remove_file(&marker);
let (_tmpdir, harness) = harness_for_fixture($fixture);
@@ -236,20 +245,20 @@ mod escape_tests {
/// Skips gracefully when Docker is unavailable or `rust:slim` is not pulled.
#[test]
fn escape_rust_malicious_build_rs() {
- if !docker_available() { return; }
+ if !docker_available() {
+ return;
+ }
let tmpdir = tempfile::TempDir::new().expect("temp dir");
let fixture = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/dynamic_fixtures/escape/rust_build_rs");
- copy_dir_recursive(&fixture, tmpdir.path())
- .expect("copy rust_build_rs fixture");
+ copy_dir_recursive(&fixture, tmpdir.path()).expect("copy rust_build_rs fixture");
let marker: PathBuf = PathBuf::from("/tmp/pwned_build_rs");
let _ = fs::remove_file(&marker);
// Run Docker-isolated cargo build. Returns Err if Docker/image unavailable.
- let result =
- nyx_scanner::dynamic::build_sandbox::prepare_rust_in_docker(tmpdir.path());
+ let result = nyx_scanner::dynamic::build_sandbox::prepare_rust_in_docker(tmpdir.path());
if result.is_err() {
// Docker or rust:slim unavailable — no container ran.
return;
@@ -274,19 +283,19 @@ mod escape_tests {
/// Skips gracefully when Docker is unavailable or `node:20-slim` is not pulled.
#[test]
fn escape_npm_malicious_lifecycle() {
- if !docker_available() { return; }
+ if !docker_available() {
+ return;
+ }
let tmpdir = tempfile::TempDir::new().expect("temp dir");
let fixture = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/dynamic_fixtures/escape/npm_malicious_lifecycle");
- copy_dir_recursive(&fixture, tmpdir.path())
- .expect("copy npm_malicious_lifecycle fixture");
+ copy_dir_recursive(&fixture, tmpdir.path()).expect("copy npm_malicious_lifecycle fixture");
let marker: PathBuf = PathBuf::from("/tmp/pwned_npm_lifecycle");
let _ = fs::remove_file(&marker);
- let result =
- nyx_scanner::dynamic::build_sandbox::prepare_node_in_docker(tmpdir.path());
+ let result = nyx_scanner::dynamic::build_sandbox::prepare_node_in_docker(tmpdir.path());
if result.is_err() {
return;
}
@@ -310,20 +319,20 @@ mod escape_tests {
/// Skips gracefully when Docker is unavailable or `golang:1.21-slim` is not pulled.
#[test]
fn escape_go_malicious_init() {
- if !docker_available() { return; }
+ if !docker_available() {
+ return;
+ }
let tmpdir = tempfile::TempDir::new().expect("temp dir");
let fixture = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/dynamic_fixtures/escape/go_malicious_init_main");
- copy_dir_recursive(&fixture, tmpdir.path())
- .expect("copy go_malicious_init_main fixture");
+ copy_dir_recursive(&fixture, tmpdir.path()).expect("copy go_malicious_init_main fixture");
let marker: PathBuf = PathBuf::from("/tmp/pwned_go_init");
let _ = fs::remove_file(&marker);
// Docker-isolated go build: init() does not run during compilation.
- let result =
- nyx_scanner::dynamic::build_sandbox::prepare_go_in_docker(tmpdir.path());
+ let result = nyx_scanner::dynamic::build_sandbox::prepare_go_in_docker(tmpdir.path());
if result.is_err() {
return;
}
@@ -346,19 +355,19 @@ mod escape_tests {
/// Skips gracefully when Docker is unavailable or the Maven image is not pulled.
#[test]
fn escape_maven_malicious_plugin() {
- if !docker_available() { return; }
+ if !docker_available() {
+ return;
+ }
let tmpdir = tempfile::TempDir::new().expect("temp dir");
let fixture = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/dynamic_fixtures/escape/maven_malicious_plugin");
- copy_dir_recursive(&fixture, tmpdir.path())
- .expect("copy maven_malicious_plugin fixture");
+ copy_dir_recursive(&fixture, tmpdir.path()).expect("copy maven_malicious_plugin fixture");
let marker: PathBuf = PathBuf::from("/tmp/pwned_maven_plugin");
let _ = fs::remove_file(&marker);
- let result =
- nyx_scanner::dynamic::build_sandbox::prepare_java_in_docker(tmpdir.path());
+ let result = nyx_scanner::dynamic::build_sandbox::prepare_java_in_docker(tmpdir.path());
if result.is_err() {
return;
}
@@ -380,7 +389,9 @@ mod escape_tests {
/// Skips gracefully when Docker is unavailable or `composer:2` is not pulled.
#[test]
fn escape_composer_malicious_postinstall() {
- if !docker_available() { return; }
+ if !docker_available() {
+ return;
+ }
let tmpdir = tempfile::TempDir::new().expect("temp dir");
let fixture = Path::new(env!("CARGO_MANIFEST_DIR"))
@@ -391,8 +402,7 @@ mod escape_tests {
let marker: PathBuf = PathBuf::from("/tmp/pwned_composer_postinstall");
let _ = fs::remove_file(&marker);
- let result =
- nyx_scanner::dynamic::build_sandbox::prepare_php_in_docker(tmpdir.path());
+ let result = nyx_scanner::dynamic::build_sandbox::prepare_php_in_docker(tmpdir.path());
if result.is_err() {
return;
}
@@ -434,12 +444,17 @@ mod escape_tests {
let container_name = format!("nyx-posctl-{}", std::process::id());
let status = std::process::Command::new("docker")
.args([
- "run", "-d", "--rm",
- "--name", &container_name,
+ "run",
+ "-d",
+ "--rm",
+ "--name",
+ &container_name,
"--cap-add=SYS_ADMIN",
- "--network", "none",
+ "--network",
+ "none",
"python:3-slim",
- "sleep", "60",
+ "sleep",
+ "60",
])
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
@@ -470,8 +485,10 @@ mod escape_tests {
// Run the fixture and capture output.
let out = std::process::Command::new("docker")
.args([
- "exec", &container_name,
- "python3", "/workdir/cap_sys_admin_positive_control.py",
+ "exec",
+ &container_name,
+ "python3",
+ "/workdir/cap_sys_admin_positive_control.py",
])
.output()
.expect("docker exec positive control");
@@ -503,7 +520,9 @@ mod escape_tests {
/// the container registry holds one entry (started once, reused once).
#[test]
fn docker_exec_reuse_for_same_workdir() {
- if !docker_available() { return; }
+ if !docker_available() {
+ return;
+ }
let (_tmpdir, harness) = harness_for_fixture("dns_leak.py");
let opts = escape_opts();
@@ -524,7 +543,9 @@ mod escape_tests {
// Verify the container is still running (not torn down between calls).
// Container name is derived from the workdir path.
- let spec_hash = _tmpdir.path().file_name()
+ let spec_hash = _tmpdir
+ .path()
+ .file_name()
.and_then(|n| n.to_str())
.unwrap_or("");
let container_name = format!("nyx-{spec_hash}");
@@ -535,10 +556,7 @@ mod escape_tests {
match out {
Ok(o) if o.status.success() => {
- let running = std::str::from_utf8(&o.stdout)
- .unwrap_or("")
- .trim()
- == "true";
+ let running = std::str::from_utf8(&o.stdout).unwrap_or("").trim() == "true";
// Container should still be running (exec reuse kept it alive).
assert!(
running,
diff --git a/tests/dynamic_verify_e2e.rs b/tests/dynamic_verify_e2e.rs
index f6cf84ab..a61127a1 100644
--- a/tests/dynamic_verify_e2e.rs
+++ b/tests/dynamic_verify_e2e.rs
@@ -18,8 +18,10 @@
#[cfg(feature = "dynamic")]
mod verify_e2e {
use nyx_scanner::commands::scan::Diag;
- use nyx_scanner::dynamic::verify::{verify_finding, VerifyOptions};
- use nyx_scanner::evidence::{Confidence, Evidence, FlowStep, FlowStepKind, UnsupportedReason, VerifyStatus};
+ use nyx_scanner::dynamic::verify::{VerifyOptions, verify_finding};
+ use nyx_scanner::evidence::{
+ Confidence, Evidence, FlowStep, FlowStepKind, UnsupportedReason, VerifyStatus,
+ };
use nyx_scanner::labels::Cap;
use nyx_scanner::patterns::{FindingCategory, Severity};
@@ -243,9 +245,15 @@ mod verify_e2e {
let v: serde_json::Value = serde_json::from_str(&json).expect("must be valid JSON");
assert!(v.get("status").is_some(), "status field must be present");
- assert!(v.get("triggered_payload").is_none(), "triggered_payload must be absent");
+ assert!(
+ v.get("triggered_payload").is_none(),
+ "triggered_payload must be absent"
+ );
assert!(v.get("detail").is_none(), "detail must be absent");
- assert!(v.get("attempts").is_none(), "attempts must be absent (empty vec skipped)");
+ assert!(
+ v.get("attempts").is_none(),
+ "attempts must be absent (empty vec skipped)"
+ );
assert!(v["finding_id"].is_string());
}
}
diff --git a/tests/env_capture_flask.rs b/tests/env_capture_flask.rs
index 76541290..75c5ca93 100644
--- a/tests/env_capture_flask.rs
+++ b/tests/env_capture_flask.rs
@@ -23,8 +23,8 @@
#![cfg(feature = "dynamic")]
use nyx_scanner::dynamic::environment::{
- capture_project_dependencies, capture_project_dependencies_with_context,
- stage_workdir_full, MAX_WORKDIR_BYTES,
+ MAX_WORKDIR_BYTES, capture_project_dependencies, capture_project_dependencies_with_context,
+ stage_workdir_full,
};
use nyx_scanner::dynamic::lang::materialize_runtime;
use nyx_scanner::dynamic::spec::{EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy};
@@ -108,7 +108,11 @@ fn capture_returns_three_deps_plus_flask() {
assert!(!captured.toolchain.toolchain_drift);
// Manifests resolved: requirements.txt and pyproject.toml.
- assert!(captured.lockfile.is_some(), "lockfile = {:?}", captured.lockfile);
+ assert!(
+ captured.lockfile.is_some(),
+ "lockfile = {:?}",
+ captured.lockfile
+ );
let manifest_names: Vec = captured
.manifests
.iter()
@@ -255,7 +259,7 @@ fn callgraph_context_extends_source_closure() {
// reverse-edge walk discovered (here just one file because the
// fixture is single-file).
use nyx_scanner::ast::analyse_file_fused;
- use nyx_scanner::callgraph::{build_call_graph};
+ use nyx_scanner::callgraph::build_call_graph;
use nyx_scanner::summary::GlobalSummaries;
use nyx_scanner::utils::config::{AnalysisMode, Config};
@@ -268,8 +272,8 @@ fn callgraph_context_extends_source_closure() {
let root = fixture_root();
let app = root.join("app.py");
let bytes = std::fs::read(&app).unwrap();
- let result = analyse_file_fused(&bytes, &app, &cfg, None, Some(&root))
- .expect("analyse fixture");
+ let result =
+ analyse_file_fused(&bytes, &app, &cfg, None, Some(&root)).expect("analyse fixture");
let root_str = root.to_string_lossy();
let mut gs = GlobalSummaries::new();
for s in result.summaries {
diff --git a/tests/fix_validation_e2e.rs b/tests/fix_validation_e2e.rs
index fdfce344..393b90fb 100644
--- a/tests/fix_validation_e2e.rs
+++ b/tests/fix_validation_e2e.rs
@@ -13,8 +13,8 @@
mod common;
use nyx_scanner::baseline::{
- check_gate, compute_verdict_diff, diags_to_baseline_entries, load_baseline, write_baseline,
- BaselineEntry, Transition, GATE_NO_NEW_CONFIRMED, GATE_RESOLVE_ALL_CONFIRMED,
+ BaselineEntry, GATE_NO_NEW_CONFIRMED, GATE_RESOLVE_ALL_CONFIRMED, Transition, check_gate,
+ compute_verdict_diff, diags_to_baseline_entries, load_baseline, write_baseline,
};
use nyx_scanner::commands::scan::compute_stable_hash;
use nyx_scanner::evidence::{Evidence, VerifyResult, VerifyStatus};
@@ -32,10 +32,7 @@ fn scan_with_hashes(dir: &Path) -> Vec {
}
/// Attach a simulated dynamic verdict to every finding in the list.
-fn set_verdict(
- diags: &mut [nyx_scanner::commands::scan::Diag],
- status: VerifyStatus,
-) {
+fn set_verdict(diags: &mut [nyx_scanner::commands::scan::Diag], status: VerifyStatus) {
for d in diags.iter_mut() {
let fid = format!("{:016x}", d.stable_hash);
let ev = d.evidence.get_or_insert_with(Evidence::default);
@@ -89,7 +86,10 @@ fn fix_resolves_confirmed_finding() {
// Step 1: scan vulnerable, simulate Confirmed verdict.
let mut vuln_diags = scan_with_hashes(vuln_path);
- assert!(!vuln_diags.is_empty(), "Need at least one SQL injection finding");
+ assert!(
+ !vuln_diags.is_empty(),
+ "Need at least one SQL injection finding"
+ );
set_verdict(&mut vuln_diags, VerifyStatus::Confirmed);
// Step 2: write stripped baseline.
@@ -260,7 +260,6 @@ fn load_baseline_accepts_full_diag_json() {
// Hashes must round-trip.
let loaded_hashes: std::collections::HashSet =
loaded.iter().map(|e| e.stable_hash).collect();
- let diag_hashes: std::collections::HashSet =
- diags.iter().map(|d| d.stable_hash).collect();
+ let diag_hashes: std::collections::HashSet = diags.iter().map(|d| d.stable_hash).collect();
assert_eq!(loaded_hashes, diag_hashes);
}
diff --git a/tests/go_fixtures.rs b/tests/go_fixtures.rs
index 6d5697ef..b70e02a3 100644
--- a/tests/go_fixtures.rs
+++ b/tests/go_fixtures.rs
@@ -14,7 +14,7 @@ mod common;
#[cfg(feature = "dynamic")]
mod go_fixture_tests {
use nyx_scanner::commands::scan::Diag;
- use nyx_scanner::dynamic::verify::{verify_finding, VerifyOptions};
+ use nyx_scanner::dynamic::verify::{VerifyOptions, verify_finding};
use nyx_scanner::evidence::{
Confidence, Evidence, FlowStep, FlowStepKind, InconclusiveReason, UnsupportedReason,
VerifyStatus,
@@ -456,7 +456,7 @@ mod go_fixture_tests {
#[cfg(feature = "dynamic")]
mod phase15_shape_tests {
- use crate::common::fixture_harness::{run_shape_fixture_lang_or_skip, Prerequisite};
+ use crate::common::fixture_harness::{Prerequisite, run_shape_fixture_lang_or_skip};
use nyx_scanner::dynamic::spec::PayloadSlot;
use nyx_scanner::evidence::{EntryKind, VerifyResult, VerifyStatus};
use nyx_scanner::labels::Cap;
@@ -506,7 +506,15 @@ mod phase15_shape_tests {
// return; };`.
run_shape_fixture_lang_or_skip(
&[Prerequisite::CommandAvailable("go")],
- Lang::Go, "go", shape, file, func, cap, sink_line, kind, slot,
+ Lang::Go,
+ "go",
+ shape,
+ file,
+ func,
+ cap,
+ sink_line,
+ kind,
+ slot,
)
}
@@ -515,8 +523,13 @@ mod phase15_shape_tests {
#[test]
fn handler_func_vuln_is_confirmed() {
let Some(r) = run(
- "handler_func", "vuln.go", "Handle", Cap::CODE_EXEC, 17,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("payload".into()),
+ "handler_func",
+ "vuln.go",
+ "Handle",
+ Cap::CODE_EXEC,
+ 17,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("payload".into()),
) else {
return;
};
@@ -526,8 +539,13 @@ mod phase15_shape_tests {
#[test]
fn handler_func_benign_not_confirmed() {
let Some(r) = run(
- "handler_func", "benign.go", "Handle", Cap::CODE_EXEC, 14,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("payload".into()),
+ "handler_func",
+ "benign.go",
+ "Handle",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("payload".into()),
) else {
return;
};
@@ -539,8 +557,13 @@ mod phase15_shape_tests {
#[test]
fn gin_handler_vuln_is_confirmed() {
let Some(r) = run(
- "gin_handler", "vuln.go", "Handle", Cap::CODE_EXEC, 16,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("payload".into()),
+ "gin_handler",
+ "vuln.go",
+ "Handle",
+ Cap::CODE_EXEC,
+ 16,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("payload".into()),
) else {
return;
};
@@ -550,8 +573,13 @@ mod phase15_shape_tests {
#[test]
fn gin_handler_benign_not_confirmed() {
let Some(r) = run(
- "gin_handler", "benign.go", "Handle", Cap::CODE_EXEC, 14,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("payload".into()),
+ "gin_handler",
+ "benign.go",
+ "Handle",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("payload".into()),
) else {
return;
};
@@ -563,8 +591,13 @@ mod phase15_shape_tests {
#[test]
fn flag_cli_vuln_is_confirmed() {
let Some(r) = run(
- "flag_cli", "vuln.go", "Run", Cap::CODE_EXEC, 19,
- EntryKind::CliSubcommand, PayloadSlot::Argv(0),
+ "flag_cli",
+ "vuln.go",
+ "Run",
+ Cap::CODE_EXEC,
+ 19,
+ EntryKind::CliSubcommand,
+ PayloadSlot::Argv(0),
) else {
return;
};
@@ -574,8 +607,13 @@ mod phase15_shape_tests {
#[test]
fn flag_cli_benign_not_confirmed() {
let Some(r) = run(
- "flag_cli", "benign.go", "Run", Cap::CODE_EXEC, 15,
- EntryKind::CliSubcommand, PayloadSlot::Argv(0),
+ "flag_cli",
+ "benign.go",
+ "Run",
+ Cap::CODE_EXEC,
+ 15,
+ EntryKind::CliSubcommand,
+ PayloadSlot::Argv(0),
) else {
return;
};
@@ -587,8 +625,13 @@ mod phase15_shape_tests {
#[test]
fn fuzz_variadic_vuln_is_confirmed() {
let Some(r) = run(
- "fuzz_variadic", "vuln.go", "FuzzHandle", Cap::CODE_EXEC, 14,
- EntryKind::Function, PayloadSlot::Param(0),
+ "fuzz_variadic",
+ "vuln.go",
+ "FuzzHandle",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
) else {
return;
};
@@ -598,8 +641,13 @@ mod phase15_shape_tests {
#[test]
fn fuzz_variadic_benign_not_confirmed() {
let Some(r) = run(
- "fuzz_variadic", "benign.go", "FuzzHandle", Cap::CODE_EXEC, 14,
- EntryKind::Function, PayloadSlot::Param(0),
+ "fuzz_variadic",
+ "benign.go",
+ "FuzzHandle",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
) else {
return;
};
diff --git a/tests/go_frameworks_corpus.rs b/tests/go_frameworks_corpus.rs
index cd1f905b..5dcddcb3 100644
--- a/tests/go_frameworks_corpus.rs
+++ b/tests/go_frameworks_corpus.rs
@@ -11,7 +11,7 @@
#![cfg(feature = "dynamic")]
-use nyx_scanner::dynamic::framework::{detect_binding, HttpMethod};
+use nyx_scanner::dynamic::framework::{HttpMethod, detect_binding};
use nyx_scanner::evidence::EntryKind;
use nyx_scanner::summary::FuncSummary;
use nyx_scanner::symbol::Lang;
diff --git a/tests/header_injection_corpus.rs b/tests/header_injection_corpus.rs
index 6cd67e0a..f84d51c2 100644
--- a/tests/header_injection_corpus.rs
+++ b/tests/header_injection_corpus.rs
@@ -16,12 +16,12 @@
mod common;
use nyx_scanner::dynamic::corpus::{
- audit_marker_collisions, benign_payload_for_lang, payloads_for_lang,
- resolve_benign_control_lang, Oracle,
+ Oracle, audit_marker_collisions, benign_payload_for_lang, payloads_for_lang,
+ resolve_benign_control_lang,
};
use nyx_scanner::dynamic::framework::registry::adapters_for;
use nyx_scanner::dynamic::lang;
-use nyx_scanner::dynamic::oracle::{oracle_fired, ProbePredicate};
+use nyx_scanner::dynamic::oracle::{ProbePredicate, oracle_fired};
use nyx_scanner::dynamic::probe::{ProbeKind, ProbeWitness, SinkProbe};
use nyx_scanner::dynamic::sandbox::SandboxOutcome;
use nyx_scanner::dynamic::spec::{EntryKind, HarnessSpec, PayloadSlot};
@@ -311,8 +311,8 @@ fn lang_emitter_dispatches_to_header_injection_harness() {
),
] {
let spec = make_spec(lang, entry_file, entry_name);
- let harness = lang::emit(&spec)
- .unwrap_or_else(|e| panic!("emit failed for {lang:?}: {e:?}"));
+ let harness =
+ lang::emit(&spec).unwrap_or_else(|e| panic!("emit failed for {lang:?}: {e:?}"));
assert!(
harness.source.contains("HeaderEmit"),
"{lang:?} header harness must carry the HeaderEmit probe kind",
@@ -396,8 +396,8 @@ fn framework_adapters_detect_header_sink() {
&bytes,
lang,
);
- let b = binding
- .unwrap_or_else(|| panic!("{lang:?} adapter must detect the header fixture"));
+ let b =
+ binding.unwrap_or_else(|| panic!("{lang:?} adapter must detect the header fixture"));
assert_eq!(b.kind, EntryKind::Function);
assert!(!b.adapter.is_empty());
}
@@ -457,10 +457,10 @@ fn slug(lang: Lang) -> &'static str {
mod e2e_phase_08 {
use crate::common::fixture_harness::FIXTURE_LOCK;
- use nyx_scanner::dynamic::runner::{run_spec, RunError, RunOutcome};
+ use nyx_scanner::dynamic::runner::{RunError, RunOutcome, run_spec};
use nyx_scanner::dynamic::sandbox::{SandboxBackend, SandboxOptions};
use nyx_scanner::dynamic::spec::{
- default_toolchain_id, EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy,
+ EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy, default_toolchain_id,
};
use nyx_scanner::evidence::DifferentialVerdict;
use nyx_scanner::labels::Cap;
@@ -588,43 +588,57 @@ mod e2e_phase_08 {
#[test]
fn java_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Java, "Vuln.java", "run") else { return };
+ let Some(outcome) = run(Lang::Java, "Vuln.java", "run") else {
+ return;
+ };
assert_confirmed(Lang::Java, &outcome);
}
#[test]
fn python_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Python, "vuln.py", "run") else { return };
+ let Some(outcome) = run(Lang::Python, "vuln.py", "run") else {
+ return;
+ };
assert_confirmed(Lang::Python, &outcome);
}
#[test]
fn php_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Php, "vuln.php", "run") else { return };
+ let Some(outcome) = run(Lang::Php, "vuln.php", "run") else {
+ return;
+ };
assert_confirmed(Lang::Php, &outcome);
}
#[test]
fn ruby_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Ruby, "vuln.rb", "run") else { return };
+ let Some(outcome) = run(Lang::Ruby, "vuln.rb", "run") else {
+ return;
+ };
assert_confirmed(Lang::Ruby, &outcome);
}
#[test]
fn js_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::JavaScript, "vuln.js", "run") else { return };
+ let Some(outcome) = run(Lang::JavaScript, "vuln.js", "run") else {
+ return;
+ };
assert_confirmed(Lang::JavaScript, &outcome);
}
#[test]
fn go_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Go, "vuln.go", "Run") else { return };
+ let Some(outcome) = run(Lang::Go, "vuln.go", "Run") else {
+ return;
+ };
assert_confirmed(Lang::Go, &outcome);
}
#[test]
fn rust_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Rust, "vuln.rs", "run") else { return };
+ let Some(outcome) = run(Lang::Rust, "vuln.rs", "run") else {
+ return;
+ };
assert_confirmed(Lang::Rust, &outcome);
}
}
diff --git a/tests/java_fixtures.rs b/tests/java_fixtures.rs
index e173d61a..3b392665 100644
--- a/tests/java_fixtures.rs
+++ b/tests/java_fixtures.rs
@@ -22,7 +22,7 @@ mod common;
#[cfg(feature = "dynamic")]
mod java_fixture_tests {
use nyx_scanner::commands::scan::Diag;
- use nyx_scanner::dynamic::verify::{verify_finding, VerifyOptions};
+ use nyx_scanner::dynamic::verify::{VerifyOptions, verify_finding};
use nyx_scanner::evidence::{
Confidence, Evidence, FlowStep, FlowStepKind, InconclusiveReason, UnsupportedReason,
VerifyStatus,
@@ -464,7 +464,7 @@ mod java_fixture_tests {
#[cfg(feature = "dynamic")]
mod phase14_shape_tests {
- use crate::common::fixture_harness::{run_shape_fixture_lang_or_skip, Prerequisite};
+ use crate::common::fixture_harness::{Prerequisite, run_shape_fixture_lang_or_skip};
use nyx_scanner::dynamic::spec::PayloadSlot;
use nyx_scanner::evidence::{EntryKind, VerifyResult, VerifyStatus};
use nyx_scanner::labels::Cap;
@@ -517,7 +517,15 @@ mod phase14_shape_tests {
Prerequisite::CommandAvailable("javac"),
Prerequisite::CommandAvailable("java"),
],
- Lang::Java, "java", shape, file, func, cap, sink_line, kind, slot,
+ Lang::Java,
+ "java",
+ shape,
+ file,
+ func,
+ cap,
+ sink_line,
+ kind,
+ slot,
)
}
@@ -526,8 +534,13 @@ mod phase14_shape_tests {
#[test]
fn static_method_vuln_is_confirmed() {
let Some(r) = run(
- "static_method", "Vuln.java", "processInput", Cap::CODE_EXEC, 12,
- EntryKind::Function, PayloadSlot::Param(0),
+ "static_method",
+ "Vuln.java",
+ "processInput",
+ Cap::CODE_EXEC,
+ 12,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
) else {
return;
};
@@ -537,8 +550,13 @@ mod phase14_shape_tests {
#[test]
fn static_method_benign_not_confirmed() {
let Some(r) = run(
- "static_method", "Benign.java", "processInput", Cap::CODE_EXEC, 13,
- EntryKind::Function, PayloadSlot::Param(0),
+ "static_method",
+ "Benign.java",
+ "processInput",
+ Cap::CODE_EXEC,
+ 13,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
) else {
return;
};
@@ -550,8 +568,13 @@ mod phase14_shape_tests {
#[test]
fn static_main_vuln_is_confirmed() {
let Some(r) = run(
- "static_main", "Vuln.java", "main", Cap::CODE_EXEC, 13,
- EntryKind::CliSubcommand, PayloadSlot::Argv(0),
+ "static_main",
+ "Vuln.java",
+ "main",
+ Cap::CODE_EXEC,
+ 13,
+ EntryKind::CliSubcommand,
+ PayloadSlot::Argv(0),
) else {
return;
};
@@ -561,8 +584,13 @@ mod phase14_shape_tests {
#[test]
fn static_main_benign_not_confirmed() {
let Some(r) = run(
- "static_main", "Benign.java", "main", Cap::CODE_EXEC, 12,
- EntryKind::CliSubcommand, PayloadSlot::Argv(0),
+ "static_main",
+ "Benign.java",
+ "main",
+ Cap::CODE_EXEC,
+ 12,
+ EntryKind::CliSubcommand,
+ PayloadSlot::Argv(0),
) else {
return;
};
@@ -574,8 +602,13 @@ mod phase14_shape_tests {
#[test]
fn servlet_doget_vuln_is_confirmed() {
let Some(r) = run(
- "servlet_doget", "Vuln.java", "doGet", Cap::CODE_EXEC, 14,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("payload".into()),
+ "servlet_doget",
+ "Vuln.java",
+ "doGet",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("payload".into()),
) else {
return;
};
@@ -585,8 +618,13 @@ mod phase14_shape_tests {
#[test]
fn servlet_doget_benign_not_confirmed() {
let Some(r) = run(
- "servlet_doget", "Benign.java", "doGet", Cap::CODE_EXEC, 14,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("payload".into()),
+ "servlet_doget",
+ "Benign.java",
+ "doGet",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("payload".into()),
) else {
return;
};
@@ -598,8 +636,13 @@ mod phase14_shape_tests {
#[test]
fn servlet_dopost_vuln_is_confirmed() {
let Some(r) = run(
- "servlet_dopost", "Vuln.java", "doPost", Cap::CODE_EXEC, 13,
- EntryKind::HttpRoute, PayloadSlot::HttpBody,
+ "servlet_dopost",
+ "Vuln.java",
+ "doPost",
+ Cap::CODE_EXEC,
+ 13,
+ EntryKind::HttpRoute,
+ PayloadSlot::HttpBody,
) else {
return;
};
@@ -609,8 +652,13 @@ mod phase14_shape_tests {
#[test]
fn servlet_dopost_benign_not_confirmed() {
let Some(r) = run(
- "servlet_dopost", "Benign.java", "doPost", Cap::CODE_EXEC, 12,
- EntryKind::HttpRoute, PayloadSlot::HttpBody,
+ "servlet_dopost",
+ "Benign.java",
+ "doPost",
+ Cap::CODE_EXEC,
+ 12,
+ EntryKind::HttpRoute,
+ PayloadSlot::HttpBody,
) else {
return;
};
@@ -622,8 +670,13 @@ mod phase14_shape_tests {
#[test]
fn spring_controller_vuln_is_confirmed() {
let Some(r) = run(
- "spring_controller", "Vuln.java", "run", Cap::CODE_EXEC, 16,
- EntryKind::HttpRoute, PayloadSlot::Param(0),
+ "spring_controller",
+ "Vuln.java",
+ "run",
+ Cap::CODE_EXEC,
+ 16,
+ EntryKind::HttpRoute,
+ PayloadSlot::Param(0),
) else {
return;
};
@@ -633,8 +686,13 @@ mod phase14_shape_tests {
#[test]
fn spring_controller_benign_not_confirmed() {
let Some(r) = run(
- "spring_controller", "Benign.java", "run", Cap::CODE_EXEC, 14,
- EntryKind::HttpRoute, PayloadSlot::Param(0),
+ "spring_controller",
+ "Benign.java",
+ "run",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::HttpRoute,
+ PayloadSlot::Param(0),
) else {
return;
};
@@ -646,8 +704,13 @@ mod phase14_shape_tests {
#[test]
fn junit_test_vuln_is_confirmed() {
let Some(r) = run(
- "junit_test", "Vuln.java", "testRun", Cap::CODE_EXEC, 17,
- EntryKind::Function, PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
+ "junit_test",
+ "Vuln.java",
+ "testRun",
+ Cap::CODE_EXEC,
+ 17,
+ EntryKind::Function,
+ PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
) else {
return;
};
@@ -657,8 +720,13 @@ mod phase14_shape_tests {
#[test]
fn junit_test_benign_not_confirmed() {
let Some(r) = run(
- "junit_test", "Benign.java", "testRun", Cap::CODE_EXEC, 15,
- EntryKind::Function, PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
+ "junit_test",
+ "Benign.java",
+ "testRun",
+ Cap::CODE_EXEC,
+ 15,
+ EntryKind::Function,
+ PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
) else {
return;
};
@@ -670,8 +738,13 @@ mod phase14_shape_tests {
#[test]
fn quarkus_route_vuln_is_confirmed() {
let Some(r) = run(
- "quarkus_route", "Vuln.java", "run", Cap::CODE_EXEC, 17,
- EntryKind::HttpRoute, PayloadSlot::Param(0),
+ "quarkus_route",
+ "Vuln.java",
+ "run",
+ Cap::CODE_EXEC,
+ 17,
+ EntryKind::HttpRoute,
+ PayloadSlot::Param(0),
) else {
return;
};
@@ -681,8 +754,13 @@ mod phase14_shape_tests {
#[test]
fn quarkus_route_benign_not_confirmed() {
let Some(r) = run(
- "quarkus_route", "Benign.java", "run", Cap::CODE_EXEC, 14,
- EntryKind::HttpRoute, PayloadSlot::Param(0),
+ "quarkus_route",
+ "Benign.java",
+ "run",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::HttpRoute,
+ PayloadSlot::Param(0),
) else {
return;
};
diff --git a/tests/java_frameworks_corpus.rs b/tests/java_frameworks_corpus.rs
index 5b87c49e..8aa4db7e 100644
--- a/tests/java_frameworks_corpus.rs
+++ b/tests/java_frameworks_corpus.rs
@@ -16,7 +16,7 @@
#![cfg(feature = "dynamic")]
-use nyx_scanner::dynamic::framework::{detect_binding, HttpMethod, ParamSource};
+use nyx_scanner::dynamic::framework::{HttpMethod, ParamSource, detect_binding};
use nyx_scanner::evidence::EntryKind;
use nyx_scanner::summary::FuncSummary;
use nyx_scanner::symbol::Lang;
@@ -143,10 +143,12 @@ fn servlet_doget_vuln_fixture_binds_route() {
// path defaults to `"/"`.
assert_eq!(route.path, "/");
// The (req, resp) pair should classify as Implicit.
- assert!(binding
- .request_params
- .iter()
- .all(|p| matches!(p.source, ParamSource::Implicit)));
+ assert!(
+ binding
+ .request_params
+ .iter()
+ .all(|p| matches!(p.source, ParamSource::Implicit))
+ );
}
#[test]
diff --git a/tests/javascript_fixtures.rs b/tests/javascript_fixtures.rs
index c88c9744..3904243e 100644
--- a/tests/javascript_fixtures.rs
+++ b/tests/javascript_fixtures.rs
@@ -18,7 +18,7 @@ mod common;
#[cfg(feature = "dynamic")]
mod javascript_fixture_tests {
- use crate::common::fixture_harness::{run_shape_fixture_lang_or_skip, Prerequisite};
+ use crate::common::fixture_harness::{Prerequisite, run_shape_fixture_lang_or_skip};
use nyx_scanner::dynamic::spec::PayloadSlot;
use nyx_scanner::evidence::{EntryKind, VerifyResult, VerifyStatus};
use nyx_scanner::labels::Cap;
@@ -89,9 +89,16 @@ mod javascript_fixture_tests {
fn commonjs_export_vuln_is_confirmed() {
let Some(r) = run(
NODE_REQ,
- "commonjs_export", "vuln.js", "runPing", Cap::CODE_EXEC, 11,
- EntryKind::Function, PayloadSlot::Param(0),
- ) else { return; };
+ "commonjs_export",
+ "vuln.js",
+ "runPing",
+ Cap::CODE_EXEC,
+ 11,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
+ ) else {
+ return;
+ };
assert_confirmed("commonjs_export", &r);
}
@@ -99,9 +106,16 @@ mod javascript_fixture_tests {
fn commonjs_export_benign_not_confirmed() {
let Some(r) = run(
NODE_REQ,
- "commonjs_export", "benign.js", "runPing", Cap::CODE_EXEC, 11,
- EntryKind::Function, PayloadSlot::Param(0),
- ) else { return; };
+ "commonjs_export",
+ "benign.js",
+ "runPing",
+ Cap::CODE_EXEC,
+ 11,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
+ ) else {
+ return;
+ };
assert_not_confirmed("commonjs_export", &r);
}
@@ -111,9 +125,16 @@ mod javascript_fixture_tests {
fn async_function_vuln_is_confirmed() {
let Some(r) = run(
NODE_REQ,
- "async_function", "vuln.js", "runPing", Cap::CODE_EXEC, 15,
- EntryKind::Function, PayloadSlot::Param(0),
- ) else { return; };
+ "async_function",
+ "vuln.js",
+ "runPing",
+ Cap::CODE_EXEC,
+ 15,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
+ ) else {
+ return;
+ };
assert_confirmed("async_function", &r);
}
@@ -121,9 +142,16 @@ mod javascript_fixture_tests {
fn async_function_benign_not_confirmed() {
let Some(r) = run(
NODE_REQ,
- "async_function", "benign.js", "runPing", Cap::CODE_EXEC, 14,
- EntryKind::Function, PayloadSlot::Param(0),
- ) else { return; };
+ "async_function",
+ "benign.js",
+ "runPing",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
+ ) else {
+ return;
+ };
assert_not_confirmed("async_function", &r);
}
@@ -133,9 +161,16 @@ mod javascript_fixture_tests {
fn esm_default_vuln_is_confirmed() {
let Some(r) = run(
NODE_REQ,
- "esm_default", "vuln.js", "runPing", Cap::CODE_EXEC, 14,
- EntryKind::Function, PayloadSlot::Param(0),
- ) else { return; };
+ "esm_default",
+ "vuln.js",
+ "runPing",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
+ ) else {
+ return;
+ };
assert_confirmed("esm_default", &r);
}
@@ -143,9 +178,16 @@ mod javascript_fixture_tests {
fn esm_default_benign_not_confirmed() {
let Some(r) = run(
NODE_REQ,
- "esm_default", "benign.js", "runPing", Cap::CODE_EXEC, 14,
- EntryKind::Function, PayloadSlot::Param(0),
- ) else { return; };
+ "esm_default",
+ "benign.js",
+ "runPing",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
+ ) else {
+ return;
+ };
assert_not_confirmed("esm_default", &r);
}
@@ -158,9 +200,16 @@ mod javascript_fixture_tests {
Prerequisite::CommandAvailable("node"),
Prerequisite::NodeModuleAvailable("express"),
],
- "express", "vuln.js", "ping", Cap::CODE_EXEC, 15,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("host".into()),
- ) else { return; };
+ "express",
+ "vuln.js",
+ "ping",
+ Cap::CODE_EXEC,
+ 15,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("host".into()),
+ ) else {
+ return;
+ };
assert_confirmed("express", &r);
}
@@ -171,9 +220,16 @@ mod javascript_fixture_tests {
Prerequisite::CommandAvailable("node"),
Prerequisite::NodeModuleAvailable("express"),
],
- "express", "benign.js", "ping", Cap::CODE_EXEC, 14,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("host".into()),
- ) else { return; };
+ "express",
+ "benign.js",
+ "ping",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("host".into()),
+ ) else {
+ return;
+ };
assert_not_confirmed("express", &r);
}
@@ -186,9 +242,16 @@ mod javascript_fixture_tests {
Prerequisite::CommandAvailable("node"),
Prerequisite::NodeModuleAvailable("koa"),
],
- "koa", "vuln.js", "ping", Cap::CODE_EXEC, 14,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("host".into()),
- ) else { return; };
+ "koa",
+ "vuln.js",
+ "ping",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("host".into()),
+ ) else {
+ return;
+ };
assert_confirmed("koa", &r);
}
@@ -199,9 +262,16 @@ mod javascript_fixture_tests {
Prerequisite::CommandAvailable("node"),
Prerequisite::NodeModuleAvailable("koa"),
],
- "koa", "benign.js", "ping", Cap::CODE_EXEC, 14,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("host".into()),
- ) else { return; };
+ "koa",
+ "benign.js",
+ "ping",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("host".into()),
+ ) else {
+ return;
+ };
assert_not_confirmed("koa", &r);
}
@@ -214,9 +284,16 @@ mod javascript_fixture_tests {
Prerequisite::CommandAvailable("node"),
Prerequisite::NodeModuleAvailable("next"),
],
- "next_route", "vuln.js", "handler", Cap::CODE_EXEC, 17,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("host".into()),
- ) else { return; };
+ "next_route",
+ "vuln.js",
+ "handler",
+ Cap::CODE_EXEC,
+ 17,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("host".into()),
+ ) else {
+ return;
+ };
assert_confirmed("next_route", &r);
}
@@ -227,9 +304,16 @@ mod javascript_fixture_tests {
Prerequisite::CommandAvailable("node"),
Prerequisite::NodeModuleAvailable("next"),
],
- "next_route", "benign.js", "handler", Cap::CODE_EXEC, 14,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("host".into()),
- ) else { return; };
+ "next_route",
+ "benign.js",
+ "handler",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("host".into()),
+ ) else {
+ return;
+ };
assert_not_confirmed("next_route", &r);
}
@@ -242,9 +326,16 @@ mod javascript_fixture_tests {
Prerequisite::CommandAvailable("node"),
Prerequisite::NodeModuleAvailable("jsdom"),
],
- "browser_event", "vuln.js", "clickHandler", Cap::HTML_ESCAPE, 14,
- EntryKind::Function, PayloadSlot::Param(0),
- ) else { return; };
+ "browser_event",
+ "vuln.js",
+ "clickHandler",
+ Cap::HTML_ESCAPE,
+ 14,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
+ ) else {
+ return;
+ };
assert_confirmed("browser_event", &r);
}
@@ -255,9 +346,16 @@ mod javascript_fixture_tests {
Prerequisite::CommandAvailable("node"),
Prerequisite::NodeModuleAvailable("jsdom"),
],
- "browser_event", "benign.js", "clickHandler", Cap::HTML_ESCAPE, 14,
- EntryKind::Function, PayloadSlot::Param(0),
- ) else { return; };
+ "browser_event",
+ "benign.js",
+ "clickHandler",
+ Cap::HTML_ESCAPE,
+ 14,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
+ ) else {
+ return;
+ };
assert_not_confirmed("browser_event", &r);
}
}
diff --git a/tests/js_fixtures.rs b/tests/js_fixtures.rs
index 490ec3e5..2ce0e3cb 100644
--- a/tests/js_fixtures.rs
+++ b/tests/js_fixtures.rs
@@ -12,7 +12,7 @@
#[cfg(feature = "dynamic")]
mod js_fixture_tests {
use nyx_scanner::commands::scan::Diag;
- use nyx_scanner::dynamic::verify::{verify_finding, VerifyOptions};
+ use nyx_scanner::dynamic::verify::{VerifyOptions, verify_finding};
use nyx_scanner::evidence::{
Confidence, Evidence, FlowStep, FlowStepKind, InconclusiveReason, UnsupportedReason,
VerifyStatus,
diff --git a/tests/js_frameworks_corpus.rs b/tests/js_frameworks_corpus.rs
index fc35111d..48d70ecc 100644
--- a/tests/js_frameworks_corpus.rs
+++ b/tests/js_frameworks_corpus.rs
@@ -11,7 +11,7 @@
#![cfg(feature = "dynamic")]
-use nyx_scanner::dynamic::framework::{detect_binding, HttpMethod, ParamSource};
+use nyx_scanner::dynamic::framework::{HttpMethod, ParamSource, detect_binding};
use nyx_scanner::evidence::EntryKind;
use nyx_scanner::summary::FuncSummary;
use nyx_scanner::symbol::Lang;
@@ -45,10 +45,12 @@ fn express_vuln_fixture_binds_route() {
let route = binding.route.as_ref().expect("route");
assert_eq!(route.path, "/run");
assert_eq!(route.method, HttpMethod::GET);
- assert!(binding
- .request_params
- .iter()
- .any(|p| p.name == "req" && matches!(p.source, ParamSource::Implicit)));
+ assert!(
+ binding
+ .request_params
+ .iter()
+ .any(|p| p.name == "req" && matches!(p.source, ParamSource::Implicit))
+ );
}
#[test]
@@ -77,10 +79,12 @@ fn koa_vuln_fixture_binds_router_route() {
let route = binding.route.as_ref().expect("route");
assert_eq!(route.path, "/run");
assert_eq!(route.method, HttpMethod::GET);
- assert!(binding
- .request_params
- .iter()
- .any(|p| p.name == "ctx" && matches!(p.source, ParamSource::Implicit)));
+ assert!(
+ binding
+ .request_params
+ .iter()
+ .any(|p| p.name == "ctx" && matches!(p.source, ParamSource::Implicit))
+ );
}
#[test]
@@ -107,14 +111,18 @@ fn fastify_vuln_fixture_binds_route() {
let route = binding.route.as_ref().expect("route");
assert_eq!(route.path, "/run");
assert_eq!(route.method, HttpMethod::GET);
- assert!(binding
- .request_params
- .iter()
- .any(|p| p.name == "request" && matches!(p.source, ParamSource::Implicit)));
- assert!(binding
- .request_params
- .iter()
- .any(|p| p.name == "reply" && matches!(p.source, ParamSource::Implicit)));
+ assert!(
+ binding
+ .request_params
+ .iter()
+ .any(|p| p.name == "request" && matches!(p.source, ParamSource::Implicit))
+ );
+ assert!(
+ binding
+ .request_params
+ .iter()
+ .any(|p| p.name == "reply" && matches!(p.source, ParamSource::Implicit))
+ );
}
#[test]
@@ -176,7 +184,6 @@ fn express_adapter_runs_before_fastify_for_express_files() {
app.get('/x', h);\n";
let tree = parse_js(src);
let summary = summary_for("h", "synthetic.js");
- let binding =
- detect_binding(&summary, tree.root_node(), src, Lang::JavaScript).expect("fires");
+ let binding = detect_binding(&summary, tree.root_node(), src, Lang::JavaScript).expect("fires");
assert_eq!(binding.adapter, "js-express");
}
diff --git a/tests/json_parse_corpus.rs b/tests/json_parse_corpus.rs
index 44be649c..c73a3410 100644
--- a/tests/json_parse_corpus.rs
+++ b/tests/json_parse_corpus.rs
@@ -11,7 +11,7 @@
#![cfg(feature = "dynamic")]
use nyx_scanner::dynamic::corpus::{payloads_for_lang, resolve_benign_control_lang};
-use nyx_scanner::dynamic::oracle::{oracle_fired, Oracle, ProbePredicate};
+use nyx_scanner::dynamic::oracle::{Oracle, ProbePredicate, oracle_fired};
use nyx_scanner::dynamic::probe::{ProbeKind, ProbeWitness, SinkProbe};
use nyx_scanner::dynamic::sandbox::SandboxOutcome;
use nyx_scanner::labels::Cap;
@@ -68,7 +68,9 @@ fn json_parse_pairs_benign_per_lang_via_canary_predicate() {
match &vuln.oracle {
Oracle::SinkProbe { predicates } => assert!(predicates.iter().any(|p| matches!(
p,
- ProbePredicate::PrototypeCanaryTouched { canary: "__nyx_canary" }
+ ProbePredicate::PrototypeCanaryTouched {
+ canary: "__nyx_canary"
+ }
))),
other => panic!("expected SinkProbe, got {other:?}"),
}
@@ -82,8 +84,16 @@ fn canary_predicate_fires_only_on_canary_property() {
canary: "__nyx_canary",
}],
};
- assert!(oracle_fired(&oracle, &outcome(), &[canary_probe("__nyx_canary")]));
- assert!(!oracle_fired(&oracle, &outcome(), &[canary_probe("__data__")]));
+ assert!(oracle_fired(
+ &oracle,
+ &outcome(),
+ &[canary_probe("__nyx_canary")]
+ ));
+ assert!(!oracle_fired(
+ &oracle,
+ &outcome(),
+ &[canary_probe("__data__")]
+ ));
assert!(!oracle_fired(&oracle, &outcome(), &[]));
}
diff --git a/tests/json_snapshot.rs b/tests/json_snapshot.rs
index bd0fa9de..83774012 100644
--- a/tests/json_snapshot.rs
+++ b/tests/json_snapshot.rs
@@ -6,9 +6,7 @@
//! `skip_serializing_if = "Option::is_none"`).
use nyx_scanner::commands::scan::Diag;
-use nyx_scanner::evidence::{
- AttemptSummary, Evidence, VerifyResult, VerifyStatus,
-};
+use nyx_scanner::evidence::{AttemptSummary, Evidence, VerifyResult, VerifyStatus};
use nyx_scanner::patterns::{FindingCategory, Severity};
fn base_diag() -> Diag {
diff --git a/tests/ldap_corpus.rs b/tests/ldap_corpus.rs
index dfd58ac5..c2e9d9b4 100644
--- a/tests/ldap_corpus.rs
+++ b/tests/ldap_corpus.rs
@@ -16,8 +16,8 @@
mod common;
use nyx_scanner::dynamic::corpus::{
- audit_marker_collisions, benign_payload_for_lang, payloads_for_lang,
- resolve_benign_control_lang, Oracle,
+ Oracle, audit_marker_collisions, benign_payload_for_lang, payloads_for_lang,
+ resolve_benign_control_lang,
};
use nyx_scanner::dynamic::framework::registry::adapters_for;
use nyx_scanner::dynamic::lang;
@@ -57,7 +57,10 @@ fn make_spec(lang: Lang, entry_file: &str, entry_name: &str) -> HarnessSpec {
fn corpus_registers_ldap_for_every_supported_lang() {
for lang in LANGS {
let slice = payloads_for_lang(Cap::LDAP_INJECTION, *lang);
- assert!(!slice.is_empty(), "LDAP_INJECTION has no payloads for {lang:?}");
+ assert!(
+ !slice.is_empty(),
+ "LDAP_INJECTION has no payloads for {lang:?}"
+ );
let has_vuln = slice.iter().any(|p| !p.is_benign);
let has_benign = slice.iter().any(|p| p.is_benign);
assert!(has_vuln, "{lang:?} LDAP missing vuln payload");
@@ -104,10 +107,9 @@ fn payload_oracle_carries_ldap_result_count_predicate() {
match &vuln.oracle {
Oracle::SinkProbe { predicates } => {
assert!(
- predicates.iter().any(|p| matches!(
- p,
- ProbePredicate::QueryResultCountGreaterThan { n: 1 }
- )),
+ predicates
+ .iter()
+ .any(|p| matches!(p, ProbePredicate::QueryResultCountGreaterThan { n: 1 })),
"{lang:?} vuln payload missing QueryResultCountGreaterThan {{ n: 1 }}",
);
}
@@ -146,7 +148,9 @@ fn marker_collisions_clean_with_phase_06_additions() {
#[test]
fn probe_kind_ldap_serdes() {
- let original = ProbeKind::Ldap { entries_returned: 3 };
+ let original = ProbeKind::Ldap {
+ entries_returned: 3,
+ };
let json = serde_json::to_string(&original).unwrap();
assert!(json.contains("Ldap"));
assert!(json.contains("entries_returned"));
@@ -181,8 +185,8 @@ fn lang_emitter_dispatches_to_ldap_harness() {
),
] {
let spec = make_spec(lang, entry_file, entry_name);
- let harness = lang::emit(&spec)
- .unwrap_or_else(|e| panic!("emit failed for {lang:?}: {e:?}"));
+ let harness =
+ lang::emit(&spec).unwrap_or_else(|e| panic!("emit failed for {lang:?}: {e:?}"));
assert!(
harness.source.contains("entries_returned"),
"{lang:?} ldap harness must carry the entries_returned probe field",
@@ -246,8 +250,7 @@ fn framework_adapters_detect_ldap_sink() {
&bytes,
lang,
);
- let b = binding
- .unwrap_or_else(|| panic!("{lang:?} adapter must detect the LDAP fixture"));
+ let b = binding.unwrap_or_else(|| panic!("{lang:?} adapter must detect the LDAP fixture"));
assert_eq!(b.kind, EntryKind::Function);
assert!(!b.adapter.is_empty());
}
@@ -279,7 +282,10 @@ fn stub_ldap_server_returns_three_for_wildcard_filter() {
let stub = LdapStub::start().expect("ldap stub starts");
let mal = LdapStub::evaluate("(|(uid=alice)(uid=*))");
let benign = LdapStub::evaluate("(uid=alice)");
- assert!(mal.len() > 1, "malicious filter must match > 1 entry, got {mal:?}");
+ assert!(
+ mal.len() > 1,
+ "malicious filter must match > 1 entry, got {mal:?}"
+ );
assert_eq!(benign.len(), 1, "benign filter must match exactly 1 entry");
assert_eq!(stub.kind(), StubKind::Ldap);
}
@@ -302,10 +308,10 @@ fn stub_kind_for_cap_routes_ldap_injection() {
mod e2e_phase_06 {
use crate::common::fixture_harness::FIXTURE_LOCK;
- use nyx_scanner::dynamic::runner::{run_spec, RunError, RunOutcome};
+ use nyx_scanner::dynamic::runner::{RunError, RunOutcome, run_spec};
use nyx_scanner::dynamic::sandbox::{SandboxBackend, SandboxOptions};
use nyx_scanner::dynamic::spec::{
- default_toolchain_id, EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy,
+ EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy, default_toolchain_id,
};
use nyx_scanner::evidence::DifferentialVerdict;
use nyx_scanner::labels::Cap;
@@ -413,7 +419,9 @@ mod e2e_phase_06 {
#[test]
fn java_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Java, "Vuln.java", "run") else { return };
+ let Some(outcome) = run(Lang::Java, "Vuln.java", "run") else {
+ return;
+ };
assert!(
outcome.triggered_by.is_some(),
"Java LDAP vuln must Confirm via run_spec; got {outcome:?}",
@@ -427,7 +435,9 @@ mod e2e_phase_06 {
#[test]
fn python_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Python, "vuln.py", "run") else { return };
+ let Some(outcome) = run(Lang::Python, "vuln.py", "run") else {
+ return;
+ };
assert!(
outcome.triggered_by.is_some(),
"Python LDAP vuln must Confirm via run_spec; got {outcome:?}",
@@ -441,7 +451,9 @@ mod e2e_phase_06 {
#[test]
fn php_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Php, "vuln.php", "run") else { return };
+ let Some(outcome) = run(Lang::Php, "vuln.php", "run") else {
+ return;
+ };
assert!(
outcome.triggered_by.is_some(),
"PHP LDAP vuln must Confirm via run_spec; got {outcome:?}",
diff --git a/tests/marker_uniqueness.rs b/tests/marker_uniqueness.rs
index a85e1d76..5bda20f2 100644
--- a/tests/marker_uniqueness.rs
+++ b/tests/marker_uniqueness.rs
@@ -95,7 +95,9 @@ fn no_marker_is_substring_of_another_caps_payload() {
continue;
}
for payload in payloads_for(cap).iter().filter(|p| !p.is_benign) {
- let payload_contains_marker = payload.bytes.windows(marker_bytes.len())
+ let payload_contains_marker = payload
+ .bytes
+ .windows(marker_bytes.len())
.any(|w| w == marker_bytes);
if payload_contains_marker {
@@ -215,7 +217,8 @@ fn all_vuln_payloads_have_non_empty_oracle_marker() {
assert!(
marker.len() >= 4,
"payload {:?} for {cap:?} has very short marker {:?} (< 4 chars) — collision risk",
- payload.label, marker
+ payload.label,
+ marker
);
}
}
diff --git a/tests/message_handler_corpus.rs b/tests/message_handler_corpus.rs
index ff9f678c..dfa7a89c 100644
--- a/tests/message_handler_corpus.rs
+++ b/tests/message_handler_corpus.rs
@@ -17,7 +17,7 @@
mod common;
use nyx_scanner::dynamic::framework::registry::adapters_for;
-use nyx_scanner::dynamic::framework::{detect_binding, FrameworkBinding};
+use nyx_scanner::dynamic::framework::{FrameworkBinding, detect_binding};
use nyx_scanner::dynamic::lang;
use nyx_scanner::dynamic::spec::{EntryKind, EntryKindTag, HarnessSpec, PayloadSlot};
use nyx_scanner::labels::Cap;
@@ -32,13 +32,7 @@ const SUPPORTED_LANGS: &[Lang] = &[
Lang::Go,
];
-const UNSUPPORTED_LANGS: &[Lang] = &[
- Lang::Php,
- Lang::Ruby,
- Lang::Rust,
- Lang::C,
- Lang::Cpp,
-];
+const UNSUPPORTED_LANGS: &[Lang] = &[Lang::Php, Lang::Ruby, Lang::Rust, Lang::C, Lang::Cpp];
fn entry_file(broker_lang: &str) -> &'static str {
// Phase 20 fixtures live at tests/dynamic_fixtures/message_handler/{broker_lang}/{vuln,benign}.
@@ -222,29 +216,29 @@ fn kafka_python_adapter_binds_message_handler_kind() {
#[test]
fn kafka_java_adapter_binds_message_handler_kind() {
- let b = detect_for(Lang::Java, entry_file("kafka_java"), "onMessage")
- .expect("kafka-java detect");
+ let b =
+ detect_for(Lang::Java, entry_file("kafka_java"), "onMessage").expect("kafka-java detect");
assert!(matches!(b.kind, EntryKind::MessageHandler { .. }));
}
#[test]
fn sqs_python_adapter_binds_message_handler_kind() {
- let b = detect_for(Lang::Python, entry_file("sqs_python"), "handler")
- .expect("sqs-python detect");
+ let b =
+ detect_for(Lang::Python, entry_file("sqs_python"), "handler").expect("sqs-python detect");
assert!(matches!(b.kind, EntryKind::MessageHandler { .. }));
}
#[test]
fn sqs_java_adapter_binds_message_handler_kind() {
- let b = detect_for(Lang::Java, entry_file("sqs_java"), "handleMessage")
- .expect("sqs-java detect");
+ let b =
+ detect_for(Lang::Java, entry_file("sqs_java"), "handleMessage").expect("sqs-java detect");
assert!(matches!(b.kind, EntryKind::MessageHandler { .. }));
}
#[test]
fn sqs_node_adapter_binds_message_handler_kind() {
- let b = detect_for(Lang::JavaScript, entry_file("sqs_node"), "handler")
- .expect("sqs-node detect");
+ let b =
+ detect_for(Lang::JavaScript, entry_file("sqs_node"), "handler").expect("sqs-node detect");
assert!(matches!(b.kind, EntryKind::MessageHandler { .. }));
}
@@ -257,8 +251,7 @@ fn pubsub_python_adapter_binds_message_handler_kind() {
#[test]
fn pubsub_go_adapter_binds_message_handler_kind() {
- let b = detect_for(Lang::Go, entry_file("pubsub_go"), "OnMessage")
- .expect("pubsub-go detect");
+ let b = detect_for(Lang::Go, entry_file("pubsub_go"), "OnMessage").expect("pubsub-go detect");
assert!(matches!(b.kind, EntryKind::MessageHandler { .. }));
}
@@ -271,24 +264,20 @@ fn rabbit_python_adapter_binds_message_handler_kind() {
#[test]
fn rabbit_java_adapter_binds_message_handler_kind() {
- let b = detect_for(Lang::Java, entry_file("rabbit_java"), "onMessage")
- .expect("rabbit-java detect");
+ let b =
+ detect_for(Lang::Java, entry_file("rabbit_java"), "onMessage").expect("rabbit-java detect");
assert!(matches!(b.kind, EntryKind::MessageHandler { .. }));
}
#[test]
fn nats_go_adapter_binds_message_handler_kind() {
- let b = detect_for(Lang::Go, entry_file("nats_go"), "OnMessage")
- .expect("nats-go detect");
+ let b = detect_for(Lang::Go, entry_file("nats_go"), "OnMessage").expect("nats-go detect");
assert!(matches!(b.kind, EntryKind::MessageHandler { .. }));
}
#[test]
fn registry_slices_include_phase_20_adapters() {
- let java_names: Vec<&'static str> = adapters_for(Lang::Java)
- .iter()
- .map(|a| a.name())
- .collect();
+ let java_names: Vec<&'static str> = adapters_for(Lang::Java).iter().map(|a| a.name()).collect();
assert!(java_names.contains(&"kafka-java"));
assert!(java_names.contains(&"sqs-java"));
assert!(java_names.contains(&"rabbit-java"));
@@ -302,10 +291,7 @@ fn registry_slices_include_phase_20_adapters() {
assert!(python_names.contains(&"pubsub-python"));
assert!(python_names.contains(&"rabbit-python"));
- let go_names: Vec<&'static str> = adapters_for(Lang::Go)
- .iter()
- .map(|a| a.name())
- .collect();
+ let go_names: Vec<&'static str> = adapters_for(Lang::Go).iter().map(|a| a.name()).collect();
assert!(go_names.contains(&"pubsub-go"));
assert!(go_names.contains(&"nats-go"));
@@ -327,10 +313,10 @@ fn registry_slices_include_phase_20_adapters() {
mod e2e_phase_20 {
use crate::common::fixture_harness::FIXTURE_LOCK;
- use nyx_scanner::dynamic::runner::{run_spec, RunError, RunOutcome};
+ use nyx_scanner::dynamic::runner::{RunError, RunOutcome, run_spec};
use nyx_scanner::dynamic::sandbox::SandboxOptions;
use nyx_scanner::dynamic::spec::{
- default_toolchain_id, EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy,
+ EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy, default_toolchain_id,
};
use nyx_scanner::evidence::DifferentialVerdict;
use nyx_scanner::labels::Cap;
@@ -468,9 +454,7 @@ mod e2e_phase_20 {
);
None
}
- Err(e) => panic!(
- "run_spec({lang:?} {fixture_dir}/{fixture_file}) errored: {e:?}",
- ),
+ Err(e) => panic!("run_spec({lang:?} {fixture_dir}/{fixture_file}) errored: {e:?}",),
}
}
@@ -497,8 +481,7 @@ mod e2e_phase_20 {
#[test]
fn sqs_python_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Python, "sqs_python", "vuln.py", "handler", "jobs")
- else {
+ let Some(outcome) = run(Lang::Python, "sqs_python", "vuln.py", "handler", "jobs") else {
return;
};
assert!(outcome.triggered_by.is_some());
@@ -540,8 +523,7 @@ mod e2e_phase_20 {
#[test]
fn sqs_node_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::JavaScript, "sqs_node", "vuln.js", "handler", "jobs")
- else {
+ let Some(outcome) = run(Lang::JavaScript, "sqs_node", "vuln.js", "handler", "jobs") else {
return;
};
assert!(
diff --git a/tests/network_policy.rs b/tests/network_policy.rs
index 2c68aaf0..e61fd2bb 100644
--- a/tests/network_policy.rs
+++ b/tests/network_policy.rs
@@ -59,7 +59,9 @@ fn oob_outbound_carries_listener() {
return;
};
let listener = Arc::new(listener);
- let p = NetworkPolicy::OobOutbound { listener: Arc::clone(&listener) };
+ let p = NetworkPolicy::OobOutbound {
+ listener: Arc::clone(&listener),
+ };
assert!(p.allows_network());
let got = p.oob_listener().expect("listener present");
assert!(
diff --git a/tests/open_redirect_corpus.rs b/tests/open_redirect_corpus.rs
index 200faa91..e8da6f52 100644
--- a/tests/open_redirect_corpus.rs
+++ b/tests/open_redirect_corpus.rs
@@ -16,12 +16,12 @@
mod common;
use nyx_scanner::dynamic::corpus::{
- audit_marker_collisions, benign_payload_for_lang, payloads_for_lang,
- resolve_benign_control_lang, Oracle,
+ Oracle, audit_marker_collisions, benign_payload_for_lang, payloads_for_lang,
+ resolve_benign_control_lang,
};
use nyx_scanner::dynamic::framework::registry::adapters_for;
use nyx_scanner::dynamic::lang;
-use nyx_scanner::dynamic::oracle::{oracle_fired, ProbePredicate};
+use nyx_scanner::dynamic::oracle::{ProbePredicate, oracle_fired};
use nyx_scanner::dynamic::probe::{ProbeKind, ProbeWitness, SinkProbe};
use nyx_scanner::dynamic::sandbox::SandboxOutcome;
use nyx_scanner::dynamic::spec::{EntryKind, HarnessSpec, PayloadSlot};
@@ -72,10 +72,7 @@ fn corpus_registers_open_redirect_for_every_supported_lang() {
let has_vuln = slice.iter().any(|p| !p.is_benign);
let has_benign = slice.iter().any(|p| p.is_benign);
assert!(has_vuln, "{lang:?} OPEN_REDIRECT missing vuln payload");
- assert!(
- has_benign,
- "{lang:?} OPEN_REDIRECT missing benign control"
- );
+ assert!(has_benign, "{lang:?} OPEN_REDIRECT missing benign control");
}
}
@@ -94,8 +91,8 @@ fn benign_control_resolves_within_lang_slice() {
for lang in LANGS {
let slice = payloads_for_lang(Cap::OPEN_REDIRECT, *lang);
let vuln = slice.iter().find(|p| !p.is_benign).unwrap();
- let resolved = resolve_benign_control_lang(vuln, Cap::OPEN_REDIRECT, *lang)
- .expect("paired control");
+ let resolved =
+ resolve_benign_control_lang(vuln, Cap::OPEN_REDIRECT, *lang).expect("paired control");
assert!(resolved.is_benign);
let direct = benign_payload_for_lang(Cap::OPEN_REDIRECT, *lang).unwrap();
assert_eq!(direct.label, resolved.label);
@@ -110,10 +107,9 @@ fn payload_oracle_carries_redirect_host_not_in_predicate() {
match &vuln.oracle {
Oracle::SinkProbe { predicates } => {
assert!(
- predicates.iter().any(|p| matches!(
- p,
- ProbePredicate::RedirectHostNotIn { .. }
- )),
+ predicates
+ .iter()
+ .any(|p| matches!(p, ProbePredicate::RedirectHostNotIn { .. })),
"{lang:?} vuln payload missing RedirectHostNotIn predicate",
);
}
@@ -275,8 +271,8 @@ fn lang_emitter_dispatches_to_open_redirect_harness() {
),
] {
let spec = make_spec(lang, entry_file, entry_name);
- let harness = lang::emit(&spec)
- .unwrap_or_else(|e| panic!("emit failed for {lang:?}: {e:?}"));
+ let harness =
+ lang::emit(&spec).unwrap_or_else(|e| panic!("emit failed for {lang:?}: {e:?}"));
assert!(
harness.source.contains("Redirect"),
"{lang:?} redirect harness must carry the Redirect probe kind",
@@ -361,8 +357,8 @@ fn framework_adapters_detect_redirect_sink() {
&bytes,
lang,
);
- let b = binding
- .unwrap_or_else(|| panic!("{lang:?} adapter must detect the redirect fixture"));
+ let b =
+ binding.unwrap_or_else(|| panic!("{lang:?} adapter must detect the redirect fixture"));
assert_eq!(b.kind, EntryKind::Function);
assert!(!b.adapter.is_empty());
}
@@ -423,10 +419,10 @@ fn slug(lang: Lang) -> &'static str {
mod e2e_phase_09 {
use crate::common::fixture_harness::FIXTURE_LOCK;
- use nyx_scanner::dynamic::runner::{run_spec, RunError, RunOutcome};
+ use nyx_scanner::dynamic::runner::{RunError, RunOutcome, run_spec};
use nyx_scanner::dynamic::sandbox::{SandboxBackend, SandboxOptions};
use nyx_scanner::dynamic::spec::{
- default_toolchain_id, EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy,
+ EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy, default_toolchain_id,
};
use nyx_scanner::evidence::DifferentialVerdict;
use nyx_scanner::labels::Cap;
@@ -554,43 +550,57 @@ mod e2e_phase_09 {
#[test]
fn java_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Java, "Vuln.java", "run") else { return };
+ let Some(outcome) = run(Lang::Java, "Vuln.java", "run") else {
+ return;
+ };
assert_confirmed(Lang::Java, &outcome);
}
#[test]
fn python_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Python, "vuln.py", "run") else { return };
+ let Some(outcome) = run(Lang::Python, "vuln.py", "run") else {
+ return;
+ };
assert_confirmed(Lang::Python, &outcome);
}
#[test]
fn php_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Php, "vuln.php", "run") else { return };
+ let Some(outcome) = run(Lang::Php, "vuln.php", "run") else {
+ return;
+ };
assert_confirmed(Lang::Php, &outcome);
}
#[test]
fn ruby_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Ruby, "vuln.rb", "run") else { return };
+ let Some(outcome) = run(Lang::Ruby, "vuln.rb", "run") else {
+ return;
+ };
assert_confirmed(Lang::Ruby, &outcome);
}
#[test]
fn js_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::JavaScript, "vuln.js", "run") else { return };
+ let Some(outcome) = run(Lang::JavaScript, "vuln.js", "run") else {
+ return;
+ };
assert_confirmed(Lang::JavaScript, &outcome);
}
#[test]
fn go_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Go, "vuln.go", "Run") else { return };
+ let Some(outcome) = run(Lang::Go, "vuln.go", "Run") else {
+ return;
+ };
assert_confirmed(Lang::Go, &outcome);
}
#[test]
fn rust_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::Rust, "vuln.rs", "run") else { return };
+ let Some(outcome) = run(Lang::Rust, "vuln.rs", "run") else {
+ return;
+ };
assert_confirmed(Lang::Rust, &outcome);
}
}
diff --git a/tests/oracle_differential.rs b/tests/oracle_differential.rs
index 210010a6..0fd739b6 100644
--- a/tests/oracle_differential.rs
+++ b/tests/oracle_differential.rs
@@ -81,7 +81,11 @@ fn sample_probe(callee: &str, arg: &str, label: &str) -> SinkProbe {
#[test]
fn build_outcome_confirmed_carries_both_traces() {
- let vuln = vec![sample_probe("os.system", "; echo NYX_PWN_CMDI", "cmdi-echo-marker")];
+ let vuln = vec![sample_probe(
+ "os.system",
+ "; echo NYX_PWN_CMDI",
+ "cmdi-echo-marker",
+ )];
let benign = vec![sample_probe("os.system", "benign_safe_cmdi", "cmdi-benign")];
let outcome = build_outcome(
"cmdi-echo-marker",
@@ -106,7 +110,10 @@ fn build_outcome_oracle_collision_keeps_both_traces() {
let vuln = vec![sample_probe("os.system", "a", "v")];
let benign = vec![sample_probe("os.system", "b", "b")];
let outcome = build_outcome("v", true, &vuln, "b", true, &benign);
- assert_eq!(outcome.verdict, DifferentialVerdict::OracleCollisionSuspected);
+ assert_eq!(
+ outcome.verdict,
+ DifferentialVerdict::OracleCollisionSuspected
+ );
assert_eq!(outcome.vuln_probes.len(), 1);
assert_eq!(outcome.benign_probes.len(), 1);
}
diff --git a/tests/oracle_sink_crash.rs b/tests/oracle_sink_crash.rs
index 0ea8837d..5a53e93c 100644
--- a/tests/oracle_sink_crash.rs
+++ b/tests/oracle_sink_crash.rs
@@ -21,13 +21,9 @@
mod common;
-use nyx_scanner::dynamic::oracle::{
- oracle_fired, probe_crash_signal, Oracle, Signal, SignalSet,
-};
+use nyx_scanner::dynamic::oracle::{Oracle, Signal, SignalSet, oracle_fired, probe_crash_signal};
use nyx_scanner::dynamic::policy;
-use nyx_scanner::dynamic::probe::{
- ProbeArg, ProbeChannel, ProbeKind, ProbeWitness, SinkProbe,
-};
+use nyx_scanner::dynamic::probe::{ProbeArg, ProbeChannel, ProbeKind, ProbeWitness, SinkProbe};
use nyx_scanner::dynamic::sandbox::SandboxOutcome;
use nyx_scanner::evidence::InconclusiveReason;
use std::time::Duration;
@@ -116,7 +112,10 @@ fn case_b_outside_sink_crash_does_not_fire_and_is_unrelated() {
let dir = TempDir::new().unwrap();
let channel = ProbeChannel::for_workdir(dir.path()).unwrap();
let probes = channel.drain();
- assert!(probes.is_empty(), "no probe written from outside-sink abort");
+ assert!(
+ probes.is_empty(),
+ "no probe written from outside-sink abort"
+ );
let oracle = Oracle::SinkCrash {
signals: SignalSet::all(),
@@ -131,8 +130,7 @@ fn case_b_outside_sink_crash_does_not_fire_and_is_unrelated() {
// outcome + no probe with a crash signal. Lock the predicate
// here so the runner's wiring in src/dynamic/runner.rs stays in
// sync with what the test labels expect.
- let process_crashed =
- crashed_outcome().exit_code.is_none() && !crashed_outcome().timed_out;
+ let process_crashed = crashed_outcome().exit_code.is_none() && !crashed_outcome().timed_out;
let has_sink_crash_probe = probes.iter().any(|p| probe_crash_signal(p).is_some());
let is_sink_crash_oracle = matches!(oracle, Oracle::SinkCrash { .. });
assert!(is_sink_crash_oracle && process_crashed && !has_sink_crash_probe);
@@ -209,7 +207,10 @@ fn case_c_witness_capture_is_bounded_and_scrubbed() {
assert_eq!(witness.cwd, "/tmp/nyx-run-1");
assert_eq!(witness.callee, "exec");
- assert_eq!(witness.args_repr, vec!["arg0".to_owned(), "arg1".to_owned()]);
+ assert_eq!(
+ witness.args_repr,
+ vec!["arg0".to_owned(), "arg1".to_owned()]
+ );
}
#[test]
@@ -266,13 +267,11 @@ fn signal_wire_format_accepts_canonical_and_short_aliases() {
// The per-language shims write SIGSEGV / SIGABRT / etc. as the
// signal value; downstream JSON consumers and the host-side oracle
// both need to deserialise the same wire format.
- let canonical =
- serde_json::from_str::("\"SIGSEGV\"").expect("canonical SIG name");
+ let canonical = serde_json::from_str::("\"SIGSEGV\"").expect("canonical SIG name");
assert_eq!(canonical, Signal::Sigsegv);
let short = serde_json::from_str::("\"SEGV\"").expect("short alias");
assert_eq!(short, Signal::Sigsegv);
- let title =
- serde_json::from_str::("\"Sigsegv\"").expect("derive-default alias");
+ let title = serde_json::from_str::("\"Sigsegv\"").expect("derive-default alias");
assert_eq!(title, Signal::Sigsegv);
}
@@ -310,10 +309,10 @@ fn signal_set_const_construction_is_order_independent() {
mod e2e_phase_08 {
use crate::common::fixture_harness::FIXTURE_LOCK;
- use nyx_scanner::dynamic::runner::{run_spec, RunOutcome};
+ use nyx_scanner::dynamic::runner::{RunOutcome, run_spec};
use nyx_scanner::dynamic::sandbox::SandboxOptions;
use nyx_scanner::dynamic::spec::{
- default_toolchain_id, EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy,
+ EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy, default_toolchain_id,
};
use nyx_scanner::labels::Cap;
use nyx_scanner::symbol::Lang;
@@ -387,7 +386,9 @@ mod e2e_phase_08 {
#[test]
fn setup_fault_routes_to_unrelated_crash() {
- let Some(outcome) = run("setup_fault.c") else { return };
+ let Some(outcome) = run("setup_fault.c") else {
+ return;
+ };
assert!(
outcome.triggered_by.is_none(),
"setup_fault must not Confirm — handler is never installed: {outcome:?}",
@@ -408,7 +409,9 @@ mod e2e_phase_08 {
#[test]
fn sink_fault_confirms_via_sink_crash_probe() {
- let Some(outcome) = run("sink_fault.c") else { return };
+ let Some(outcome) = run("sink_fault.c") else {
+ return;
+ };
assert!(
outcome.triggered_by.is_some(),
"sink_fault must Confirm via SinkCrash + differential: {outcome:?}",
diff --git a/tests/oracle_sink_probe.rs b/tests/oracle_sink_probe.rs
index ba1b911b..68c6ed12 100644
--- a/tests/oracle_sink_probe.rs
+++ b/tests/oracle_sink_probe.rs
@@ -17,9 +17,9 @@
#![cfg(feature = "dynamic")]
-use nyx_scanner::dynamic::oracle::{oracle_fired, Oracle, ProbePredicate};
+use nyx_scanner::dynamic::oracle::{Oracle, ProbePredicate, oracle_fired};
use nyx_scanner::dynamic::probe::{
- ProbeArg, ProbeChannel, ProbeKind, ProbeWitness, SinkProbe, PROBE_PATH_ENV,
+ PROBE_PATH_ENV, ProbeArg, ProbeChannel, ProbeKind, ProbeWitness, SinkProbe,
};
use std::time::Duration;
use tempfile::TempDir;
@@ -59,7 +59,9 @@ fn synthetic_harness_fires_probe(
kind: ProbeKind::Normal,
witness: ProbeWitness::empty(),
};
- channel.write(&probe).expect("synthetic harness probe write");
+ channel
+ .write(&probe)
+ .expect("synthetic harness probe write");
}
/// "Control" harness — runs the same way but does NOT write a probe.
diff --git a/tests/phase21_corpus.rs b/tests/phase21_corpus.rs
index 6c5503e6..492a2629 100644
--- a/tests/phase21_corpus.rs
+++ b/tests/phase21_corpus.rs
@@ -121,12 +121,7 @@ fn graphql_resolver_supported_in_target_langs() {
#[test]
fn websocket_supported_in_target_langs() {
- for lang in [
- Lang::Python,
- Lang::JavaScript,
- Lang::TypeScript,
- Lang::Ruby,
- ] {
+ for lang in [Lang::Python, Lang::JavaScript, Lang::TypeScript, Lang::Ruby] {
assert!(
lang::entry_kinds_supported(lang).contains(&EntryKindTag::WebSocket),
"{lang:?} must advertise WebSocket after Phase 21",
diff --git a/tests/php_fixtures.rs b/tests/php_fixtures.rs
index 5e2ef65c..c2ca4db8 100644
--- a/tests/php_fixtures.rs
+++ b/tests/php_fixtures.rs
@@ -14,7 +14,7 @@ mod common;
#[cfg(feature = "dynamic")]
mod php_fixture_tests {
use nyx_scanner::commands::scan::Diag;
- use nyx_scanner::dynamic::verify::{verify_finding, VerifyOptions};
+ use nyx_scanner::dynamic::verify::{VerifyOptions, verify_finding};
use nyx_scanner::evidence::{
Confidence, Evidence, FlowStep, FlowStepKind, InconclusiveReason, UnsupportedReason,
VerifyStatus,
@@ -456,7 +456,7 @@ mod php_fixture_tests {
#[cfg(feature = "dynamic")]
mod phase15_shape_tests {
- use crate::common::fixture_harness::{run_shape_fixture_lang_or_skip, Prerequisite};
+ use crate::common::fixture_harness::{Prerequisite, run_shape_fixture_lang_or_skip};
use nyx_scanner::dynamic::spec::PayloadSlot;
use nyx_scanner::evidence::{EntryKind, VerifyResult, VerifyStatus};
use nyx_scanner::labels::Cap;
@@ -506,7 +506,15 @@ mod phase15_shape_tests {
// return; };`.
run_shape_fixture_lang_or_skip(
&[Prerequisite::CommandAvailable("php")],
- Lang::Php, "php", shape, file, func, cap, sink_line, kind, slot,
+ Lang::Php,
+ "php",
+ shape,
+ file,
+ func,
+ cap,
+ sink_line,
+ kind,
+ slot,
)
}
@@ -515,8 +523,13 @@ mod phase15_shape_tests {
#[test]
fn route_closure_vuln_is_confirmed() {
let Some(r) = run(
- "route_closure", "vuln.php", "run", Cap::CODE_EXEC, 10,
- EntryKind::HttpRoute, PayloadSlot::Param(0),
+ "route_closure",
+ "vuln.php",
+ "run",
+ Cap::CODE_EXEC,
+ 10,
+ EntryKind::HttpRoute,
+ PayloadSlot::Param(0),
) else {
return;
};
@@ -526,8 +539,13 @@ mod phase15_shape_tests {
#[test]
fn route_closure_benign_not_confirmed() {
let Some(r) = run(
- "route_closure", "benign.php", "run", Cap::CODE_EXEC, 11,
- EntryKind::HttpRoute, PayloadSlot::Param(0),
+ "route_closure",
+ "benign.php",
+ "run",
+ Cap::CODE_EXEC,
+ 11,
+ EntryKind::HttpRoute,
+ PayloadSlot::Param(0),
) else {
return;
};
@@ -539,8 +557,13 @@ mod phase15_shape_tests {
#[test]
fn cli_script_vuln_is_confirmed() {
let Some(r) = run(
- "cli_script", "vuln.php", "main", Cap::CODE_EXEC, 8,
- EntryKind::CliSubcommand, PayloadSlot::Argv(0),
+ "cli_script",
+ "vuln.php",
+ "main",
+ Cap::CODE_EXEC,
+ 8,
+ EntryKind::CliSubcommand,
+ PayloadSlot::Argv(0),
) else {
return;
};
@@ -550,8 +573,13 @@ mod phase15_shape_tests {
#[test]
fn cli_script_benign_not_confirmed() {
let Some(r) = run(
- "cli_script", "benign.php", "main", Cap::CODE_EXEC, 11,
- EntryKind::CliSubcommand, PayloadSlot::Argv(0),
+ "cli_script",
+ "benign.php",
+ "main",
+ Cap::CODE_EXEC,
+ 11,
+ EntryKind::CliSubcommand,
+ PayloadSlot::Argv(0),
) else {
return;
};
@@ -563,8 +591,13 @@ mod phase15_shape_tests {
#[test]
fn top_level_script_vuln_is_confirmed() {
let Some(r) = run(
- "top_level_script", "vuln.php", "", Cap::CODE_EXEC, 8,
- EntryKind::Function, PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
+ "top_level_script",
+ "vuln.php",
+ "",
+ Cap::CODE_EXEC,
+ 8,
+ EntryKind::Function,
+ PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
) else {
return;
};
@@ -574,8 +607,13 @@ mod phase15_shape_tests {
#[test]
fn top_level_script_benign_not_confirmed() {
let Some(r) = run(
- "top_level_script", "benign.php", "", Cap::CODE_EXEC, 10,
- EntryKind::Function, PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
+ "top_level_script",
+ "benign.php",
+ "",
+ Cap::CODE_EXEC,
+ 10,
+ EntryKind::Function,
+ PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
) else {
return;
};
diff --git a/tests/php_frameworks_corpus.rs b/tests/php_frameworks_corpus.rs
index 4d899a2a..bdc62cbb 100644
--- a/tests/php_frameworks_corpus.rs
+++ b/tests/php_frameworks_corpus.rs
@@ -11,7 +11,7 @@
#![cfg(feature = "dynamic")]
-use nyx_scanner::dynamic::framework::{detect_binding, HttpMethod, ParamSource};
+use nyx_scanner::dynamic::framework::{HttpMethod, ParamSource, detect_binding};
use nyx_scanner::evidence::EntryKind;
use nyx_scanner::summary::FuncSummary;
use nyx_scanner::symbol::Lang;
diff --git a/tests/policy_deny.rs b/tests/policy_deny.rs
index d7f1ddf3..4c21173a 100644
--- a/tests/policy_deny.rs
+++ b/tests/policy_deny.rs
@@ -12,7 +12,7 @@
use nyx_scanner::commands::scan::Diag;
use nyx_scanner::dynamic::policy::{self, DenyRule, PolicyDecision};
-use nyx_scanner::dynamic::verify::{verify_finding, VerifyOptions};
+use nyx_scanner::dynamic::verify::{VerifyOptions, verify_finding};
use nyx_scanner::evidence::{
Confidence, Evidence, FlowStep, FlowStepKind, InconclusiveReason, SpanEvidence, VerifyStatus,
};
@@ -78,9 +78,7 @@ fn allow_returns_for_diag_without_secrets() {
fn credentials_rule_fires_on_aws_key_in_flow_step_snippet() {
let mut diag = empty_diag();
let mut ev = Evidence::default();
- ev.flow_steps = vec![flow_step_with_snippet(
- "key=AKIAFAKETEST00000000",
- )];
+ ev.flow_steps = vec![flow_step_with_snippet("key=AKIAFAKETEST00000000")];
diag.evidence = Some(ev);
match policy::evaluate(&diag) {
PolicyDecision::Deny {
@@ -116,9 +114,7 @@ fn credentials_rule_fires_on_bearer_header_note() {
fn private_key_rule_fires_on_pem_block_in_snippet() {
let mut diag = empty_diag();
let mut ev = Evidence::default();
- ev.source = Some(span_with_snippet(
- "-----BEGIN OPENSSH PRIVATE KEY-----",
- ));
+ ev.source = Some(span_with_snippet("-----BEGIN OPENSSH PRIVATE KEY-----"));
diag.evidence = Some(ev);
match policy::evaluate(&diag) {
PolicyDecision::Deny { rule, .. } => {
@@ -185,9 +181,7 @@ fn credentials_rule_fires_before_other_rules() {
// endpoint name. Order asserted by the policy.evaluate impl.
let mut diag = empty_diag();
let mut ev = Evidence::default();
- ev.notes = vec![
- "deploying key=AKIAFAKETEST00000000 to api.prod.example.com".to_owned(),
- ];
+ ev.notes = vec!["deploying key=AKIAFAKETEST00000000 to api.prod.example.com".to_owned()];
diag.evidence = Some(ev);
match policy::evaluate(&diag) {
PolicyDecision::Deny { rule, .. } => {
diff --git a/tests/prototype_pollution_corpus.rs b/tests/prototype_pollution_corpus.rs
index 07dea6cc..f3e995d9 100644
--- a/tests/prototype_pollution_corpus.rs
+++ b/tests/prototype_pollution_corpus.rs
@@ -16,12 +16,12 @@
mod common;
use nyx_scanner::dynamic::corpus::{
- audit_marker_collisions, benign_payload_for_lang, payloads_for_lang,
- resolve_benign_control_lang, Oracle,
+ Oracle, audit_marker_collisions, benign_payload_for_lang, payloads_for_lang,
+ resolve_benign_control_lang,
};
use nyx_scanner::dynamic::framework::registry::adapters_for;
use nyx_scanner::dynamic::lang;
-use nyx_scanner::dynamic::oracle::{oracle_fired, ProbePredicate};
+use nyx_scanner::dynamic::oracle::{ProbePredicate, oracle_fired};
use nyx_scanner::dynamic::probe::{ProbeKind, ProbeWitness, SinkProbe};
use nyx_scanner::dynamic::sandbox::SandboxOutcome;
use nyx_scanner::dynamic::spec::{EntryKind, HarnessSpec, PayloadSlot};
@@ -63,7 +63,10 @@ fn corpus_registers_prototype_pollution_for_js_and_ts() {
);
let has_vuln = slice.iter().any(|p| !p.is_benign);
let has_benign = slice.iter().any(|p| p.is_benign);
- assert!(has_vuln, "{lang:?} PROTOTYPE_POLLUTION missing vuln payload");
+ assert!(
+ has_vuln,
+ "{lang:?} PROTOTYPE_POLLUTION missing vuln payload"
+ );
assert!(
has_benign,
"{lang:?} PROTOTYPE_POLLUTION missing benign control"
@@ -111,10 +114,9 @@ fn payload_oracle_carries_prototype_canary_predicate() {
match &vuln.oracle {
Oracle::SinkProbe { predicates } => {
assert!(
- predicates.iter().any(|p| matches!(
- p,
- ProbePredicate::PrototypeCanaryTouched { .. }
- )),
+ predicates
+ .iter()
+ .any(|p| matches!(p, ProbePredicate::PrototypeCanaryTouched { .. })),
"{lang:?} vuln payload missing PrototypeCanaryTouched predicate",
);
}
@@ -246,7 +248,9 @@ fn lang_emitter_dispatches_to_prototype_pollution_harness() {
"{lang:?} harness must reference the canary property name",
);
assert!(
- harness.source.contains("Object.defineProperty(Object.prototype"),
+ harness
+ .source
+ .contains("Object.defineProperty(Object.prototype"),
"{lang:?} harness must install the canary trap on Object.prototype",
);
assert!(
@@ -408,10 +412,10 @@ fn slug(lang: Lang) -> &'static str {
mod e2e_phase_10 {
use crate::common::fixture_harness::FIXTURE_LOCK;
- use nyx_scanner::dynamic::runner::{run_spec, RunError, RunOutcome};
+ use nyx_scanner::dynamic::runner::{RunError, RunOutcome, run_spec};
use nyx_scanner::dynamic::sandbox::{SandboxBackend, SandboxOptions};
use nyx_scanner::dynamic::spec::{
- default_toolchain_id, EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy,
+ EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy, default_toolchain_id,
};
use nyx_scanner::evidence::DifferentialVerdict;
use nyx_scanner::labels::Cap;
@@ -523,13 +527,17 @@ mod e2e_phase_10 {
#[test]
fn js_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::JavaScript, "vuln.js", "run") else { return };
+ let Some(outcome) = run(Lang::JavaScript, "vuln.js", "run") else {
+ return;
+ };
assert_confirmed(Lang::JavaScript, &outcome);
}
#[test]
fn ts_vuln_confirms_via_run_spec() {
- let Some(outcome) = run(Lang::TypeScript, "vuln.ts", "run") else { return };
+ let Some(outcome) = run(Lang::TypeScript, "vuln.ts", "run") else {
+ return;
+ };
assert_confirmed(Lang::TypeScript, &outcome);
}
}
diff --git a/tests/python_fixtures.rs b/tests/python_fixtures.rs
index 74ed8c34..8a94f5bb 100644
--- a/tests/python_fixtures.rs
+++ b/tests/python_fixtures.rs
@@ -14,15 +14,14 @@ mod common;
#[cfg(feature = "dynamic")]
mod python_fixture_tests {
use crate::common::fixture_harness::{
- run_fixture_and_compare_to_golden, run_harness_snapshot, run_shape_fixture,
- CopyStrategy, FixtureSpec, Prerequisite,
+ CopyStrategy, FixtureSpec, Prerequisite, run_fixture_and_compare_to_golden,
+ run_harness_snapshot, run_shape_fixture,
};
use nyx_scanner::commands::scan::Diag;
use nyx_scanner::dynamic::spec::PayloadSlot;
- use nyx_scanner::dynamic::verify::{verify_finding, VerifyOptions};
+ use nyx_scanner::dynamic::verify::{VerifyOptions, verify_finding};
use nyx_scanner::evidence::{
- Confidence, EntryKind, Evidence, FlowStep, FlowStepKind, UnsupportedReason,
- VerifyStatus,
+ Confidence, EntryKind, Evidence, FlowStep, FlowStepKind, UnsupportedReason, VerifyStatus,
};
use nyx_scanner::labels::Cap;
use nyx_scanner::patterns::{FindingCategory, Severity};
@@ -39,7 +38,12 @@ mod python_fixture_tests {
.unwrap_or(false)
}
- fn spec(fixture: &'static str, func: &'static str, cap: Cap, sink_line: u32) -> FixtureSpec<'static> {
+ fn spec(
+ fixture: &'static str,
+ func: &'static str,
+ cap: Cap,
+ sink_line: u32,
+ ) -> FixtureSpec<'static> {
FixtureSpec {
lang_dir: "python",
fixture,
@@ -82,13 +86,19 @@ mod python_fixture_tests {
#[test]
fn sqli_positive_matches_golden() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
run_fixture_and_compare_to_golden(&spec("sqli_positive.py", "login", Cap::SQL_QUERY, 17));
}
#[test]
fn sqli_negative_matches_golden() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
run_fixture_and_compare_to_golden(&spec("sqli_negative.py", "login", Cap::SQL_QUERY, 12));
}
@@ -104,22 +114,46 @@ mod python_fixture_tests {
#[test]
fn sqli_adversarial_matches_golden() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
- run_fixture_and_compare_to_golden(&spec("sqli_adversarial.py", "get_value", Cap::SQL_QUERY, 999));
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
+ run_fixture_and_compare_to_golden(&spec(
+ "sqli_adversarial.py",
+ "get_value",
+ Cap::SQL_QUERY,
+ 999,
+ ));
}
// ── Command injection ────────────────────────────────────────────────────
#[test]
fn cmdi_positive_matches_golden() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
- run_fixture_and_compare_to_golden(&spec("cmdi_positive.py", "run_ping", Cap::CODE_EXEC, 13));
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
+ run_fixture_and_compare_to_golden(&spec(
+ "cmdi_positive.py",
+ "run_ping",
+ Cap::CODE_EXEC,
+ 13,
+ ));
}
#[test]
fn cmdi_negative_matches_golden() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
- run_fixture_and_compare_to_golden(&spec("cmdi_negative.py", "run_ping", Cap::CODE_EXEC, 17));
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
+ run_fixture_and_compare_to_golden(&spec(
+ "cmdi_negative.py",
+ "run_ping",
+ Cap::CODE_EXEC,
+ 17,
+ ));
}
#[test]
@@ -134,7 +168,10 @@ mod python_fixture_tests {
#[test]
fn cmdi_adversarial_matches_golden() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
run_fixture_and_compare_to_golden(&spec(
"cmdi_adversarial.py",
"process_input",
@@ -147,14 +184,30 @@ mod python_fixture_tests {
#[test]
fn fileio_positive_matches_golden() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
- run_fixture_and_compare_to_golden(&spec("fileio_positive.py", "read_file", Cap::FILE_IO, 11));
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
+ run_fixture_and_compare_to_golden(&spec(
+ "fileio_positive.py",
+ "read_file",
+ Cap::FILE_IO,
+ 11,
+ ));
}
#[test]
fn fileio_negative_matches_golden() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
- run_fixture_and_compare_to_golden(&spec("fileio_negative.py", "read_file", Cap::FILE_IO, 18));
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
+ run_fixture_and_compare_to_golden(&spec(
+ "fileio_negative.py",
+ "read_file",
+ Cap::FILE_IO,
+ 18,
+ ));
}
#[test]
@@ -169,21 +222,35 @@ mod python_fixture_tests {
#[test]
fn fileio_adversarial_matches_golden() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
- run_fixture_and_compare_to_golden(&spec("fileio_adversarial.py", "read_file", Cap::FILE_IO, 999));
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
+ run_fixture_and_compare_to_golden(&spec(
+ "fileio_adversarial.py",
+ "read_file",
+ Cap::FILE_IO,
+ 999,
+ ));
}
// ── SSRF ─────────────────────────────────────────────────────────────────
#[test]
fn ssrf_positive_matches_golden() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
run_fixture_and_compare_to_golden(&spec("ssrf_positive.py", "fetch_url", Cap::SSRF, 11));
}
#[test]
fn ssrf_negative_matches_golden() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
run_fixture_and_compare_to_golden(&spec("ssrf_negative.py", "fetch_url", Cap::SSRF, 26));
}
@@ -194,15 +261,26 @@ mod python_fixture_tests {
#[test]
fn ssrf_adversarial_matches_golden() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
- run_fixture_and_compare_to_golden(&spec("ssrf_adversarial.py", "fetch_url", Cap::SSRF, 999));
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
+ run_fixture_and_compare_to_golden(&spec(
+ "ssrf_adversarial.py",
+ "fetch_url",
+ Cap::SSRF,
+ 999,
+ ));
}
// ── XSS ──────────────────────────────────────────────────────────────────
#[test]
fn xss_positive_matches_golden() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
run_fixture_and_compare_to_golden(&spec(
"xss_positive.py",
"render_comment",
@@ -213,7 +291,10 @@ mod python_fixture_tests {
#[test]
fn xss_negative_matches_golden() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
run_fixture_and_compare_to_golden(&spec(
"xss_negative.py",
"render_comment",
@@ -234,7 +315,10 @@ mod python_fixture_tests {
#[test]
fn xss_adversarial_matches_golden() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
run_fixture_and_compare_to_golden(&spec(
"xss_adversarial.py",
"render_comment",
@@ -342,20 +426,36 @@ mod python_fixture_tests {
#[test]
fn generic_vuln_is_confirmed() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
let r = run_shape_fixture(
- "generic", "vuln.py", "run_ping", Cap::CODE_EXEC, 12,
- EntryKind::Function, PayloadSlot::Param(0),
+ "generic",
+ "vuln.py",
+ "run_ping",
+ Cap::CODE_EXEC,
+ 12,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
);
assert_confirmed("generic", &r);
}
#[test]
fn generic_benign_not_confirmed() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
let r = run_shape_fixture(
- "generic", "benign.py", "run_ping", Cap::CODE_EXEC, 20,
- EntryKind::Function, PayloadSlot::Param(0),
+ "generic",
+ "benign.py",
+ "run_ping",
+ Cap::CODE_EXEC,
+ 20,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
);
assert_not_confirmed("generic", &r);
}
@@ -363,8 +463,13 @@ mod python_fixture_tests {
#[test]
fn generic_harness_snapshot_matches_golden() {
run_harness_snapshot(
- "generic", "vuln.py", "run_ping", Cap::CODE_EXEC, 12,
- EntryKind::Function, PayloadSlot::Param(0),
+ "generic",
+ "vuln.py",
+ "run_ping",
+ Cap::CODE_EXEC,
+ 12,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
);
}
@@ -372,20 +477,36 @@ mod python_fixture_tests {
#[test]
fn cli_vuln_is_confirmed() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
let r = run_shape_fixture(
- "cli", "vuln.py", "main", Cap::CODE_EXEC, 14,
- EntryKind::CliSubcommand, PayloadSlot::Argv(0),
+ "cli",
+ "vuln.py",
+ "main",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::CliSubcommand,
+ PayloadSlot::Argv(0),
);
assert_confirmed("cli", &r);
}
#[test]
fn cli_benign_not_confirmed() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
let r = run_shape_fixture(
- "cli", "benign.py", "main", Cap::CODE_EXEC, 11,
- EntryKind::CliSubcommand, PayloadSlot::Argv(0),
+ "cli",
+ "benign.py",
+ "main",
+ Cap::CODE_EXEC,
+ 11,
+ EntryKind::CliSubcommand,
+ PayloadSlot::Argv(0),
);
assert_not_confirmed("cli", &r);
}
@@ -393,8 +514,13 @@ mod python_fixture_tests {
#[test]
fn cli_harness_snapshot_matches_golden() {
run_harness_snapshot(
- "cli", "vuln.py", "main", Cap::CODE_EXEC, 14,
- EntryKind::CliSubcommand, PayloadSlot::Argv(0),
+ "cli",
+ "vuln.py",
+ "main",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::CliSubcommand,
+ PayloadSlot::Argv(0),
);
}
@@ -402,20 +528,36 @@ mod python_fixture_tests {
#[test]
fn pytest_vuln_is_confirmed() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
let r = run_shape_fixture(
- "pytest", "vuln.py", "test_run_ping", Cap::CODE_EXEC, 14,
- EntryKind::Function, PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
+ "pytest",
+ "vuln.py",
+ "test_run_ping",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::Function,
+ PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
);
assert_confirmed("pytest", &r);
}
#[test]
fn pytest_benign_not_confirmed() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
let r = run_shape_fixture(
- "pytest", "benign.py", "test_run_ping", Cap::CODE_EXEC, 14,
- EntryKind::Function, PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
+ "pytest",
+ "benign.py",
+ "test_run_ping",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::Function,
+ PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
);
assert_not_confirmed("pytest", &r);
}
@@ -423,8 +565,13 @@ mod python_fixture_tests {
#[test]
fn pytest_harness_snapshot_matches_golden() {
run_harness_snapshot(
- "pytest", "vuln.py", "test_run_ping", Cap::CODE_EXEC, 14,
- EntryKind::Function, PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
+ "pytest",
+ "vuln.py",
+ "test_run_ping",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::Function,
+ PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
);
}
@@ -432,20 +579,36 @@ mod python_fixture_tests {
#[test]
fn async_vuln_is_confirmed() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
let r = run_shape_fixture(
- "async", "vuln.py", "run_ping", Cap::CODE_EXEC, 13,
- EntryKind::Function, PayloadSlot::Param(0),
+ "async",
+ "vuln.py",
+ "run_ping",
+ Cap::CODE_EXEC,
+ 13,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
);
assert_confirmed("async", &r);
}
#[test]
fn async_benign_not_confirmed() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
let r = run_shape_fixture(
- "async", "benign.py", "run_ping", Cap::CODE_EXEC, 14,
- EntryKind::Function, PayloadSlot::Param(0),
+ "async",
+ "benign.py",
+ "run_ping",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
);
assert_not_confirmed("async", &r);
}
@@ -453,8 +616,13 @@ mod python_fixture_tests {
#[test]
fn async_harness_snapshot_matches_golden() {
run_harness_snapshot(
- "async", "vuln.py", "run_ping", Cap::CODE_EXEC, 13,
- EntryKind::Function, PayloadSlot::Param(0),
+ "async",
+ "vuln.py",
+ "run_ping",
+ Cap::CODE_EXEC,
+ 13,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
);
}
@@ -462,28 +630,44 @@ mod python_fixture_tests {
#[test]
fn celery_vuln_is_confirmed() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
if !python_module_available("celery") {
eprintln!("SKIP: celery not importable");
return;
}
let r = run_shape_fixture(
- "celery", "vuln.py", "run_job", Cap::CODE_EXEC, 17,
- EntryKind::Function, PayloadSlot::Param(0),
+ "celery",
+ "vuln.py",
+ "run_job",
+ Cap::CODE_EXEC,
+ 17,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
);
assert_confirmed("celery", &r);
}
#[test]
fn celery_benign_not_confirmed() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
if !python_module_available("celery") {
eprintln!("SKIP: celery not importable");
return;
}
let r = run_shape_fixture(
- "celery", "benign.py", "run_job", Cap::CODE_EXEC, 17,
- EntryKind::Function, PayloadSlot::Param(0),
+ "celery",
+ "benign.py",
+ "run_job",
+ Cap::CODE_EXEC,
+ 17,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
);
assert_not_confirmed("celery", &r);
}
@@ -491,8 +675,13 @@ mod python_fixture_tests {
#[test]
fn celery_harness_snapshot_matches_golden() {
run_harness_snapshot(
- "celery", "vuln.py", "run_job", Cap::CODE_EXEC, 17,
- EntryKind::Function, PayloadSlot::Param(0),
+ "celery",
+ "vuln.py",
+ "run_job",
+ Cap::CODE_EXEC,
+ 17,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
);
}
@@ -500,28 +689,44 @@ mod python_fixture_tests {
#[test]
fn flask_vuln_is_confirmed() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
if !python_module_available("flask") {
eprintln!("SKIP: flask not importable");
return;
}
let r = run_shape_fixture(
- "flask", "vuln.py", "ping", Cap::CODE_EXEC, 18,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("host".into()),
+ "flask",
+ "vuln.py",
+ "ping",
+ Cap::CODE_EXEC,
+ 18,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("host".into()),
);
assert_confirmed("flask", &r);
}
#[test]
fn flask_benign_not_confirmed() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
if !python_module_available("flask") {
eprintln!("SKIP: flask not importable");
return;
}
let r = run_shape_fixture(
- "flask", "benign.py", "ping", Cap::CODE_EXEC, 17,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("host".into()),
+ "flask",
+ "benign.py",
+ "ping",
+ Cap::CODE_EXEC,
+ 17,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("host".into()),
);
assert_not_confirmed("flask", &r);
}
@@ -529,8 +734,13 @@ mod python_fixture_tests {
#[test]
fn flask_harness_snapshot_matches_golden() {
run_harness_snapshot(
- "flask", "vuln.py", "ping", Cap::CODE_EXEC, 18,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("host".into()),
+ "flask",
+ "vuln.py",
+ "ping",
+ Cap::CODE_EXEC,
+ 18,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("host".into()),
);
}
@@ -538,28 +748,44 @@ mod python_fixture_tests {
#[test]
fn fastapi_vuln_is_confirmed() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
if !python_module_available("fastapi") {
eprintln!("SKIP: fastapi not importable");
return;
}
let r = run_shape_fixture(
- "fastapi", "vuln.py", "ping", Cap::CODE_EXEC, 16,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("host".into()),
+ "fastapi",
+ "vuln.py",
+ "ping",
+ Cap::CODE_EXEC,
+ 16,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("host".into()),
);
assert_confirmed("fastapi", &r);
}
#[test]
fn fastapi_benign_not_confirmed() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
if !python_module_available("fastapi") {
eprintln!("SKIP: fastapi not importable");
return;
}
let r = run_shape_fixture(
- "fastapi", "benign.py", "ping", Cap::CODE_EXEC, 16,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("host".into()),
+ "fastapi",
+ "benign.py",
+ "ping",
+ Cap::CODE_EXEC,
+ 16,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("host".into()),
);
assert_not_confirmed("fastapi", &r);
}
@@ -567,8 +793,13 @@ mod python_fixture_tests {
#[test]
fn fastapi_harness_snapshot_matches_golden() {
run_harness_snapshot(
- "fastapi", "vuln.py", "ping", Cap::CODE_EXEC, 16,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("host".into()),
+ "fastapi",
+ "vuln.py",
+ "ping",
+ Cap::CODE_EXEC,
+ 16,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("host".into()),
);
}
@@ -576,28 +807,44 @@ mod python_fixture_tests {
#[test]
fn django_vuln_is_confirmed() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
if !python_module_available("django") {
eprintln!("SKIP: django not importable");
return;
}
let r = run_shape_fixture(
- "django", "vuln.py", "ping", Cap::CODE_EXEC, 15,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("host".into()),
+ "django",
+ "vuln.py",
+ "ping",
+ Cap::CODE_EXEC,
+ 15,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("host".into()),
);
assert_confirmed("django", &r);
}
#[test]
fn django_benign_not_confirmed() {
- if !python3_available() { eprintln!("SKIP: python3 not available"); return; }
+ if !python3_available() {
+ eprintln!("SKIP: python3 not available");
+ return;
+ }
if !python_module_available("django") {
eprintln!("SKIP: django not importable");
return;
}
let r = run_shape_fixture(
- "django", "benign.py", "ping", Cap::CODE_EXEC, 14,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("host".into()),
+ "django",
+ "benign.py",
+ "ping",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("host".into()),
);
assert_not_confirmed("django", &r);
}
@@ -605,8 +852,13 @@ mod python_fixture_tests {
#[test]
fn django_harness_snapshot_matches_golden() {
run_harness_snapshot(
- "django", "vuln.py", "ping", Cap::CODE_EXEC, 15,
- EntryKind::HttpRoute, PayloadSlot::QueryParam("host".into()),
+ "django",
+ "vuln.py",
+ "ping",
+ Cap::CODE_EXEC,
+ 15,
+ EntryKind::HttpRoute,
+ PayloadSlot::QueryParam("host".into()),
);
}
diff --git a/tests/python_frameworks_corpus.rs b/tests/python_frameworks_corpus.rs
index a0b96efa..33e14234 100644
--- a/tests/python_frameworks_corpus.rs
+++ b/tests/python_frameworks_corpus.rs
@@ -21,7 +21,7 @@
mod common;
-use nyx_scanner::dynamic::framework::{detect_binding, HttpMethod, ParamSource};
+use nyx_scanner::dynamic::framework::{HttpMethod, ParamSource, detect_binding};
use nyx_scanner::evidence::EntryKind;
use nyx_scanner::summary::FuncSummary;
use nyx_scanner::symbol::Lang;
@@ -193,10 +193,10 @@ fn fastapi_adapter_runs_before_starlette_for_fastapi_files() {
#[cfg(feature = "dynamic")]
mod e2e_phase_12 {
use crate::common::fixture_harness::FIXTURE_LOCK;
- use nyx_scanner::dynamic::runner::{run_spec, RunError, RunOutcome};
+ use nyx_scanner::dynamic::runner::{RunError, RunOutcome, run_spec};
use nyx_scanner::dynamic::sandbox::SandboxOptions;
use nyx_scanner::dynamic::spec::{
- default_toolchain_id, EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy,
+ EntryKind, HarnessSpec, PayloadSlot, SpecDerivationStrategy, default_toolchain_id,
};
use nyx_scanner::evidence::DifferentialVerdict;
use nyx_scanner::labels::Cap;
@@ -277,7 +277,9 @@ mod e2e_phase_12 {
}
fn assert_confirmed(fixture_subdir: &str) {
- let Some(outcome) = run(fixture_subdir) else { return };
+ let Some(outcome) = run(fixture_subdir) else {
+ return;
+ };
assert!(
outcome.triggered_by.is_some(),
"{fixture_subdir} CODE_EXEC vuln must Confirm via run_spec; got {outcome:?}",
diff --git a/tests/repro_determinism.rs b/tests/repro_determinism.rs
index 16d409d3..3f8c5757 100644
--- a/tests/repro_determinism.rs
+++ b/tests/repro_determinism.rs
@@ -90,16 +90,19 @@ mod repro_determinism_tests {
// Write repro bundle (first time).
let artifact1 = repro::write(
- &spec, &opts, &outcome, &verdict,
+ &spec,
+ &opts,
+ &outcome,
+ &verdict,
"# harness source v1\n",
"def login(x): pass\n",
b"' UNION SELECT 'NYX_SQL_CONFIRMED'--",
"sqli-union-nyx",
None,
- ).expect("first repro write must succeed");
+ )
+ .expect("first repro write must succeed");
- let outcome_json_1 =
- std::fs::read_to_string(artifact1.root.join("expected/outcome.json"))
+ let outcome_json_1 = std::fs::read_to_string(artifact1.root.join("expected/outcome.json"))
.expect("outcome.json must exist after first write");
// Write repro bundle (second time, same inputs).
@@ -107,16 +110,19 @@ mod repro_determinism_tests {
std::fs::remove_dir_all(&artifact1.root).unwrap();
let artifact2 = repro::write(
- &spec, &opts, &outcome, &verdict,
+ &spec,
+ &opts,
+ &outcome,
+ &verdict,
"# harness source v1\n",
"def login(x): pass\n",
b"' UNION SELECT 'NYX_SQL_CONFIRMED'--",
"sqli-union-nyx",
None,
- ).expect("second repro write must succeed");
+ )
+ .expect("second repro write must succeed");
- let outcome_json_2 =
- std::fs::read_to_string(artifact2.root.join("expected/outcome.json"))
+ let outcome_json_2 = std::fs::read_to_string(artifact2.root.join("expected/outcome.json"))
.expect("outcome.json must exist after second write");
assert_eq!(
@@ -141,9 +147,17 @@ mod repro_determinism_tests {
let verdict = make_confirmed_verdict("determinism00002");
let artifact = repro::write(
- &spec, &opts, &outcome, &verdict,
- "# harness", "# entry", b"payload", "label", None,
- ).expect("repro write must succeed");
+ &spec,
+ &opts,
+ &outcome,
+ &verdict,
+ "# harness",
+ "# entry",
+ b"payload",
+ "label",
+ None,
+ )
+ .expect("repro write must succeed");
let outcome_json =
std::fs::read_to_string(artifact.root.join("expected/outcome.json")).unwrap();
@@ -262,8 +276,7 @@ fn main() {
None,
)
.expect("first Rust repro write");
- let json1 =
- std::fs::read_to_string(artifact1.root.join("expected/outcome.json")).unwrap();
+ let json1 = std::fs::read_to_string(artifact1.root.join("expected/outcome.json")).unwrap();
std::fs::remove_dir_all(&artifact1.root).unwrap();
@@ -279,8 +292,7 @@ fn main() {
None,
)
.expect("second Rust repro write");
- let json2 =
- std::fs::read_to_string(artifact2.root.join("expected/outcome.json")).unwrap();
+ let json2 = std::fs::read_to_string(artifact2.root.join("expected/outcome.json")).unwrap();
assert_eq!(
json1, json2,
@@ -325,24 +337,39 @@ fn main() {
let entry_src = "function login(username) { console.log(username); }\n";
let artifact1 = repro::write(
- &spec, &opts, &outcome, &verdict,
- "// harness js\n", entry_src,
- b"' UNION SELECT 'NYX_SQL_CONFIRMED'--", "sqli-union-nyx", None,
- ).expect("first JS repro write");
- let json1 =
- std::fs::read_to_string(artifact1.root.join("expected/outcome.json")).unwrap();
+ &spec,
+ &opts,
+ &outcome,
+ &verdict,
+ "// harness js\n",
+ entry_src,
+ b"' UNION SELECT 'NYX_SQL_CONFIRMED'--",
+ "sqli-union-nyx",
+ None,
+ )
+ .expect("first JS repro write");
+ let json1 = std::fs::read_to_string(artifact1.root.join("expected/outcome.json")).unwrap();
std::fs::remove_dir_all(&artifact1.root).unwrap();
let artifact2 = repro::write(
- &spec, &opts, &outcome, &verdict,
- "// harness js\n", entry_src,
- b"' UNION SELECT 'NYX_SQL_CONFIRMED'--", "sqli-union-nyx", None,
- ).expect("second JS repro write");
- let json2 =
- std::fs::read_to_string(artifact2.root.join("expected/outcome.json")).unwrap();
+ &spec,
+ &opts,
+ &outcome,
+ &verdict,
+ "// harness js\n",
+ entry_src,
+ b"' UNION SELECT 'NYX_SQL_CONFIRMED'--",
+ "sqli-union-nyx",
+ None,
+ )
+ .expect("second JS repro write");
+ let json2 = std::fs::read_to_string(artifact2.root.join("expected/outcome.json")).unwrap();
- assert_eq!(json1, json2, "JS outcome.json must be byte-identical across two writes");
+ assert_eq!(
+ json1, json2,
+ "JS outcome.json must be byte-identical across two writes"
+ );
unsafe { std::env::remove_var("NYX_REPRO_BASE") };
}
@@ -382,24 +409,39 @@ fn main() {
let entry_src = "package entry\nfunc Login(username string) {}\n";
let artifact1 = repro::write(
- &spec, &opts, &outcome, &verdict,
- "// harness go\n", entry_src,
- b"' UNION SELECT 'NYX_SQL_CONFIRMED'--", "sqli-union-nyx", None,
- ).expect("first Go repro write");
- let json1 =
- std::fs::read_to_string(artifact1.root.join("expected/outcome.json")).unwrap();
+ &spec,
+ &opts,
+ &outcome,
+ &verdict,
+ "// harness go\n",
+ entry_src,
+ b"' UNION SELECT 'NYX_SQL_CONFIRMED'--",
+ "sqli-union-nyx",
+ None,
+ )
+ .expect("first Go repro write");
+ let json1 = std::fs::read_to_string(artifact1.root.join("expected/outcome.json")).unwrap();
std::fs::remove_dir_all(&artifact1.root).unwrap();
let artifact2 = repro::write(
- &spec, &opts, &outcome, &verdict,
- "// harness go\n", entry_src,
- b"' UNION SELECT 'NYX_SQL_CONFIRMED'--", "sqli-union-nyx", None,
- ).expect("second Go repro write");
- let json2 =
- std::fs::read_to_string(artifact2.root.join("expected/outcome.json")).unwrap();
+ &spec,
+ &opts,
+ &outcome,
+ &verdict,
+ "// harness go\n",
+ entry_src,
+ b"' UNION SELECT 'NYX_SQL_CONFIRMED'--",
+ "sqli-union-nyx",
+ None,
+ )
+ .expect("second Go repro write");
+ let json2 = std::fs::read_to_string(artifact2.root.join("expected/outcome.json")).unwrap();
- assert_eq!(json1, json2, "Go outcome.json must be byte-identical across two writes");
+ assert_eq!(
+ json1, json2,
+ "Go outcome.json must be byte-identical across two writes"
+ );
unsafe { std::env::remove_var("NYX_REPRO_BASE") };
}
@@ -439,24 +481,39 @@ fn main() {
let entry_src = "public class Entry { public static void login(String u) {} }\n";
let artifact1 = repro::write(
- &spec, &opts, &outcome, &verdict,
- "// NyxHarness.java\n", entry_src,
- b"' UNION SELECT 'NYX_SQL_CONFIRMED'--", "sqli-union-nyx", None,
- ).expect("first Java repro write");
- let json1 =
- std::fs::read_to_string(artifact1.root.join("expected/outcome.json")).unwrap();
+ &spec,
+ &opts,
+ &outcome,
+ &verdict,
+ "// NyxHarness.java\n",
+ entry_src,
+ b"' UNION SELECT 'NYX_SQL_CONFIRMED'--",
+ "sqli-union-nyx",
+ None,
+ )
+ .expect("first Java repro write");
+ let json1 = std::fs::read_to_string(artifact1.root.join("expected/outcome.json")).unwrap();
std::fs::remove_dir_all(&artifact1.root).unwrap();
let artifact2 = repro::write(
- &spec, &opts, &outcome, &verdict,
- "// NyxHarness.java\n", entry_src,
- b"' UNION SELECT 'NYX_SQL_CONFIRMED'--", "sqli-union-nyx", None,
- ).expect("second Java repro write");
- let json2 =
- std::fs::read_to_string(artifact2.root.join("expected/outcome.json")).unwrap();
+ &spec,
+ &opts,
+ &outcome,
+ &verdict,
+ "// NyxHarness.java\n",
+ entry_src,
+ b"' UNION SELECT 'NYX_SQL_CONFIRMED'--",
+ "sqli-union-nyx",
+ None,
+ )
+ .expect("second Java repro write");
+ let json2 = std::fs::read_to_string(artifact2.root.join("expected/outcome.json")).unwrap();
- assert_eq!(json1, json2, "Java outcome.json must be byte-identical across two writes");
+ assert_eq!(
+ json1, json2,
+ "Java outcome.json must be byte-identical across two writes"
+ );
unsafe { std::env::remove_var("NYX_REPRO_BASE") };
}
@@ -496,24 +553,39 @@ fn main() {
let entry_src = " VerifyResult {
reason: None,
inconclusive_reason: None,
detail: Some(
- "flask_eval chain composer fixture: eval(NYX_PAYLOAD) under python-3.11"
- .into(),
+ "flask_eval chain composer fixture: eval(NYX_PAYLOAD) under python-3.11".into(),
),
attempts: vec![AttemptSummary {
payload_label: FLASK_EVAL_PAYLOAD_LABEL.into(),
@@ -167,10 +166,8 @@ fn flask_eval_bundle_root() -> PathBuf {
}
fn read_json(path: &Path) -> serde_json::Value {
- let bytes = std::fs::read(path)
- .unwrap_or_else(|e| panic!("read {}: {e}", path.display()));
- serde_json::from_slice(&bytes)
- .unwrap_or_else(|e| panic!("parse {}: {e}", path.display()))
+ let bytes = std::fs::read(path).unwrap_or_else(|e| panic!("read {}: {e}", path.display()));
+ serde_json::from_slice(&bytes).unwrap_or_else(|e| panic!("parse {}: {e}", path.display()))
}
/// Regenerate the committed flask_eval bundle. Run with `--ignored` to
@@ -206,8 +203,7 @@ fn regen_python_3_11_flask_eval_bundle() {
}
assert_eq!(
- artifact.root,
- bundle_root,
+ artifact.root, bundle_root,
"bundle wrote to unexpected path",
);
}
diff --git a/tests/repro_hermetic.rs b/tests/repro_hermetic.rs
index 1ca052c2..d81905be 100644
--- a/tests/repro_hermetic.rs
+++ b/tests/repro_hermetic.rs
@@ -29,7 +29,7 @@
#[cfg(feature = "dynamic")]
mod repro_hermetic_tests {
use nyx_scanner::dynamic::repro;
- use nyx_scanner::dynamic::repro::{replay_bundle, ReplayResult};
+ use nyx_scanner::dynamic::repro::{ReplayResult, replay_bundle};
use nyx_scanner::dynamic::sandbox::{SandboxOptions, SandboxOutcome};
use nyx_scanner::dynamic::spec::{EntryKind, HarnessSpec, PayloadSlot};
use nyx_scanner::evidence::{AttemptSummary, VerifyResult, VerifyStatus};
@@ -110,7 +110,8 @@ mod repro_hermetic_tests {
b"' OR 1=1-- NYX",
"sqli-or-1",
None,
- ).unwrap();
+ )
+ .unwrap();
let lock_path = artifact.root.join("toolchain.lock");
assert!(lock_path.exists(), "toolchain.lock missing from bundle");
@@ -135,10 +136,16 @@ mod repro_hermetic_tests {
b"' OR 1=1-- NYX",
"sqli-or-1",
None,
- ).unwrap();
- let lock2: serde_json::Value =
- serde_json::from_str(&std::fs::read_to_string(artifact2.root.join("toolchain.lock")).unwrap()).unwrap();
- assert_eq!(lock["files"], lock2["files"], "lock file hashes must be deterministic");
+ )
+ .unwrap();
+ let lock2: serde_json::Value = serde_json::from_str(
+ &std::fs::read_to_string(artifact2.root.join("toolchain.lock")).unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ lock["files"], lock2["files"],
+ "lock file hashes must be deterministic"
+ );
unsafe { std::env::remove_var("NYX_REPRO_BASE") };
}
@@ -162,7 +169,8 @@ mod repro_hermetic_tests {
b"payload",
"label",
None,
- ).unwrap();
+ )
+ .unwrap();
// Simulate "no language toolchain installed" by stripping PATH
// down to /usr/bin (where `sh`, `grep`, `cat` live) before
@@ -188,15 +196,14 @@ mod repro_hermetic_tests {
// running the (broken) harness. Detect that and skip — Phase
// 28 acceptance is about the refusal path, not the host-has-it
// path.
- let host_has_python =
- std::process::Command::new("sh")
- .arg("-c")
- .arg("command -v python3")
- .env_clear()
- .env("PATH", &minimal_path)
- .output()
- .map(|o| o.status.success())
- .unwrap_or(false);
+ let host_has_python = std::process::Command::new("sh")
+ .arg("-c")
+ .arg("command -v python3")
+ .env_clear()
+ .env("PATH", &minimal_path)
+ .output()
+ .map(|o| o.status.success())
+ .unwrap_or(false);
if host_has_python {
eprintln!("skip: host has python3 in minimal PATH; cannot simulate clean CI image");
return;
@@ -234,14 +241,16 @@ mod repro_hermetic_tests {
std::fs::write(
bundle.join("reproduce.sh"),
"#!/bin/sh\necho 'host toolchain missing' >&2\nexit 3\n",
- ).unwrap();
+ )
+ .unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(
bundle.join("reproduce.sh"),
std::fs::Permissions::from_mode(0o755),
- ).unwrap();
+ )
+ .unwrap();
}
assert_eq!(replay_bundle(&bundle, &[]), ReplayResult::ToolchainMismatch);
}
@@ -254,14 +263,16 @@ mod repro_hermetic_tests {
std::fs::write(
bundle.join("reproduce.sh"),
"#!/bin/sh\necho 'PASS: simulated green'\nexit 0\n",
- ).unwrap();
+ )
+ .unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(
bundle.join("reproduce.sh"),
std::fs::Permissions::from_mode(0o755),
- ).unwrap();
+ )
+ .unwrap();
}
assert_eq!(replay_bundle(&bundle, &[]), ReplayResult::Pass);
}
@@ -284,11 +295,15 @@ mod repro_hermetic_tests {
&SandboxOptions::default(),
&make_outcome(),
&make_verdict(),
- "# harness", "# entry", b"payload", "label", None,
- ).unwrap();
+ "# harness",
+ "# entry",
+ b"payload",
+ "label",
+ None,
+ )
+ .unwrap();
- let pinned =
- nyx_scanner::dynamic::toolchain::pinned_image_ref(&spec.toolchain_id);
+ let pinned = nyx_scanner::dynamic::toolchain::pinned_image_ref(&spec.toolchain_id);
if pinned.is_some() {
assert!(
artifact.root.join("docker_pull.sh").exists(),
diff --git a/tests/ruby_fixtures.rs b/tests/ruby_fixtures.rs
index 93c94a43..18a0dcdb 100644
--- a/tests/ruby_fixtures.rs
+++ b/tests/ruby_fixtures.rs
@@ -13,7 +13,7 @@ mod common;
#[cfg(feature = "dynamic")]
mod phase15_shape_tests {
- use crate::common::fixture_harness::{run_shape_fixture_lang_or_skip, Prerequisite};
+ use crate::common::fixture_harness::{Prerequisite, run_shape_fixture_lang_or_skip};
use nyx_scanner::dynamic::spec::PayloadSlot;
use nyx_scanner::evidence::{EntryKind, VerifyResult, VerifyStatus};
use nyx_scanner::labels::Cap;
@@ -77,8 +77,13 @@ mod phase15_shape_tests {
#[test]
fn sinatra_route_vuln_is_confirmed() {
let Some(r) = run(
- "sinatra_route", "vuln.rb", "run", Cap::CODE_EXEC, 7,
- EntryKind::HttpRoute, PayloadSlot::Param(0),
+ "sinatra_route",
+ "vuln.rb",
+ "run",
+ Cap::CODE_EXEC,
+ 7,
+ EntryKind::HttpRoute,
+ PayloadSlot::Param(0),
) else {
return;
};
@@ -88,8 +93,13 @@ mod phase15_shape_tests {
#[test]
fn sinatra_route_benign_not_confirmed() {
let Some(r) = run(
- "sinatra_route", "benign.rb", "run", Cap::CODE_EXEC, 10,
- EntryKind::HttpRoute, PayloadSlot::Param(0),
+ "sinatra_route",
+ "benign.rb",
+ "run",
+ Cap::CODE_EXEC,
+ 10,
+ EntryKind::HttpRoute,
+ PayloadSlot::Param(0),
) else {
return;
};
@@ -101,8 +111,13 @@ mod phase15_shape_tests {
#[test]
fn rails_action_vuln_is_confirmed() {
let Some(r) = run(
- "rails_action", "vuln.rb", "index", Cap::CODE_EXEC, 17,
- EntryKind::HttpRoute, PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
+ "rails_action",
+ "vuln.rb",
+ "index",
+ Cap::CODE_EXEC,
+ 17,
+ EntryKind::HttpRoute,
+ PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
) else {
return;
};
@@ -112,8 +127,13 @@ mod phase15_shape_tests {
#[test]
fn rails_action_benign_not_confirmed() {
let Some(r) = run(
- "rails_action", "benign.rb", "index", Cap::CODE_EXEC, 20,
- EntryKind::HttpRoute, PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
+ "rails_action",
+ "benign.rb",
+ "index",
+ Cap::CODE_EXEC,
+ 20,
+ EntryKind::HttpRoute,
+ PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
) else {
return;
};
@@ -125,8 +145,13 @@ mod phase15_shape_tests {
#[test]
fn rack_middleware_vuln_is_confirmed() {
let Some(r) = run(
- "rack_middleware", "vuln.rb", "call", Cap::CODE_EXEC, 9,
- EntryKind::HttpRoute, PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
+ "rack_middleware",
+ "vuln.rb",
+ "call",
+ Cap::CODE_EXEC,
+ 9,
+ EntryKind::HttpRoute,
+ PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
) else {
return;
};
@@ -136,8 +161,13 @@ mod phase15_shape_tests {
#[test]
fn rack_middleware_benign_not_confirmed() {
let Some(r) = run(
- "rack_middleware", "benign.rb", "call", Cap::CODE_EXEC, 11,
- EntryKind::HttpRoute, PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
+ "rack_middleware",
+ "benign.rb",
+ "call",
+ Cap::CODE_EXEC,
+ 11,
+ EntryKind::HttpRoute,
+ PayloadSlot::EnvVar("NYX_PAYLOAD".into()),
) else {
return;
};
@@ -149,8 +179,13 @@ mod phase15_shape_tests {
#[test]
fn controller_method_vuln_is_confirmed() {
let Some(r) = run(
- "controller_method", "vuln.rb", "authenticate", Cap::CODE_EXEC, 7,
- EntryKind::Function, PayloadSlot::Param(0),
+ "controller_method",
+ "vuln.rb",
+ "authenticate",
+ Cap::CODE_EXEC,
+ 7,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
) else {
return;
};
@@ -160,8 +195,13 @@ mod phase15_shape_tests {
#[test]
fn controller_method_benign_not_confirmed() {
let Some(r) = run(
- "controller_method", "benign.rb", "authenticate", Cap::CODE_EXEC, 10,
- EntryKind::Function, PayloadSlot::Param(0),
+ "controller_method",
+ "benign.rb",
+ "authenticate",
+ Cap::CODE_EXEC,
+ 10,
+ EntryKind::Function,
+ PayloadSlot::Param(0),
) else {
return;
};
diff --git a/tests/ruby_frameworks_corpus.rs b/tests/ruby_frameworks_corpus.rs
index 01b51c31..f8c7de19 100644
--- a/tests/ruby_frameworks_corpus.rs
+++ b/tests/ruby_frameworks_corpus.rs
@@ -11,7 +11,7 @@
#![cfg(feature = "dynamic")]
-use nyx_scanner::dynamic::framework::{detect_binding, HttpMethod, ParamSource};
+use nyx_scanner::dynamic::framework::{HttpMethod, ParamSource, detect_binding};
use nyx_scanner::evidence::EntryKind;
use nyx_scanner::summary::FuncSummary;
use nyx_scanner::symbol::Lang;
@@ -155,8 +155,8 @@ fn sinatra_does_not_fire_on_rails_controller() {
let bytes = std::fs::read(path).expect("rails vuln fixture exists");
let tree = parse_ruby(&bytes);
let summary = summary_for("index", path);
- let binding = detect_binding(&summary, tree.root_node(), &bytes, Lang::Ruby)
- .expect("adapter binds");
+ let binding =
+ detect_binding(&summary, tree.root_node(), &bytes, Lang::Ruby).expect("adapter binds");
// First-match-wins ordering must produce `ruby-rails`, not
// `ruby-sinatra`, even if both adapters could in theory match.
assert_eq!(binding.adapter, "ruby-rails");
diff --git a/tests/rust_fixtures.rs b/tests/rust_fixtures.rs
index 7e39de51..1637a3c4 100644
--- a/tests/rust_fixtures.rs
+++ b/tests/rust_fixtures.rs
@@ -12,18 +12,21 @@ mod common;
#[cfg(feature = "dynamic")]
mod rust_fixture_tests {
use crate::common::fixture_harness::{
- run_fixture_and_compare_to_golden, CopyStrategy, FixtureSpec, Prerequisite,
+ CopyStrategy, FixtureSpec, Prerequisite, run_fixture_and_compare_to_golden,
};
use nyx_scanner::commands::scan::Diag;
- use nyx_scanner::dynamic::verify::{verify_finding, VerifyOptions};
- use nyx_scanner::evidence::{
- Confidence, Evidence, FlowStep, FlowStepKind,
- };
+ use nyx_scanner::dynamic::verify::{VerifyOptions, verify_finding};
+ use nyx_scanner::evidence::{Confidence, Evidence, FlowStep, FlowStepKind};
use nyx_scanner::labels::Cap;
use nyx_scanner::patterns::{FindingCategory, Severity};
use std::path::{Path, PathBuf};
- fn spec(fixture: &'static str, func: &'static str, cap: Cap, sink_line: u32) -> FixtureSpec<'static> {
+ fn spec(
+ fixture: &'static str,
+ func: &'static str,
+ cap: Cap,
+ sink_line: u32,
+ ) -> FixtureSpec<'static> {
FixtureSpec {
lang_dir: "rust",
fixture,
@@ -290,7 +293,7 @@ mod rust_fixture_tests {
#[cfg(feature = "dynamic")]
mod phase16_shape_tests {
- use crate::common::fixture_harness::{run_shape_fixture_lang_or_skip, Prerequisite};
+ use crate::common::fixture_harness::{Prerequisite, run_shape_fixture_lang_or_skip};
use nyx_scanner::dynamic::spec::PayloadSlot;
use nyx_scanner::evidence::{EntryKind, VerifyResult, VerifyStatus};
use nyx_scanner::labels::Cap;
@@ -357,8 +360,13 @@ mod phase16_shape_tests {
#[test]
fn actix_route_vuln_is_confirmed() {
let Some(r) = run(
- "actix_route", "vuln.rs", "handler", Cap::CODE_EXEC, 16,
- EntryKind::HttpRoute, PayloadSlot::Param(0),
+ "actix_route",
+ "vuln.rs",
+ "handler",
+ Cap::CODE_EXEC,
+ 16,
+ EntryKind::HttpRoute,
+ PayloadSlot::Param(0),
) else {
return;
};
@@ -368,8 +376,13 @@ mod phase16_shape_tests {
#[test]
fn actix_route_benign_not_confirmed() {
let Some(r) = run(
- "actix_route", "benign.rs", "handler", Cap::CODE_EXEC, 14,
- EntryKind::HttpRoute, PayloadSlot::Param(0),
+ "actix_route",
+ "benign.rs",
+ "handler",
+ Cap::CODE_EXEC,
+ 14,
+ EntryKind::HttpRoute,
+ PayloadSlot::Param(0),
) else {
return;
};
@@ -381,8 +394,13 @@ mod phase16_shape_tests {
#[test]
fn axum_handler_vuln_is_confirmed() {
let Some(r) = run(
- "axum_handler", "vuln.rs", "handler", Cap::CODE_EXEC, 15,
- EntryKind::HttpRoute, PayloadSlot::Param(0),
+ "axum_handler",
+ "vuln.rs",
+ "handler",
+ Cap::CODE_EXEC,
+ 15,
+ EntryKind::HttpRoute,
+ PayloadSlot::Param(0),
) else {
return;
};
@@ -392,8 +410,13 @@ mod phase16_shape_tests {
#[test]
fn axum_handler_benign_not_confirmed() {
let Some(r) = run(
- "axum_handler", "benign.rs", "handler", Cap::CODE_EXEC, 13,
- EntryKind::HttpRoute, PayloadSlot::Param(0),
+ "axum_handler",
+ "benign.rs",
+ "handler",
+ Cap::CODE_EXEC,
+ 13,
+ EntryKind::HttpRoute,
+ PayloadSlot::Param(0),
) else {
return;
};
@@ -405,8 +428,13 @@ mod phase16_shape_tests {
#[test]
fn clap_cli_vuln_is_confirmed() {
let Some(r) = run(
- "clap_cli", "vuln.rs", "run", Cap::CODE_EXEC, 17,
- EntryKind::CliSubcommand, PayloadSlot::Argv(0),
+ "clap_cli",
+ "vuln.rs",
+ "run",
+ Cap::CODE_EXEC,
+ 17,
+ EntryKind::CliSubcommand,
+ PayloadSlot::Argv(0),
) else {
return;
};
@@ -416,8 +444,13 @@ mod phase16_shape_tests {
#[test]
fn clap_cli_benign_not_confirmed() {
let Some(r) = run(
- "clap_cli", "benign.rs", "run", Cap::CODE_EXEC, 13,
- EntryKind::CliSubcommand, PayloadSlot::Argv(0),
+ "clap_cli",
+ "benign.rs",
+ "run",
+ Cap::CODE_EXEC,
+ 13,
+ EntryKind::CliSubcommand,
+ PayloadSlot::Argv(0),
) else {
return;
};
@@ -429,8 +462,13 @@ mod phase16_shape_tests {
#[test]
fn libfuzzer_target_vuln_is_confirmed() {
let Some(r) = run(
- "libfuzzer_target", "vuln.rs", "fuzz_target", Cap::CODE_EXEC, 15,
- EntryKind::LibraryApi, PayloadSlot::Param(0),
+ "libfuzzer_target",
+ "vuln.rs",
+ "fuzz_target",
+ Cap::CODE_EXEC,
+ 15,
+ EntryKind::LibraryApi,
+ PayloadSlot::Param(0),
) else {
return;
};
@@ -440,8 +478,13 @@ mod phase16_shape_tests {
#[test]
fn libfuzzer_target_benign_not_confirmed() {
let Some(r) = run(
- "libfuzzer_target", "benign.rs", "fuzz_target", Cap::CODE_EXEC, 13,
- EntryKind::LibraryApi, PayloadSlot::Param(0),
+ "libfuzzer_target",
+ "benign.rs",
+ "fuzz_target",
+ Cap::CODE_EXEC,
+ 13,
+ EntryKind::LibraryApi,
+ PayloadSlot::Param(0),
) else {
return;
};
diff --git a/tests/rust_frameworks_corpus.rs b/tests/rust_frameworks_corpus.rs
index d6eab037..a62900fb 100644
--- a/tests/rust_frameworks_corpus.rs
+++ b/tests/rust_frameworks_corpus.rs
@@ -11,7 +11,7 @@
#![cfg(feature = "dynamic")]
-use nyx_scanner::dynamic::framework::{detect_binding, HttpMethod};
+use nyx_scanner::dynamic::framework::{HttpMethod, detect_binding};
use nyx_scanner::evidence::EntryKind;
use nyx_scanner::summary::FuncSummary;
use nyx_scanner::symbol::Lang;
diff --git a/tests/sandbox_docker.rs b/tests/sandbox_docker.rs
index 18dfe1a9..343dfe85 100644
--- a/tests/sandbox_docker.rs
+++ b/tests/sandbox_docker.rs
@@ -16,8 +16,8 @@
use nyx_scanner::dynamic::harness::BuiltHarness;
use nyx_scanner::dynamic::sandbox::docker::{
- ensure_image_pulled, image_reference_for_toolchain, network_args, stub_mount_args,
- toolchain_is_pinned, workdir_mount_args, STUB_MOUNT_ROOT, WORK_MOUNT_PATH,
+ STUB_MOUNT_ROOT, WORK_MOUNT_PATH, ensure_image_pulled, image_reference_for_toolchain,
+ network_args, stub_mount_args, toolchain_is_pinned, workdir_mount_args,
};
use nyx_scanner::dynamic::sandbox::{
self, HostPort, NetworkPolicy, SandboxBackend, SandboxOptions,
@@ -87,12 +87,20 @@ fn stub_mount_args_uses_indexed_fixed_paths() {
#[test]
fn network_args_translate_every_policy() {
- assert!(network_args(&NetworkPolicy::None).iter().any(|a| a == "none"));
+ assert!(
+ network_args(&NetworkPolicy::None)
+ .iter()
+ .any(|a| a == "none")
+ );
let stubs = NetworkPolicy::StubsOnly {
allow: vec![HostPort::new("sql", 5432)],
};
let stubs_args = network_args(&stubs);
- assert!(stubs_args.iter().any(|a| a == "--add-host=sql:host-gateway"));
+ assert!(
+ stubs_args
+ .iter()
+ .any(|a| a == "--add-host=sql:host-gateway")
+ );
let open = network_args(&NetworkPolicy::Open);
assert!(open.iter().any(|a| a == "bridge"));
assert!(!open.iter().any(|a| a.starts_with("--add-host=")));
@@ -117,9 +125,15 @@ fn toolchain_pinning_state_is_observable() {
let pinned = toolchain_is_pinned("python-3.11");
let r = image_reference_for_toolchain("python-3.11").unwrap();
if pinned {
- assert!(r.contains("@sha256:"), "pinned ref must carry digest, got {r}");
+ assert!(
+ r.contains("@sha256:"),
+ "pinned ref must carry digest, got {r}"
+ );
} else {
- assert!(!r.contains("@sha256:"), "unpinned ref must not carry digest, got {r}");
+ assert!(
+ !r.contains("@sha256:"),
+ "unpinned ref must not carry digest, got {r}"
+ );
}
}
@@ -131,8 +145,8 @@ fn ensure_image_pulled_returns_true_for_python_slim() {
eprintln!("docker unavailable — skipping");
return;
}
- let r = image_reference_for_toolchain("python-3.11")
- .expect("python-3.11 must be in the catalogue");
+ let r =
+ image_reference_for_toolchain("python-3.11").expect("python-3.11 must be in the catalogue");
assert!(
ensure_image_pulled(r),
"ensure_image_pulled must succeed for `{r}` when docker is available",
@@ -170,8 +184,7 @@ fn harness_workdir_is_mounted_at_fixed_work_path() {
return;
}
let tmp = tempfile::TempDir::new().expect("tempdir");
- std::fs::write(tmp.path().join("token.txt"), "phase-19-mount-token\n")
- .expect("write fixture");
+ std::fs::write(tmp.path().join("token.txt"), "phase-19-mount-token\n").expect("write fixture");
write_harness_script(
tmp.path(),
// Read from the fixed /work mount path — this passes only when the
diff --git a/tests/sandbox_escape_suite.rs b/tests/sandbox_escape_suite.rs
index 76dff77e..f9430238 100644
--- a/tests/sandbox_escape_suite.rs
+++ b/tests/sandbox_escape_suite.rs
@@ -155,7 +155,10 @@ mod escape_suite {
unsafe { std::env::set_var(format!("NYX_ESCAPE_DYN_{technique}_{variant}"), "1") };
}
- builds().lock().unwrap().insert(key.clone(), Some(out_bin.clone()));
+ builds()
+ .lock()
+ .unwrap()
+ .insert(key.clone(), Some(out_bin.clone()));
Some(out_bin)
}
@@ -291,34 +294,58 @@ mod escape_suite {
// keep the build dependency-free.
#[test]
- fn chmod_4755_benign() { let _ = assert_contained("chmod_4755", "benign"); }
+ fn chmod_4755_benign() {
+ let _ = assert_contained("chmod_4755", "benign");
+ }
#[test]
- fn chmod_4755_vuln() { let _ = assert_contained("chmod_4755", "vuln"); }
+ fn chmod_4755_vuln() {
+ let _ = assert_contained("chmod_4755", "vuln");
+ }
#[test]
- fn etc_write_benign() { let _ = assert_contained("etc_write", "benign"); }
+ fn etc_write_benign() {
+ let _ = assert_contained("etc_write", "benign");
+ }
#[test]
- fn etc_write_vuln() { let _ = assert_contained("etc_write", "vuln"); }
+ fn etc_write_vuln() {
+ let _ = assert_contained("etc_write", "vuln");
+ }
#[test]
- fn dlopen_outside_chroot_benign() { let _ = assert_contained("dlopen_outside_chroot", "benign"); }
+ fn dlopen_outside_chroot_benign() {
+ let _ = assert_contained("dlopen_outside_chroot", "benign");
+ }
#[test]
- fn dlopen_outside_chroot_vuln() { let _ = assert_contained("dlopen_outside_chroot", "vuln"); }
+ fn dlopen_outside_chroot_vuln() {
+ let _ = assert_contained("dlopen_outside_chroot", "vuln");
+ }
#[test]
- fn proc_root_passwd_benign() { let _ = assert_contained("proc_root_passwd", "benign"); }
+ fn proc_root_passwd_benign() {
+ let _ = assert_contained("proc_root_passwd", "benign");
+ }
#[test]
- fn proc_root_passwd_vuln() { let _ = assert_contained("proc_root_passwd", "vuln"); }
+ fn proc_root_passwd_vuln() {
+ let _ = assert_contained("proc_root_passwd", "vuln");
+ }
#[test]
- fn raw_socket_bind_benign() { let _ = assert_contained("raw_socket_bind", "benign"); }
+ fn raw_socket_bind_benign() {
+ let _ = assert_contained("raw_socket_bind", "benign");
+ }
#[test]
- fn raw_socket_bind_vuln() { let _ = assert_contained("raw_socket_bind", "vuln"); }
+ fn raw_socket_bind_vuln() {
+ let _ = assert_contained("raw_socket_bind", "vuln");
+ }
#[test]
- fn setuid_zero_benign() { let _ = assert_contained("setuid_zero", "benign"); }
+ fn setuid_zero_benign() {
+ let _ = assert_contained("setuid_zero", "benign");
+ }
#[test]
- fn setuid_zero_vuln() { let _ = assert_contained("setuid_zero", "vuln"); }
+ fn setuid_zero_vuln() {
+ let _ = assert_contained("setuid_zero", "vuln");
+ }
// ── Track-B regression tripwire ──────────────────────────────────────────
diff --git a/tests/sandbox_hardening_linux.rs b/tests/sandbox_hardening_linux.rs
index 0998cc47..f06f2d0a 100644
--- a/tests/sandbox_hardening_linux.rs
+++ b/tests/sandbox_hardening_linux.rs
@@ -27,9 +27,9 @@ mod hardening_tests {
self, HardeningRecord, ProcessHardeningProfile, SandboxBackend, SandboxOptions,
};
- fn linux_outcome(out: &sandbox::SandboxOutcome)
- -> Option
- {
+ fn linux_outcome(
+ out: &sandbox::SandboxOutcome,
+ ) -> Option {
match out.hardening_outcome.as_ref()? {
HardeningRecord::Linux(o) => Some(*o),
#[allow(unreachable_patterns)]
@@ -43,9 +43,7 @@ mod hardening_tests {
static PROBE_BINARY: OnceLock