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
}