fuzz/src/lib.rs
//! **Holger's fuzz entries** (lane OSSF-NEXT, 2026-10-30): one function per untrusted
//! parser, the same function a libFuzzer target (`fuzz_targets/<name>.rs`), the seed test
//! (`tests/seeds.rs`) and a crash's regression test call.
//!
//! | target | what reads attacker bytes |
//! |---|---|
//! | `bundle_verify` | a `.holger.znippy` update bundle: the znippy archive index and members (`load_bundle`), its `core.json`, and the signed verify path under a pinned-signer policy (`load_bundle_verified`, `verify_member_map_with_time`) |
//! | `dsse_material` | provenance material: DSSE envelopes, Sigstore bundles, PEP 740 attestations, cosign attachments, npm signatures (`material::parse`), then `verify_artifact` against Sigstore's pinned public-good root |
//! | `intoto_statement` | an in-toto statement and its SLSA predicate (`statement::Statement::parse`) |
//! | `x509_cert` | a certificate as a Sigstore bundle carries it: DER, the identity and SCT extensions (`der`, `cert`) |
//! | `apk_index` | an `APKINDEX.tar.gz` and its signature segment (`apk::verify_index`) against Wolfi's key |
//! | `package_metadata` | the first byte picks the parser of a package's metadata: the `.deb` control stanza, the `.rpm` header, a Helm chart and its `Chart.yaml`, a wheel's `METADATA`, the licence a package declares, and the inspector over every format |
//!
//! A target only asks that the parser RETURNS: an `Err` is the right answer to most
//! inputs; a panic, a hang or an allocation past libFuzzer's limit is a finding.
pub mod seeds;
use std::path::PathBuf;
use std::sync::OnceLock;
/// Every target, in the order of its `[[bin]]` in `Cargo.toml`.
pub const TARGETS: &[&str] = &[
"bundle_verify",
"dsse_material",
"intoto_statement",
"x509_cert",
"apk_index",
"package_metadata",
];
/// Run `data` through `target`'s entry.
pub fn run(target: &str, data: &[u8]) {
match target {
"bundle_verify" => bundle_verify(data),
"dsse_material" => dsse_material(data),
"intoto_statement" => intoto_statement(data),
"x509_cert" => x509_cert(data),
"apk_index" => apk_index(data),
"package_metadata" => package_metadata(data),
other => panic!("no fuzz target {other}"),
}
}
/// The bundle reader opens a path: each process writes its input to its own file.
fn scratch_file() -> &'static PathBuf {
static F: OnceLock<PathBuf> = OnceLock::new();
F.get_or_init(|| {
let dir = std::env::temp_dir().join(format!("holger-fuzz-{}", std::process::id()));
std::fs::create_dir_all(&dir).expect("a scratch dir");
dir.join("input.holger.znippy")
})
}
/// A pinned-signer policy no input can satisfy: the verify path runs to its refusal.
fn bundle_policy() -> &'static holger_update_bundle::TrustPolicy {
use holger_update_bundle::policy::{ApprovedSigner, ExpiredPosture};
static P: OnceLock<holger_update_bundle::TrustPolicy> = OnceLock::new();
P.get_or_init(|| holger_update_bundle::TrustPolicy {
schema_version: holger_update_bundle::schema::POLICY_SCHEMA.into(),
trust_mode: Some(holger_update_bundle::TrustMode::PinnedSigners),
trusted_roots: vec![],
certificate_validity: holger_update_bundle::CertificateValidity::Enforce,
approved_signers: vec![ApprovedSigner {
fingerprint: "00".repeat(32),
subject: None,
roles: vec!["security-reviewer".into()],
}],
required_approval_roles: vec!["security-reviewer".into()],
minimum_distinct_approvers: 1,
trusted_release_signers: vec!["11".repeat(32)],
release_signer_must_be_distinct: true,
maximum_bundle_age_secs: None,
require_expiry: true,
on_expired_metadata: Some(ExpiredPosture::ReadOnly),
require_sbom: true,
require_vex: false,
require_scan: false,
allowed_products: vec![],
allowed_models: vec![],
})
}
pub fn bundle_verify(data: &[u8]) {
let path = scratch_file();
if std::fs::write(path, data).is_err() {
return;
}
if let Ok(members) = holger_update_bundle::load_bundle(path) {
if let Some(core) = members.get("core.json") {
let _ = holger_update_bundle::BundleCore::from_json_bytes(core);
}
let _ = holger_update_bundle::verify::verify_member_map_with_time(
&members,
bundle_policy(),
&holger_update_bundle::verify::TargetSpec::default(),
None,
&holger_update_bundle::HostRtc,
);
}
let _ = holger_update_bundle::container::load_bundle_verified(path, bundle_policy());
}
const TRUSTED_ROOT: &[u8] =
include_bytes!("../../provenance/tests/fixtures/sigstore/trusted_root.json");
fn trust() -> &'static holger_provenance::trust::TrustRoot {
static T: OnceLock<holger_provenance::trust::TrustRoot> = OnceLock::new();
T.get_or_init(|| {
holger_provenance::trust::TrustRoot::from_trusted_root_json(TRUSTED_ROOT)
.expect("the pinned root reads")
})
}
pub fn dsse_material(data: &[u8]) {
use holger_provenance::policy::{effective, ProvenanceFloor, ProvenanceGate};
let _ = holger_provenance::material::parse("fuzz.json", data);
let policy = effective(&ProvenanceFloor::default(), &ProvenanceGate::Prefer);
let materials = vec![("fuzz.sigstore.json".to_string(), data.to_vec())];
let _ = holger_provenance::verify::verify_artifact(
b"the artifact",
&materials,
&policy,
Some(trust()),
);
}
pub fn intoto_statement(data: &[u8]) {
if let Ok(s) = holger_provenance::statement::Statement::parse(data) {
let _ = s.describe_subjects();
let _ = s.is_provenance();
let _ = s.slsa();
let _ = s.names(&holger_provenance::statement::ArtifactDigests::of(
b"the artifact",
));
}
}
pub fn x509_cert(data: &[u8]) {
use holger_provenance::{cert, der};
if let Ok((tlv, _rest)) = der::read(data) {
let _ = der::children(tlv.content);
}
let _ = der::cert_parts(data);
let _ = cert::identity(data);
let _ = cert::scts(data);
let _ = cert::validity(data);
let _ = cert::allows_code_signing(data);
let _ = cert::public_key(data);
let _ = cert::issued_by(data, data);
}
const WOLFI_KEY: &[u8] =
include_bytes!("../../provenance/tests/fixtures/wolfi/wolfi-signing.rsa.pub");
pub fn apk_index(data: &[u8]) {
use holger_provenance::apk;
static KEYS: OnceLock<Vec<apk::ApkKey>> = OnceLock::new();
let keys =
KEYS.get_or_init(|| {
vec![apk::ApkKey::from_pem("wolfi-signing.rsa.pub", WOLFI_KEY)
.expect("the wolfi key reads")]
});
let _ = apk::is_index("APKINDEX.tar.gz", data);
let _ = apk::verify_index(data, keys);
}
/// The selector byte of `package_metadata`, in order.
pub const PACKAGE_PARSERS: &[&str] = &[
"deb-control",
"rpm-header",
"helm-chart",
"chart-yaml",
"wheel-metadata",
"metadata-text",
"license",
"inspect",
];
pub fn package_metadata(data: &[u8]) {
let Some((&sel, rest)) = data.split_first() else {
return;
};
match sel as usize % PACKAGE_PARSERS.len() {
0 => {
let _ = holger_znippy_deb_repository::control_from_deb(rest);
}
1 => {
let _ = holger_znippy_rpm_repository::parse_rpm_header(
rest, "hello", "2.10", "1", "x86_64",
);
}
2 => {
let _ = holger_znippy_helm_repository::chart_meta_from_tgz_capped(rest, 1 << 20);
}
3 => {
let _ = holger_znippy_helm_repository::parse_chart_yaml(&String::from_utf8_lossy(rest));
}
4 => {
let _ = holger_server_lib::scanner::wheel::read_metadata(rest);
}
5 => {
let _ =
holger_server_lib::scanner::wheel::parse_metadata(&String::from_utf8_lossy(rest));
}
6 => {
let Some((&eco, body)) = rest.split_first() else {
return;
};
let eco = [
"pip", "npm", "cargo", "maven", "gem", "nuget", "deb", "rpm", "helm", "go",
][eco as usize % 10];
let _ = holger_server_lib::scanner::license::detect(eco, body);
}
_ => {
let Some((&f, body)) = rest.split_first() else {
return;
};
let all = holger_inspect::Format::ALL;
let format = all[f as usize % all.len()];
static INSPECTOR: OnceLock<holger_inspect::Inspector> = OnceLock::new();
let insp = INSPECTOR.get_or_init(|| {
holger_inspect::Inspector::from_config(&holger_inspect::InspectConfig::default())
});
let _ = insp.inspect(body, format, "fuzz-1.0");
}
}
}