Skip to content

Commit 897159a

Browse files
feat(bridge): cohort overrides for vendored-pin (#74) + Dioxus/GTK transitive (#75) (#78)
## Summary Two layered refinements on top of #76 (phantom-declared / phantom-transitive split). Same three-way Mitigable/Unmitigable/Informational output, but the Informational tier now produces an accurate \`action\` field for two cohorts where the generic message was misleading. Closes #74 in part (build-script-only / vendored-pin name-list portion). Closes #75. ## Cohort E-3 — build-script-only / vendored-pin (#74, partial) A naive \`cargo machete --fix\` strip of certain phantom-declared crates breaks the build inscrutably (cross-compile TLS, native-lib resolution, build-time codegen). New \`is_build_script_only_or_vendored_pin(name)\` predicate covers crates that **have no \`use\` site by design**: - Build-script side-effect crates: \`pkg-config\`, \`cc\`, \`bindgen\`, \`cmake\`, \`autocfg\`, \`vcpkg\`, \`winres\`, \`embed-resource\` - Canonical vendored-pin: \`openssl-src\` When a phantom-declared crate matches, the action flips from \"Strip from Cargo.toml\" to \"DO NOT STRIP — load-bearing via build.rs side-effects or native-lib linkage\". **Future follow-up**: feature-based detection (e.g. \`openssl-sys = { features = [\"vendored\"] }\`) needs feature-set plumbing through \`ReachabilityEvidence\` — left out of scope. ## Cohort E-2 — Dioxus/GTK transitive (#75) Phantom-transitive advisories where the parent is in the Dioxus desktop family (\`wry\`, \`dioxus-desktop\`, \`dioxus\`) and the affected crate is in the GTK/webkit family now get a Cohort E-2 message naming the no-local-fix path (wait for parent release, or swap the desktop renderer). Sub-rule covers \`printpdf\`→\`kuchiki\`. GTK/webkit family matched: \`atk*\`, \`gdk*\`, \`gtk*\`, \`glib\`, \`glib-sys\`, \`gio\`, \`gio-sys\`, \`gobject-sys\`, \`gtk3-macros\`, \`proc-macro-error\`, \`paste\`, \`fxhash\`, \`webkit2gtk\`, \`webkit2gtk-sys\`. ## Bonus repair: src/assail/analyzer.rs test-module corruption The squash-merge sequence of PRs #71 (Julia) → #77 (refile of #72 vendored-snapshot) → #73 (flake.lock) left \`src/assail/analyzer.rs\` with an unclosed-delimiter at line 7962: - \`count_julia_dce\` had \`flake_findings\` body - \`julia_ext_jl_dce_is_exempt\` was missing closing braces - Two flake tests (\`flake_without_lock_is_low_severity\`, \`flake_with_narhash_has_no_finding\`) had landed inside the Julia section \`cargo test --lib\` was failing to compile on main as a result. This PR reassembles each section in its intended location; no test logic changed. ## Test plan - [x] \`cargo test --features http --lib bridge::classify::\` — 14/14 pass (5 new + 9 existing) - [x] \`cargo test --features http --lib\` — 343 lib tests pass (was previously failing to compile) - [x] \`cargo check --features http\` — green ## Changes - \`src/bridge/classify.rs\`: +268 / -29 lines (3 predicate fns + 2 cohort override branches + 5 regression tests) - \`src/assail/analyzer.rs\`: +/-109 lines, net wash (reassemble corrupted test sections) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5266c4b commit 897159a

2 files changed

Lines changed: 317 additions & 60 deletions

File tree

src/assail/analyzer.rs

Lines changed: 63 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7794,11 +7794,6 @@ pub fn safe_get_x() -> Option<String> {
77947794
// ---------------------------------------------------------------
77957795

77967796
fn count_julia_dce(content: &str, file_path: &str) -> usize {
7797-
// flake.nix SupplyChain severity (downgrade to Low when fix is
7798-
// trivially mechanical — generate flake.lock).
7799-
// ---------------------------------------------------------------
7800-
7801-
fn flake_findings(content: &str, file_path: &str) -> Vec<WeakPoint> {
78027797
let analyzer = Analyzer::new(std::path::Path::new(".")).expect("analyzer construction");
78037798
let mut stats = ProgramStatistics::default();
78047799
let mut wp = Vec::new();
@@ -7817,47 +7812,6 @@ pub fn safe_get_x() -> Option<String> {
78177812
count_julia_dce(src, "FooExt.jl"),
78187813
0,
78197814
"*Ext.jl files use eval/Meta.parse idiomatically — must be exempt"
7820-
.analyze_config(content, &mut stats, &mut wp, file_path)
7821-
.expect("analyze_config");
7822-
wp.into_iter()
7823-
.filter(|w| matches!(w.category, WeakPointCategory::SupplyChain))
7824-
.collect()
7825-
}
7826-
7827-
#[test]
7828-
fn flake_without_lock_is_low_severity() {
7829-
let src = r#"{
7830-
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
7831-
outputs = { self, nixpkgs }: { };
7832-
}"#;
7833-
// Use a path that does NOT have a sibling flake.lock in the working dir.
7834-
let findings = flake_findings(src, "/nonexistent/dir/flake.nix");
7835-
assert_eq!(findings.len(), 1, "unpinned flake.nix must produce one finding");
7836-
assert!(
7837-
matches!(findings[0].severity, Severity::Low),
7838-
"missing flake.lock alone is mechanically fixable — must be Low severity, got {:?}",
7839-
findings[0].severity
7840-
);
7841-
assert!(
7842-
findings[0].description.contains("nix flake update"),
7843-
"description must point at the fix command"
7844-
);
7845-
}
7846-
7847-
#[test]
7848-
fn flake_with_narhash_has_no_finding() {
7849-
let src = r#"{
7850-
inputs.nixpkgs = {
7851-
url = "github:NixOS/nixpkgs/nixos-unstable";
7852-
narHash = "sha256-...";
7853-
};
7854-
outputs = { self, nixpkgs }: { };
7855-
}"#;
7856-
let findings = flake_findings(src, "/nonexistent/dir/flake.nix");
7857-
assert_eq!(
7858-
findings.len(),
7859-
0,
7860-
"flake.nix with inline narHash must NOT produce a SupplyChain finding"
78617815
);
78627816
}
78637817

@@ -7880,6 +7834,10 @@ pub fn safe_get_x() -> Option<String> {
78807834
assert!(
78817835
count_julia_dce(src, "src/dangerous.jl") > 0,
78827836
"non-extension Julia files must still flag eval()"
7837+
);
7838+
}
7839+
7840+
// ---------------------------------------------------------------
78837841
// Vendored-snapshot directory skip
78847842
// ---------------------------------------------------------------
78857843

@@ -7944,6 +7902,65 @@ pub fn safe_get_x() -> Option<String> {
79447902
.iter()
79457903
.any(|p| p.to_string_lossy().contains("rescript-ecosystem")),
79467904
"rescript-ecosystem vendored snapshot must be skipped"
7905+
);
7906+
}
7907+
7908+
// ---------------------------------------------------------------
7909+
// flake.nix SupplyChain severity (downgrade to Low when fix is
7910+
// trivially mechanical — generate flake.lock). Restored 2026-05-27
7911+
// after the squash-merge of #77 (refile of #72) collided with #73
7912+
// and silently dropped the helper + first two tests.
7913+
// ---------------------------------------------------------------
7914+
7915+
fn flake_findings(content: &str, file_path: &str) -> Vec<WeakPoint> {
7916+
let analyzer = Analyzer::new(std::path::Path::new(".")).expect("analyzer construction");
7917+
let mut stats = ProgramStatistics::default();
7918+
let mut wp = Vec::new();
7919+
analyzer
7920+
.analyze_config(content, &mut stats, &mut wp, file_path)
7921+
.expect("analyze_config");
7922+
wp.into_iter()
7923+
.filter(|w| matches!(w.category, WeakPointCategory::SupplyChain))
7924+
.collect()
7925+
}
7926+
7927+
#[test]
7928+
fn flake_without_lock_is_low_severity() {
7929+
let src = r#"{
7930+
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
7931+
outputs = { self, nixpkgs }: { };
7932+
}"#;
7933+
let findings = flake_findings(src, "/nonexistent/dir/flake.nix");
7934+
assert_eq!(findings.len(), 1, "unpinned flake.nix must produce one finding");
7935+
assert!(
7936+
matches!(findings[0].severity, Severity::Low),
7937+
"missing flake.lock alone is mechanically fixable — must be Low severity, got {:?}",
7938+
findings[0].severity
7939+
);
7940+
assert!(
7941+
findings[0].description.contains("nix flake update"),
7942+
"description must point at the fix command"
7943+
);
7944+
}
7945+
7946+
#[test]
7947+
fn flake_with_narhash_has_no_finding() {
7948+
let src = r#"{
7949+
inputs.nixpkgs = {
7950+
url = "github:NixOS/nixpkgs/nixos-unstable";
7951+
narHash = "sha256-...";
7952+
};
7953+
outputs = { self, nixpkgs }: { };
7954+
}"#;
7955+
let findings = flake_findings(src, "/nonexistent/dir/flake.nix");
7956+
assert_eq!(
7957+
findings.len(),
7958+
0,
7959+
"flake.nix with inline narHash must NOT produce a SupplyChain finding"
7960+
);
7961+
}
7962+
7963+
#[test]
79477964
fn flake_with_rev_pins_has_no_finding() {
79487965
let src = r#"{
79497966
inputs.nixpkgs = {

0 commit comments

Comments
 (0)