Gunnar

nordisk/heimdall

guests/verify-bundle/src/fuzz_api.rs

// SPDX-License-Identifier: AGPL-3.0-only
//! Entry points for the cargo-fuzz targets in `fuzz/` (and their seed-replay
//! tests). Each one drives a parser over arbitrary bytes; the property is that
//! none of them panics or aborts, and that nothing is accepted that should not be.

use crate::dsse::{self, DsseTrust};
use crate::model::CertificateValidity;
use crate::znippy::Archive;

/// The znippy index parser, then a read of every member it lists.
pub fn archive(data: &[u8]) {
    if let Ok(a) = Archive::parse(data) {
        let _ = a.check_expansion_ratio();
        let names: Vec<String> = a.names().map(str::to_string).collect();
        for n in names {
            let _ = a.read(&n, 1 << 20);
        }
    }
}

/// The whole pipeline with fixed, permissive-shaped parameters: arbitrary
/// bytes must never be ACCEPTED (no key in these parameters signs anything).
pub fn bundle(data: &[u8]) {
    const P: &str = r#"VerifyParams(now: "2026-10-30T12:00:00Z", policy: (schema_version: "holger.bundle-trust-policy/v1", trust_mode: Some(PinnedSigners), minimum_distinct_approvers: 0, trusted_release_signers: ["00"]))"#;
    let o = crate::verify_bundle(data, P.as_bytes());
    assert!(!o.is_accepted(), "arbitrary bytes were accepted");
}

/// One DSSE envelope, with no signer pinned: never verified.
pub fn dsse_envelope(data: &[u8]) {
    let trust = DsseTrust {
        trusted: &[],
        keys: Vec::new(),
        certificates: vec![data],
        now: chrono::DateTime::UNIX_EPOCH,
        validity: CertificateValidity::Enforce,
    };
    assert!(dsse::verify_envelope("fuzz.dsse.json", data, &trust, &["00"]).is_err());
}

/// The parameter document (RON).
pub fn params(data: &[u8]) {
    let _ = crate::VerifyParams::from_ron_bytes(data);
}

/// An X.509 signer certificate.
pub fn certificate(data: &[u8]) {
    if let Ok(c) = crate::cert::SignerCert::parse(data) {
        let _ = c.verify(b"m", &[0u8; 64]);
    }
}

/// One OpenZL frame through the decoder exactly as the archive reader drives
/// it: the header's size first (bounded here at 16 MiB, as a member cap would),
/// then a decode into exactly that many bytes. Must never panic, abort, or write
/// past the buffer (natively under ASan; in wasm a C fault can only trap).
#[cfg(feature = "openzl")]
pub fn openzl_frame(data: &[u8]) {
    if let Ok(n) = crate::zl::frame_size(data) {
        if n <= 16 << 20 {
            let mut out = vec![0u8; n as usize];
            if let Ok(w) = crate::zl::decode(data, &mut out) {
                assert!(w <= out.len());
            }
        }
    }
}