Skip to content

Commit 6dd9cf1

Browse files
hyperpolymathclaude
andcommitted
feat(bridge): cohort overrides for vendored-pin (#74) + Dioxus/GTK transitive (#75)
Bridge classifier still produces the three-way Mitigable/Unmitigable/ Informational output, but now distinguishes two further cases inside the Informational tier so the suggested action matches reality: PhantomDeclared cohort override (closes #74 in part): - New `is_build_script_only_or_vendored_pin(name)` predicate covers pkg-config, cc, bindgen, cmake, autocfg, vcpkg, winres, embed-resource, 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". Same Informational class, different recommendation. Stops `cargo machete --fix` from breaking cross-compile TLS / native-lib resolution. - Feature-based detection (e.g. openssl-sys with `vendored` feature) remains future work — it needs feature-set plumbing into evidence that the bridge doesn't have today. PhantomTransitive cohort override (closes #75): - New `is_dioxus_gui_parent(parent)` matches wry, dioxus-desktop, dioxus. - New `is_gtk_webkit_family(name)` matches the atk*/gdk*/gtk*/glib/ gio*/gobject-sys/gtk3-macros/proc-macro-error/paste/fxhash/ webkit2gtk* surface observed in presswerk. - When (parent is Dioxus GUI) AND (crate is GTK/webkit family), emit the Cohort E-2 message naming the no-local-fix path + tracker. - printpdf+kuchiki sub-rule covers the printpdf-internal HTML→PDF parser path. Five new regression tests in `bridge::classify::tests` (14 total in the module). Full lib suite: 343 passed. Also restores the test module corruption in src/assail/analyzer.rs: the squash-merge sequence for PRs #71 / #77 (refile of #72) / #73 left the file with an unclosed delimiter at line 7962 (count_julia_dce helper had flake_findings body, julia_ext_jl_dce_is_exempt was missing its closing braces, two flake tests landed inside the Julia section). Reassembles each section in its intended location; no test logic changed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5266c4b commit 6dd9cf1

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)