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

nordisk/holger

fuzz/src/seeds.rs

//! **The synthetic seeds**: small, well-formed inputs per target, made here from the crates'
//! own builders and the provenance crate's real fixtures, so libFuzzer starts past the
//! magic numbers. `tests/seeds.rs` holds `corpus/<target>/` equal to this list.
//!
//! `package_metadata` seeds start with their selector byte (`PACKAGE_PARSERS`), so no
//! committed seed begins with a package's magic number: the corpus is fuzz input, not a
//! package anyone installs.

use std::io::Write;
use std::path::Path;

use base64::Engine as _;

const PROV_FX: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../provenance/tests/fixtures");

fn read(rel: &str) -> Vec<u8> {
    std::fs::read(Path::new(PROV_FX).join(rel)).unwrap_or_else(|e| panic!("{rel}: {e}"))
}

fn tgz(files: &[(&str, &[u8])]) -> Vec<u8> {
    let mut b = tar::Builder::new(Vec::new());
    for (name, body) in files {
        let mut h = tar::Header::new_gnu();
        h.set_size(body.len() as u64);
        h.set_mode(0o644);
        h.set_mtime(0);
        h.set_cksum();
        b.append_data(&mut h, name, *body).expect("in-memory tar");
    }
    let tar = b.into_inner().expect("in-memory tar");
    let mut gz = flate2::GzBuilder::new()
        .mtime(0)
        .write(Vec::new(), flate2::Compression::default());
    gz.write_all(&tar).expect("in-memory gzip");
    gz.finish().expect("in-memory gzip")
}

fn zip_of(files: &[(&str, &[u8])]) -> Vec<u8> {
    let mut w = zip::ZipWriter::new(std::io::Cursor::new(Vec::new()));
    let opt = zip::write::SimpleFileOptions::default()
        .compression_method(zip::CompressionMethod::Deflated)
        .last_modified_time(zip::DateTime::default());
    for (name, body) in files {
        w.start_file(*name, opt).expect("in-memory zip");
        w.write_all(body).expect("in-memory zip");
    }
    w.finish().expect("in-memory zip").into_inner()
}

fn with(sel: u8, rest: &[u8]) -> Vec<u8> {
    let mut v = vec![sel];
    v.extend_from_slice(rest);
    v
}

fn parser(name: &str) -> u8 {
    crate::PACKAGE_PARSERS
        .iter()
        .position(|p| *p == name)
        .expect("a package parser") as u8
}

const METADATA: &[u8] = b"Metadata-Version: 2.1\nName: fuzzpkg\nVersion: 1.0\nSummary: a fuzz seed\nLicense: Apache-2.0\nRequires-Dist: requests (>=2.0)\nRequires-Dist: idna; extra == \"x\"\n\nlong description\n";
const CHART: &[u8] = b"apiVersion: v2\nname: fuzzchart\nversion: 1.0.0\ndescription: a fuzz seed\ndependencies:\n  - name: common\n    version: 2.x\n    repository: oci://example.invalid/charts\n";
const PACKAGE_JSON: &[u8] = b"{\"name\":\"fuzzpkg\",\"version\":\"1.0.0\",\"license\":\"MIT\",\"dependencies\":{\"lodash\":\"^4.17.21\"}}";
const CARGO_TOML: &[u8] =
    b"[package]\nname = \"fuzzpkg\"\nversion = \"1.0.0\"\nlicense = \"MIT OR Apache-2.0\"\n";
const CONTROL: &str = "Package: fuzzpkg\nVersion: 1.0-1\nArchitecture: amd64\nMaintainer: Fuzz <fuzz@mock.invalid>\nDepends: libc6 (>= 2.34)\nDescription: a fuzz seed\n";

