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

nordisk/heimdall

guests/static-scan/tests/registry.rs

// SPDX-License-Identifier: AGPL-3.0-only
//! The registry check (yanked, deprecated, retracted; modgunn::registry) and
//! the malicious check (OSV MAL- records; modgunn::feeds::parse_malicious), on
//! the registry documents the caller sent. Heimdall is a defensive sandbox.

mod common;
use common::*;

use heimdall_scan::report::unknown;
use heimdall_scan::{Check, CheckResult, DataKind, Severity, Verdict};

const CRATES: &[u8] = br#"{"crate":{"name":"smallvec"},"versions":[
  {"num":"1.6.0","yanked":true,"crate":"smallvec"},
  {"num":"1.6.1","yanked":false,"crate":"smallvec"},
  {"num":"1.13.2","yanked":false,"crate":"smallvec"}]}"#;
const NPM: &[u8] = br#"{"name":"request","versions":{
  "2.88.2":{"deprecated":"request has been deprecated, see https://github.com/request/request/issues/3142"},
  "2.88.0":{}}}"#;
const PYPI: &[u8] = br#"{"info":{"name":"PyYAML"},"releases":{
  "5.3":[{"yanked":true,"yanked_reason":"security"}],
  "5.3.1":[{"yanked":false}]}}"#;
const GOMOD: &[u8] = b"module github.com/acme/widget\n\ngo 1.22\n\nretract (\n    v1.2.0 // published by accident\n    [v1.3.0, v1.3.5]\n)\n";
const MAL: &[u8] = br#"[{"id":"MAL-2025-1234","summary":"Malicious code in evil-pkg (npm)",
  "affected":[{"package":{"ecosystem":"npm","name":"evil-pkg"}}]},
  {"id":"MAL-2025-9999","summary":"Malicious code in nasty (PyPI)",
  "affected":[{"package":{"ecosystem":"PyPI","name":"nasty"},"versions":["0.1.0"]}]}]"#;

fn reg(purl: &str, data: Option<&[u8]>) -> CheckResult {
    let d: Vec<(DataKind, &[u8])> = data
        .map(|d| vec![(DataKind::Registry, d)])
        .unwrap_or_default();
    check(
        &envelope(b"", &d),
        &[("purl", purl), ("checks", "registry")],
        Check::Registry,
    )
}

fn mal(purl: &str, data: Option<&[u8]>) -> CheckResult {
    let d: Vec<(DataKind, &[u8])> = data
        .map(|d| vec![(DataKind::Malicious, d)])
        .unwrap_or_default();
    check(
        &envelope(b"", &d),
        &[("purl", purl), ("checks", "malicious")],
        Check::Malicious,
    )
}

#[test]
fn a_yanked_crate_version_is_a_registry_finding_with_the_upgrade_target() {
    let r = reg("pkg:cargo/smallvec@1.6.0", Some(CRATES));
    assert_eq!(r.verdict, Verdict::Findings);
    assert_eq!(r.findings[0].id, "MODGUNN-YANK-cargo-smallvec-1.6.0");
    assert!(
        r.findings[0].summary.contains("1.6.1"),
        "{:?}",
        r.findings[0]
    );
}

#[test]
fn a_version_that_is_not_yanked_is_clean() {
    assert_clean(&reg("pkg:cargo/smallvec@1.13.2", Some(CRATES)));
}

#[test]
fn a_deprecated_npm_version_is_a_finding() {
    let r = reg("pkg:npm/request@2.88.2", Some(NPM));
    assert!(has(&r, "MODGUNN-DEPRECATED-npm-request-2.88.2"), "{r:?}");
    assert_clean(&reg("pkg:npm/request@2.88.0", Some(NPM)));
}

#[test]
fn a_yanked_pypi_release_is_a_finding_and_names_compare_by_pep_503() {
    let r = reg("pkg:pypi/pyyaml@5.3", Some(PYPI));
    assert!(has(&r, "MODGUNN-YANK-pypi-PyYAML-5.3"), "{r:?}");
    assert_clean(&reg("pkg:pypi/PyYAML@5.3.1", Some(PYPI)));
}

