use super::*;
use crate::test_support::*;
use allow_core::FindingKind;
use std::path::Path;
#[test]
fn migrates_process_allowlist_to_policy_exception_entries() {
let policy = process_policy_fixture_path();
let cfg = load_legacy_or_canonical(&policy)
.unwrap_or_else(|err| std::panic::panic_any(format!("process policy migrates: {err}")));
assert_eq!(cfg.policy, "cargo-allow");
assert_eq!(cfg.allow.len(), 2);
let install = cfg
.allow
.iter()
.find(|entry| entry.id == "proc-cargo-install-cargo-deny")
.unwrap_or_else(|| std::panic::panic_any("expected cargo install process entry"));
assert_eq!(install.kind, FindingKind::PolicyException);
assert_eq!(install.family.as_deref(), Some("process_spawn"));
assert_eq!(install.classification, "network_process");
assert_eq!(
install.path.as_deref(),
Some(Path::new(".github/workflows/ci.yml"))
);
assert_eq!(install.selector.ast_kind.as_deref(), Some("process_spawn"));
assert_eq!(
install.selector.symbol.as_deref(),
Some("cargo install cargo-deny --locked")
);
assert_eq!(
install.selector.target_fingerprint.as_deref(),
Some("process:cargo install cargo-deny --locked")
);
assert_eq!(
install.lifecycle.review_after.as_deref(),
Some("2026-09-09")
);
assert!(
install
.evidence
.iter()
.any(|item| item == "legacy-policy:proc-cargo-install-cargo-deny")
);
assert!(
install
.evidence
.iter()
.any(|item| item == "network_reach:true")
);
let local = cfg
.allow
.iter()
.find(|entry| entry.id == "proc-bash-package-proof")
.unwrap_or_else(|| std::panic::panic_any("expected package proof process entry"));
assert_eq!(local.classification, "local_process");
assert_eq!(local.lifecycle.expires.as_deref(), Some("never"));
assert_eq!(local.lifecycle.review_after.as_deref(), Some("2026-05-09"));
}
#[test]
fn process_compat_synthesizes_matched_new_and_stale_drift() {
let policy = process_policy_fixture_path();
let cfg = load_process_compat_config(&policy)
.unwrap_or_else(|err| std::panic::panic_any(format!("process compat config loads: {err}")));
let findings = process_findings_from_config(&cfg);
let matched = allow_match::evaluate(&cfg, &findings, allow_match::CheckMode::NoNew);
assert_eq!(
matched
.iter()
.filter(|outcome| {
outcome.finding_index.is_some()
&& matches!(
outcome.status,
allow_core::MatchStatus::Matched | allow_core::MatchStatus::ReviewDue
)
})
.count(),
2
);
let missing_allow = allow_match::evaluate(
&cfg,
&[process_policy_finding(
".github/workflows/release.yml",
"bash scripts/publish.sh",
)],
allow_match::CheckMode::NoNew,
);
assert!(
missing_allow
.iter()
.any(|outcome| outcome.status == allow_core::MatchStatus::New)
);
let stale_allow = allow_match::evaluate(&cfg, &[], allow_match::CheckMode::Audit);
assert!(stale_allow.iter().any(|outcome| {
outcome.finding_index.is_none()
&& matches!(
outcome.status,
allow_core::MatchStatus::Stale | allow_core::MatchStatus::ReviewDue
)
}));
}
#[test]
fn process_policy_requires_legacy_xtask_fields() {
let policy = malformed_process_policy_fixture_path();
let err = load_process_compat_config(&policy)
.expect_err("process policy without network_reach should fail");
assert!(
err.to_string()
.contains("proc-missing missing network_reach")
);
}
#[test]
fn process_migration_preserves_legacy_evidence_when_present() {
let path = fixture_dir().join("process-allowlist.toml");
std::fs::write(
&path,
r#"schema_version = 1
policy = "process-allowlist"
owner = "EffortlessMetrics"
status = "advisory"
[[allow]]
id = "proc-release-script"
binary = "bash"
argv_shape = ["scripts/release.sh"]
network_reach = false
called_by = [".github/workflows/release.yml"]
owner = "release"
reason = "Release helper fixture."
evidence = ["doc:docs/release/README.md", "issue:#123"]
created = "2026-05-09"
expires = "permanent"
"#,
)
.unwrap_or_else(|err| std::panic::panic_any(format!("fixture write: {err}")));
let cfg = load_legacy_or_canonical(&path).unwrap_or_else(|err| {
std::panic::panic_any(format!("process policy with evidence migrates: {err}"))
});
let entry = cfg
.allow
.first()
.unwrap_or_else(|| std::panic::panic_any("expected process allow entry"));
assert!(
entry
.evidence
.iter()
.any(|item| item == "doc:docs/release/README.md")
);
assert!(entry.evidence.iter().any(|item| item == "issue:#123"));
assert!(
entry
.evidence
.iter()
.any(|item| item == "legacy-policy:proc-release-script")
);
assert!(entry.evidence.iter().any(|item| item == "binary:bash"));
}
#[test]
fn migrates_network_allowlist_to_policy_exception_entries() {
let policy = network_policy_fixture_path();
let cfg = load_legacy_or_canonical(&policy)
.unwrap_or_else(|err| std::panic::panic_any(format!("network policy migrates: {err}")));
assert_eq!(cfg.policy, "cargo-allow");
assert_eq!(cfg.allow.len(), 2);
let public = cfg
.allow
.iter()
.find(|entry| entry.id == "net-crates-io-fetch")
.unwrap_or_else(|| std::panic::panic_any("expected crates.io network entry"));
assert_eq!(public.kind, FindingKind::PolicyException);
assert_eq!(public.family.as_deref(), Some("network_destination"));
assert_eq!(public.classification, "public_network");
assert_eq!(
public.path.as_deref(),
Some(Path::new("policy/network-allowlist.toml"))
);
assert_eq!(
public.selector.ast_kind.as_deref(),
Some("network_destination")
);
assert_eq!(
public.selector.symbol.as_deref(),
Some("crates.io lane build")
);
assert_eq!(
public.selector.target_fingerprint.as_deref(),
Some("network:crates.io:auth:false:lane:build")
);
assert_eq!(public.lifecycle.expires.as_deref(), Some("never"));
assert_eq!(public.lifecycle.review_after.as_deref(), Some("2026-05-09"));
assert!(
public
.evidence
.iter()
.any(|item| item == "legacy-policy:net-crates-io-fetch")
);
let authenticated = cfg
.allow
.iter()
.find(|entry| entry.id == "net-github-api")
.unwrap_or_else(|| std::panic::panic_any("expected GitHub API network entry"));
assert_eq!(authenticated.classification, "authenticated_network");
assert!(
authenticated
.evidence
.iter()
.any(|item| item == "auth_secret:GITHUB_TOKEN")
);
}
#[test]
fn network_migration_accepts_covered_by_as_legacy_evidence() {
let path = fixture_dir().join("network-allowlist.toml");
std::fs::write(
&path,
r#"schema_version = 1
policy = "network-allowlist"
owner = "EffortlessMetrics"
status = "advisory"
[[allow]]
id = "net-release-api"
destination = "api.github.com"
auth_required = true
auth_secret = "GITHUB_TOKEN"
lane = "release"
owner = "release/ci"
reason = "Release API fixture."
covered_by = "doc:docs/ci.md"
created = "2026-05-09"
expires = "permanent"
"#,
)
.unwrap_or_else(|err| std::panic::panic_any(format!("fixture write: {err}")));
let cfg = load_legacy_or_canonical(&path).unwrap_or_else(|err| {
std::panic::panic_any(format!("network policy with covered_by migrates: {err}"))
});
let entry = cfg
.allow
.first()
.unwrap_or_else(|| std::panic::panic_any("expected network allow entry"));
assert_eq!(
entry.evidence,
vec![
"doc:docs/ci.md".to_string(),
"legacy-policy:net-release-api".to_string(),
"destination:api.github.com".to_string(),
"lane:release".to_string(),
"auth_required:true".to_string(),
"auth_secret:GITHUB_TOKEN".to_string(),
]
);
}
#[test]
fn network_compat_synthesizes_matched_new_and_stale_drift() {
let policy = network_policy_fixture_path();
let cfg = load_network_compat_config(&policy)
.unwrap_or_else(|err| std::panic::panic_any(format!("network compat config loads: {err}")));
let findings = network_findings_from_config(&cfg);
let matched = allow_match::evaluate(&cfg, &findings, allow_match::CheckMode::NoNew);
assert_eq!(
matched
.iter()
.filter(|outcome| {
outcome.finding_index.is_some()
&& matches!(
outcome.status,
allow_core::MatchStatus::Matched | allow_core::MatchStatus::ReviewDue
)
})
.count(),
2
);
let missing_allow = allow_match::evaluate(
&cfg,
&[network_policy_finding("example.com lane test")],
allow_match::CheckMode::NoNew,
);
assert!(
missing_allow
.iter()
.any(|outcome| outcome.status == allow_core::MatchStatus::New)
);
let stale_allow = allow_match::evaluate(&cfg, &[], allow_match::CheckMode::Audit);
assert!(stale_allow.iter().any(|outcome| {
outcome.finding_index.is_none()
&& matches!(
outcome.status,
allow_core::MatchStatus::Stale | allow_core::MatchStatus::ReviewDue
)
}));
}
#[test]
fn network_policy_requires_legacy_xtask_fields() {
let policy = malformed_network_policy_fixture_path();
let err = load_network_compat_config(&policy)
.expect_err("network policy without auth_required should fail");
assert!(
err.to_string()
.contains("net-missing missing auth_required")
);
}