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

mikronova/modgunn

tests/engine_ab_equivalence.rs

//! Engine A ≡ Engine B — randomized equivalence contract.
//!
//! The whole point of the A/B split is that the two matching strategies are
//! *interchangeable*: identical verdicts on identical inputs, differing only in cost.
//! This test proves that over a large pseudo-random corpus (no `rand` dep — a tiny
//! SplitMix64 keeps the lean build dependency-free and the corpus reproducible), and
//! also pins both against the shipped [`OsvScanner`] so the abstraction can never
//! silently drift from modgunn's original semantics.
#![cfg(feature = "scan")]

use modgunn::core::{ArtifactRef, Severity};
use modgunn::engine::{build, AdvisoryEngine, EngineKind, IndexedEngine, LinearEngine};
use modgunn::scan::{Advisory, AdvisoryDb, Artifact, OsvScanner, Policy, Provenance, ScanEngine};
use modgunn::versions::RangeEvent::{Fixed, Introduced};

/// Reproducible PRNG — SplitMix64. No external crate; deterministic corpus.
struct Rng(u64);
impl Rng {
    fn next(&mut self) -> u64 {
        self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
        let mut z = self.0;
        z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
        z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
        z ^ (z >> 31)
    }
    fn below(&mut self, n: u64) -> u64 {
        self.next() % n
    }
}

const ECOS: [&str; 4] = ["cargo", "npm", "pypi", "maven"];

fn gen_db(rng: &mut Rng, n_adv: usize, n_pkg: usize) -> AdvisoryDb {
    let sev = [
        Severity::Low,
        Severity::Medium,
        Severity::High,
        Severity::Critical,
    ];
    let mut advisories = Vec::with_capacity(n_adv);
    for i in 0..n_adv {
        let eco = ECOS[rng.below(ECOS.len() as u64) as usize];
        let pkg = format!("pkg{}", rng.below(n_pkg as u64));
        let severity = sev[rng.below(sev.len() as u64) as usize];
        let mut adv = match rng.below(3) {
            // exact-version advisory
            0 => {
                let v = format!("1.{}.0", rng.below(5));
                Advisory::basic(format!("ADV-{i}"), eco, &pkg, vec![v], severity, "exact")
            }
            // range advisory [1.x, 1.y)
            1 => {
                let lo = rng.below(3);
                let hi = lo + 1 + rng.below(3);
                Advisory::basic(format!("ADV-{i}"), eco, &pkg, vec![], severity, "range")
                    .with_ranges(vec![
                        Introduced(format!("1.{lo}.0")),
                        Fixed(format!("1.{hi}.0")),
                    ])
            }
            // all-versions advisory
            _ => Advisory::basic(format!("ADV-{i}"), eco, &pkg, vec![], severity, "all"),
        };
        // ~1 in 8 withdrawn (must never match either engine).
        if rng.below(8) == 0 {
            adv.withdrawn = true;
        }
        // ~1 in 16 KEV (escalation exercised identically by both).
        if rng.below(16) == 0 {
            adv.kev = true;
        }
        advisories.push(adv);
    }
    AdvisoryDb::new("db-rev-rand", advisories)
}

fn gen_artifacts(rng: &mut Rng, n: usize, n_pkg: usize) -> Vec<Artifact> {
    let mut out = Vec::with_capacity(n);
    for _ in 0..n {
        let eco = ECOS[rng.below(ECOS.len() as u64) as usize];
        let pkg = format!("pkg{}", rng.below((n_pkg + n_pkg / 4) as u64)); // some misses
        let version = format!("1.{}.{}", rng.below(6), rng.below(4));
        let license = match rng.below(4) {
            0 => None,
            1 => Some("GPL-3.0".to_string()),
            2 => Some("MIT".to_string()),
            _ => Some("Apache-2.0".to_string()),
        };
        let prov = if rng.below(2) == 0 {
            Provenance::signed_by("ci")
        } else {
            Provenance::unsigned()
        };
        out.push(Artifact::new(
            ArtifactRef {
                ecosystem: eco.into(),
                name: pkg.clone(),
                version,
                blob_sha256: format!("{pkg}-sha"),
            },
            license,
            prov,
        ));
    }
    out
}

fn policy() -> Policy {
    Policy {
        version: "rand-policy".into(),
        license: modgunn::scan::LicensePolicy {
            allow: vec!["MIT".into(), "Apache-2.0".into()],
            deny: vec!["GPL-3.0".into()],
            on_unknown: modgunn::core::Decision::Warn,
        },
        provenance: modgunn::scan::ProvenancePolicy::Warn,
        epss_block_threshold: Some(0.5),
        ..Default::default()
    }
}

#[test]
fn engines_agree_on_a_large_random_corpus() {
    let mut rng = Rng(0xDEAD_BEEF_CAFE_F00D);
    let db = gen_db(&mut rng, 4_000, 400);
    let artifacts = gen_artifacts(&mut rng, 20_000, 400);
    let policy = policy();

    let a = LinearEngine::new(&db);
    let b = IndexedEngine::new(&db);
    let scanner = OsvScanner::new(policy.clone(), db.clone());

    let mut hits = 0usize;
    for art in &artifacts {
        let va = a.verdict(&policy, art);
        let vb = b.verdict(&policy, art);
        assert_eq!(
            va, vb,
            "A vs B disagree on {} {}",
            art.artifact.name, art.artifact.version
        );
        // And both equal the shipped per-artifact scanner (Engine A's convenience API).
        assert_eq!(va, scanner.scan(art), "engine drifted from OsvScanner");
        if !va.findings.is_empty() {
            hits += 1;
        }
    }
    // Sanity: the corpus is non-trivial — a healthy fraction of artifacts hit
    // something (else the equivalence would be vacuous).
    assert!(
        hits > 1_000,
        "corpus too clean to be a meaningful equivalence proof (hits={hits})"
    );
}

#[test]
fn boxed_runtime_swap_agrees() {
    let mut rng = Rng(0x0123_4567_89AB_CDEF);
    let db = gen_db(&mut rng, 1_000, 100);
    let artifacts = gen_artifacts(&mut rng, 3_000, 100);
    let policy = policy();

    let ea: Box<dyn AdvisoryEngine> = build(EngineKind::A, &db);
    let eb: Box<dyn AdvisoryEngine> = build(EngineKind::B, &db);
    let fa = ea.fleet(&policy, &artifacts);
    let fb = eb.fleet(&policy, &artifacts);
    assert_eq!(fa, fb, "boxed A/B fleet scans must be identical");
}