Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

xtask/tests/audit_follows_deny.rs

//! ★ **cargo-audit and cargo-deny judge advisories by ONE list** (lane HOL-NCSC, NCSC P3).
//!
//! The `[advisories] ignore` of `deny.toml` carries each exception with its reason; cargo-audit
//! does not read it, so `.cargo/audit.toml` repeats the ids. The two drifted (2026-10-30:
//! the security workflow ignored one id, deny.toml six). This guard holds them equal, keeps
//! the workflow free of a third list (`--ignore` on its command line), and holds that
//! `cargo deny check` runs in CI: a step of the full set of `cargo xtask ci` and a job of the
//! security workflow.

use std::collections::BTreeSet;
use std::path::PathBuf;

fn root() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .parent()
        .expect("xtask/..")
        .to_path_buf()
}

fn read(rel: &str) -> String {
    std::fs::read_to_string(root().join(rel)).unwrap_or_else(|e| panic!("{rel}: {e}"))
}

/// The ids of a toml's `[advisories] ignore`: plain strings or `{ id = .. }` tables.
fn ignored(rel: &str) -> BTreeSet<String> {
    let v: toml::Value = toml::from_str(&read(rel)).unwrap_or_else(|e| panic!("{rel}: {e}"));
    let list = v
        .get("advisories")
        .and_then(|a| a.get("ignore"))
        .and_then(|i| i.as_array())
        .cloned()
        .unwrap_or_default();
    list.iter()
        .map(|e| match e {
            toml::Value::String(s) => s.clone(),
            other => other
                .get("id")
                .and_then(|i| i.as_str())
                .unwrap_or_else(|| panic!("{rel}: an ignore entry without an id: {other}"))
                .to_owned(),
        })
        .collect()
}

/// The workflow without its comment lines.
fn workflow() -> String {
    read(".forgejo/workflows/security.yml")
        .lines()
        .filter(|l| !l.trim_start().starts_with('#'))
        .collect::<Vec<_>>()
        .join("\n")
}

#[test]
fn cargo_audit_ignores_exactly_what_deny_toml_ignores() {
    let deny = ignored("deny.toml");
    assert!(
        !deny.is_empty(),
        "deny.toml ignores nothing? the guard would be vacuous"
    );
    let audit = ignored(".cargo/audit.toml");
    assert_eq!(
        audit, deny,
        ".cargo/audit.toml and deny.toml ignore different advisories"
    );
    assert!(
        !workflow().contains("--ignore"),
        "the security workflow keeps a list of its own; cargo audit reads .cargo/audit.toml"
    );
}

#[test]
fn cargo_deny_check_runs_in_ci() {
    let wf = workflow();
    assert!(
        wf.contains("cargo deny --locked check"),
        "the security workflow does not run cargo deny --locked check"
    );
    let ci = read("xtask/src/ci.rs");
    assert!(
        ci.contains(r#"Step::host("deny", "cargo", &["deny", "--locked", "check"])"#),
        "cargo xtask ci has no deny step"
    );
}