/// `(target, file name, bytes)`.
pub fn synthetic() -> Vec<(&'static str, String, Vec<u8>)> {
    let mut out: Vec<(&'static str, String, Vec<u8>)> = Vec::new();

    // bundle_verify: a packed bundle with a core.json, and the bare members.
    let dir = std::env::temp_dir().join(format!("holger-fuzz-seed-{}", std::process::id()));
    std::fs::create_dir_all(&dir).expect("a scratch dir");
    let path = dir.join("seed.holger.znippy");
    let mut members = holger_update_bundle::MemberMap::new();
    members.insert(
        "core.json".into(),
        br#"{"schema_version":"holger.update-bundle/v1","product":"fuzz","bundle_id":"seed"}"#
            .to_vec(),
    );
    members.insert("manifest.json".into(), br#"{"members":[]}"#.to_vec());
    members.insert("payload/readme.txt".into(), b"a fuzz seed\n".to_vec());
    holger_update_bundle::pack_bundle(&members, &path).expect("a seed bundle packs");
    out.push((
        "bundle_verify",
        "packed".into(),
        std::fs::read(&path).expect("the seed bundle"),
    ));
    let _ = std::fs::remove_dir_all(&dir);

    // dsse_material: the provenance crate's real, public material (text).
    for (name, rel) in [
        ("pep740", "pypi/sampleproject-4.0.0.tar.gz.provenance.json"),
        ("npm-attestations", "npm/sigstore-5.0.0.attestations.json"),
        ("cosign", "chainguard/static-latest.cosign.json"),
        ("slsa-jsonl", "slsa-dwarf/holger-2.0.0-rebuild.intoto.jsonl"),
    ] {
        out.push(("dsse_material", name.into(), read(rel)));
    }

    // intoto_statement
    out.push((
        "intoto_statement",
        "slsa-v1".into(),
        br#"{"_type":"https://in-toto.io/Statement/v1","subject":[{"name":"x.whl","digest":{"sha256":"2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"}}],"predicateType":"https://slsa.dev/provenance/v1","predicate":{"buildDefinition":{"buildType":"https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1","externalParameters":{},"resolvedDependencies":[{"uri":"git+https://example.invalid/r","digest":{"gitCommit":"00"}}]},"runDetails":{"builder":{"id":"https://github.com/actions/runner/github-hosted"}}}}"#.to_vec(),
    ));

    // x509_cert: the certificates of Sigstore's pinned root (DER).
    let root: serde_json::Value =
        serde_json::from_slice(&read("sigstore/trusted_root.json")).expect("trusted_root.json");
    let mut n = 0;
    for ca in root["certificateAuthorities"]
        .as_array()
        .into_iter()
        .flatten()
    {
        for c in ca["certChain"]["certificates"]
            .as_array()
            .into_iter()
            .flatten()
        {
            if let Some(b) = c["rawBytes"]
                .as_str()
                .and_then(|s| base64::engine::general_purpose::STANDARD.decode(s).ok())
            {
                n += 1;
                out.push(("x509_cert", format!("sigstore-ca-{n}"), b));
            }
        }
    }

    // apk_index: an index-shaped stream (a signature segment, then the index).
    out.push((
        "apk_index",
        "shaped".into(),
        [tgz(&[(".SIGN.RSA256.wolfi-signing.rsa.pub", &[0u8; 64][..])]), tgz(&[("DESCRIPTION", b"fuzz"), ("APKINDEX", b"C:Q1abc=\nP:fuzzpkg\nV:1.0-r0\nA:x86_64\nS:100\nI:200\nT:a fuzz seed\nL:MIT\n\n")])].concat(),
    ));

    // package_metadata: one seed per parser, the selector byte first.
    let deb = holger_znippy_deb_repository::fixtures::make_deb(".gz", &{
        let tar = holger_znippy_deb_repository::fixtures::control_tar(CONTROL);
        let mut gz = flate2::GzBuilder::new()
            .mtime(0)
            .write(Vec::new(), flate2::Compression::default());
        gz.write_all(&tar).expect("in-memory gzip");
        gz.finish().expect("in-memory gzip")
    });
    out.push((
        "package_metadata",
        "deb-control".into(),
        with(parser("deb-control"), &deb),
    ));
    {
        use holger_znippy_rpm_repository::fixtures::{make_rpm, Tag};
        let rpm = make_rpm(&[
            Tag::Str(1004, "a fuzz seed"),
            Tag::Str(1014, "MIT"),
            Tag::Int32(1003, 1),
            Tag::StrArray(1049, &["glibc", "zlib"]),
        ]);
        out.push((
            "package_metadata",
            "rpm-header".into(),
            with(parser("rpm-header"), &rpm),
        ));
    }
    let chart = tgz(&[
        ("fuzzchart/Chart.yaml", CHART),
        ("fuzzchart/values.yaml", b"replicas: 1\n"),
    ]);
    out.push((
        "package_metadata",
        "helm-chart".into(),
        with(parser("helm-chart"), &chart),
    ));
    out.push((
        "package_metadata",
        "chart-yaml".into(),
        with(parser("chart-yaml"), CHART),
    ));
    let wheel = zip_of(&[
        ("fuzzpkg/__init__.py", b"x = 1\n"),
        ("fuzzpkg-1.0.dist-info/METADATA", METADATA),
        (
            "fuzzpkg-1.0.dist-info/WHEEL",
            b"Wheel-Version: 1.0\nTag: py3-none-any\n",
        ),
    ]);
    out.push((
        "package_metadata",
        "wheel-metadata".into(),
        with(parser("wheel-metadata"), &wheel),
    ));
    out.push((
        "package_metadata",
        "metadata-text".into(),
        with(parser("metadata-text"), METADATA),
    ));
    let npm = tgz(&[
        ("package/package.json", PACKAGE_JSON),
        ("package/index.js", b"module.exports = 1;\n"),
    ]);
    let krate = tgz(&[
        ("fuzzpkg-1.0.0/Cargo.toml", CARGO_TOML),
        ("fuzzpkg-1.0.0/src/lib.rs", b"pub fn f() {}\n"),
    ]);
    for (eco, body, name) in [
        (0u8, &wheel, "license-pip"),
        (1, &npm, "license-npm"),
        (2, &krate, "license-cargo"),
    ] {
        out.push((
            "package_metadata",
            name.into(),
            with(parser("license"), &with(eco, body)),
        ));
    }
    for (i, f) in holger_inspect::Format::ALL.iter().enumerate() {
        let body = match f {
            holger_inspect::Format::Wheel
            | holger_inspect::Format::Jar
            | holger_inspect::Format::Nuget => wheel.clone(),
            holger_inspect::Format::Npm => npm.clone(),
            holger_inspect::Format::Crate => krate.clone(),
            holger_inspect::Format::Helm => chart.clone(),
            holger_inspect::Format::Deb => deb.clone(),
            _ => tgz(&[("fuzz/README", b"a fuzz seed\n")]),
        };
        out.push((
            "package_metadata",
            format!("inspect-{i:02}"),
            with(parser("inspect"), &with(i as u8, &body)),
        ));
    }
    out
}