#[test]
fn a_retracted_go_version_is_a_finding_including_a_retracted_range() {
    assert!(
        reg("pkg:golang/github.com/acme/widget@v1.2.0", Some(GOMOD)).verdict == Verdict::Findings
    );
    assert!(
        reg("pkg:golang/github.com/acme/widget@v1.3.2", Some(GOMOD)).verdict == Verdict::Findings
    );
    assert_clean(&reg(
        "pkg:golang/github.com/acme/widget@v1.4.0",
        Some(GOMOD),
    ));
}

#[test]
fn a_registry_document_about_another_package_is_unknown_never_clean() {
    assert_unknown(
        &reg("pkg:cargo/serde@1.6.1", Some(CRATES)),
        unknown::DATA_INVALID,
    );
    assert_unknown(
        &reg("pkg:npm/lodash@2.88.0", Some(NPM)),
        unknown::DATA_INVALID,
    );
    assert_unknown(
        &reg("pkg:golang/github.com/evil/widget@v1.0.0", Some(GOMOD)),
        unknown::DATA_INVALID,
    );
}

#[test]
fn a_version_missing_from_the_registry_document_is_unknown_as_a_stale_snapshot() {
    assert_unknown(
        &reg("pkg:cargo/smallvec@9.9.9", Some(CRATES)),
        unknown::DATA_INVALID,
    );
}

#[test]
fn a_missing_registry_snapshot_is_unknown() {
    assert_unknown(
        &reg("pkg:cargo/smallvec@1.6.0", None),
        unknown::DATA_MISSING,
    );
}

#[test]
fn a_malformed_registry_document_is_unknown() {
    for bad in [
        &b"{"[..],
        b"[]",
        br#"{"crate":{"name":"smallvec"}}"#,
        b"\xff\xfe",
    ] {
        assert_unknown(
            &reg("pkg:cargo/smallvec@1.6.0", Some(bad)),
            unknown::DATA_INVALID,
        );
    }
}

#[test]
fn a_purl_without_a_version_is_unknown_for_the_registry() {
    assert_unknown(
        &reg("pkg:cargo/smallvec", Some(CRATES)),
        unknown::NO_VERSION,
    );
}

#[test]
fn an_ecosystem_without_a_registry_reader_is_unknown() {
    assert_unknown(
        &reg(
            "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1",
            Some(b"{}"),
        ),
        unknown::UNSUPPORTED_ECOSYSTEM,
    );
}

#[test]
fn a_package_on_the_malicious_feed_is_a_critical_finding() {
    let r = mal("pkg:npm/evil-pkg@1.0.0", Some(MAL));
    assert_eq!(r.verdict, Verdict::Findings);
    assert_eq!(r.findings[0].id, "MAL-2025-1234");
    assert_eq!(r.findings[0].severity, Severity::Critical);
}

#[test]
fn a_versioned_malicious_record_matches_only_its_versions() {
    assert!(has(
        &mal("pkg:pypi/nasty@0.1.0", Some(MAL)),
        "MAL-2025-9999"
    ));
    assert_clean(&mal("pkg:pypi/nasty@0.2.0", Some(MAL)));
}

#[test]
fn a_package_not_on_the_feed_is_clean() {
    assert_clean(&mal("pkg:npm/lodash@4.17.21", Some(MAL)));
}

#[test]
fn an_empty_feed_array_is_a_real_none() {
    assert_clean(&mal("pkg:npm/lodash@4.17.21", Some(b"[]")));
}

#[test]
fn a_single_osv_record_feed_reads_too() {
    let one = br#"{"id":"MAL-2025-1","summary":"x","affected":[{"package":{"ecosystem":"crates.io","name":"evil"}}]}"#;
    assert!(has(&mal("pkg:cargo/evil@0.1.0", Some(one)), "MAL-2025-1"));
}

#[test]
fn a_missing_or_garbage_feed_is_unknown() {
    assert_unknown(&mal("pkg:npm/evil-pkg@1.0.0", None), unknown::DATA_MISSING);
    assert_unknown(
        &mal("pkg:npm/evil-pkg@1.0.0", Some(b"not json")),
        unknown::DATA_INVALID,
    );
}

#[test]
fn without_a_version_whole_package_records_still_match_and_the_rest_is_unknown() {
    let r = mal("pkg:npm/evil-pkg", Some(MAL));
    assert_eq!(r.verdict, Verdict::Findings);
    assert_unknown(&mal("pkg:pypi/nasty", Some(MAL)), unknown::NO_VERSION);
}