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

nordisk/dwarves

crates/dwarves-slsa/tests/workspace_example.rs

//! `[[workspace_example]]`: a release step that runs an example of a workspace from the
//! release's SOURCES (`cargo run --release --locked --offline --example <name>`) in a
//! sealed, network-off build container, and takes the files it writes as subjects. The
//! case it is for: Heimdall's verify-bundle guest, whose `dist` example builds the three
//! wasm components (job, verifier, decoder) and their `.wasm.blake3` reproducibly; the
//! guest is its own workspace, so its lock is a second `lock` of the release.

use std::collections::{BTreeMap, BTreeSet};
use std::path::Path;
use std::process::Command;
use std::sync::Arc;

use dwarves_runa::{Pipeline, Step};
use dwarves_slsa::runner::{
    self, check_isolation, fetch_container, release_pipeline, workspace_example_container,
    Executor, Layout, ReleaseRun, RunOptions, RunRecord, StepRecord, ACTIONS,
    FORGE_COLLECT_EXAMPLE,
};
use dwarves_slsa::spec::ReleaseSpec;
use dwarves_slsa::Result;

const IMAGE: &str = "localhost/nordisk-cargo-toolchain@sha256:5470ba85216b3c38d35de0bbb36a4244e104995fce4e9bf79a0c0357e9b8b29f";
const ROOT: &str = "/home/u/slsa-runs/heimdall-guests-0.2.1-20261031T120000Z";

const OUTPUTS: [&str; 6] = [
    "guests/heimdall-verify-bundle.wasm",
    "guests/heimdall-verify-bundle.wasm.blake3",
    "guests/heimdall-verify-bundle-verifier.wasm",
    "guests/heimdall-verify-bundle-verifier.wasm.blake3",
    "guests/heimdall-verify-bundle-decoder.wasm",
    "guests/heimdall-verify-bundle-decoder.wasm.blake3",
];

fn spec_text(commit: &str, extra: &str) -> String {
    let outputs = OUTPUTS.iter().map(|o| format!("{o:?}")).collect::<Vec<_>>().join(", ");
    format!(
        r#"
name = "heimdall-guests-0.2.1"
image = "{IMAGE}"
lock = ["heimdall/Cargo.toml", "heimdall/guests/verify-bundle/Cargo.toml"]
[[repo]]
name = "heimdall"
org = "nordisk"
commit = "{commit}"
[[workspace_example]]
manifest = "heimdall/guests/verify-bundle/Cargo.toml"
example = "dist"
args = ["{{out}}/guests", "/target/guests"]
outputs = [{outputs}]
{extra}"#
    )
}

fn spec() -> ReleaseSpec {
    ReleaseSpec::parse(&spec_text(&"a".repeat(40), "")).unwrap()
}

fn refused(edit: impl Fn(String) -> String, why: &str) {
    let text = edit(spec_text(&"a".repeat(40), ""));
    let e = ReleaseSpec::parse(&text).unwrap_err();
    assert!(e.0.contains(why), "want {why:?} in: {e}");
}

#[test]
fn a_release_of_one_workspace_example_only_is_a_release() {
    let s = spec();
    assert!(s.crates.is_empty() && s.workspace_binaries.is_empty());
    let w = &s.workspace_examples[0];
    assert_eq!(w.manifest, "heimdall/guests/verify-bundle/Cargo.toml");
    assert_eq!(w.example, "dist");
    assert_eq!(w.args, ["{out}/guests", "/target/guests"]);
    assert_eq!(w.outputs.len(), 6);
    let ep = s.external_parameters();
    assert_eq!(ep["workspaceExamples"][0]["example"], "dist");
    assert_eq!(ep["workspaceExamples"][0]["outputs"][0], OUTPUTS[0]);
    // the TOML round-trips
    assert_eq!(ReleaseSpec::parse(&s.to_toml().unwrap()).unwrap(), s);
}

#[test]
fn the_guest_lock_is_a_second_lock_vendored_with_the_first() {
    let s = spec();
    assert_eq!(s.locks, ["heimdall/Cargo.toml", "heimdall/guests/verify-bundle/Cargo.toml"]);
    let c = fetch_container(&Layout::new(ROOT), &s);
    assert_eq!(
        c.cmd,
        [
            "cargo", "vendor", "--locked", "--versioned-dirs", "-s",
            "/src/heimdall/guests/verify-bundle/Cargo.toml", "/vendor/crates"
        ]
    );
    assert_eq!(c.workdir, "/src/heimdall");
}

#[test]
fn a_workspace_example_of_an_unpinned_repo_or_outside_path_is_refused() {
    refused(
        |t| t.replace("manifest = \"heimdall/guests", "manifest = \"edda/guests"),
        "pinned",
    );
    refused(
        |t| t.replace("manifest = \"heimdall/guests/verify-bundle/Cargo.toml\"", "manifest = \"heimdall/../x/Cargo.toml\""),
        "manifest",
    );
    refused(
        |t| t.replace("manifest = \"heimdall/guests/verify-bundle/Cargo.toml\"", "manifest = \"heimdall/guests/verify-bundle/x.rs\""),
        "manifest",
    );
}

#[test]
fn a_workspace_example_names_only_safe_names_and_paths() {
    refused(|t| t.replace("example = \"dist\"", "example = \"../dist\""), "example");
    refused(|t| t.replace("example = \"dist\"", "example = \"\""), "example");
    refused(|t| t.replace("\"{out}/guests\"", "\"{out}/../guests\""), "{out}");
    refused(|t| t.replace("\"{out}/guests\"", "\"{out}guests\""), "{out}");
    refused(|t| t.replace("\"/target/guests\"", "\"/target/../src\""), "..");
}

#[test]
fn a_workspace_example_declares_its_outputs_once_each_inside_out() {
    refused(|t| t.replace(&format!("{:?}", OUTPUTS[1]), "\"/etc/passwd\""), "output");
    refused(|t| t.replace(&format!("{:?}", OUTPUTS[1]), "\"guests/../x\""), "output");
    refused(|t| t.replace(&format!("{:?}", OUTPUTS[1]), &format!("{:?}", OUTPUTS[0])), "twice");
    refused(
        |t| {
            let start = t.find("outputs = [").unwrap();
            let end = t[start..].find(']').unwrap() + start + 1;
            format!("{}outputs = []{}", &t[..start], &t[end..])
        },
        "output",
    );
    // two examples may not write the same file
    let two = spec_text(
        &"a".repeat(40),
        &format!(
            "[[workspace_example]]\nmanifest = \"heimdall/guests/verify-bundle/Cargo.toml\"\nexample = \"dist\"\nargs = [\"{{out}}\"]\noutputs = [{:?}]\n",
            OUTPUTS[0]
        ),
    );
    let e = ReleaseSpec::parse(&two).unwrap_err();
    assert!(e.0.contains("twice"), "{e}");
}

#[test]
fn a_workspace_example_runs_offline_from_the_snapshot_in_a_sealed_container() {
    let s = spec();
    let l = Layout::new(ROOT);
    let c = workspace_example_container(&l, &s, 0, &s.workspace_examples[0], 6);
    check_isolation(&c, &l, true).unwrap();
    assert!(!c.network, "network none");
    assert_eq!(
        c.cmd,
        [
            "cargo", "run", "--release", "--locked", "--offline", "--manifest-path",
            "/src/heimdall/guests/verify-bundle/Cargo.toml", "--example", "dist",
            "--target-dir", "/target/example-0", "--", "/example-out/guests", "/target/guests"
        ]
    );
    // the example's own cargo (it builds the guest for wasm32) is offline by construction
    assert!(c.env.contains(&("CARGO_NET_OFFLINE".to_string(), "true".to_string())), "{:?}", c.env);
    assert!(c.env.contains(&("CARGO_HOME".to_string(), "/cargo-home".to_string())));
    assert_eq!(c.workdir, "/src/heimdall/guests/verify-bundle");
    let o = c.bind("/example-out").expect("its own output directory");
    assert!(!o.read_only);
    assert_eq!(o.host, Path::new(ROOT).join("example-out/0"));
    assert!(c.bind("/out").is_none(), "the build never writes out/ itself: the forge collects");
    for ro in ["/src", "/vendor", "/stage", "/plan", "/published"] {
        assert!(c.bind(ro).unwrap().read_only, "{ro}");
    }
}

#[test]
fn the_pipeline_runs_the_example_after_the_vendor_check_then_collects_it() {
    let s = spec();
    let p = release_pipeline(&s, &Layout::new(ROOT), &BTreeSet::new(), 4).unwrap();
    let names: Vec<&str> = p.stages.iter().map(|s| s.name.as_str()).collect();
    assert_eq!(
        names,
        [
            "sources", "seal-sources", "fetch", "probe-lock", "probe-vendor", "vendor-check",
            "workspace-example-0-dist", "workspace-example-0-dist-out",
        ]
    );
    match &p.stages[6].steps[0] {
        Step::Sandbox { cmd, network, .. } => {
            assert_eq!(cmd[..2], ["cargo", "run"]);
            assert!(!network);
        }
        other => panic!("{other:?}"),
    }
    match &p.stages[7].steps[0] {
        Step::Forge { action, args } => {
            assert_eq!(action, FORGE_COLLECT_EXAMPLE);
            assert_eq!(args, &["0"]);
        }
        other => panic!("{other:?}"),
    }
    assert!(ACTIONS.contains(&FORGE_COLLECT_EXAMPLE));
    p.validate().unwrap();
}

fn write(p: &Path, body: &[u8]) {
    std::fs::create_dir_all(p.parent().unwrap()).unwrap();
    std::fs::write(p, body).unwrap();
}

fn written(l: &Layout) {
    for o in OUTPUTS {
        write(&l.example_out().join("0").join(o), o.as_bytes());
    }
}

#[test]
fn the_forge_collects_exactly_the_declared_outputs_into_out() {
    let d = tempfile::tempdir().unwrap();
    let l = Layout::new(d.path());
    let rr = ReleaseRun::new(spec(), l.clone(), None, BTreeMap::new());
    written(&l);
    rr.forge(FORGE_COLLECT_EXAMPLE, &["0".into()], &mut |_| {}).unwrap();
    for o in OUTPUTS {
        assert_eq!(std::fs::read(l.out().join(o)).unwrap(), o.as_bytes(), "{o}");
    }
    // a second collect would overwrite a subject
    let e = rr.forge(FORGE_COLLECT_EXAMPLE, &["0".into()], &mut |_| {}).unwrap_err();
    assert!(e.0.contains("twice") || e.0.contains("exists"), "{e}");
    // an index that is not a step
    let e = rr.forge(FORGE_COLLECT_EXAMPLE, &["7".into()], &mut |_| {}).unwrap_err();
    assert!(e.0.contains("7"), "{e}");
}

#[test]
fn the_forge_refuses_a_missing_an_undeclared_or_a_linked_output() {
    // missing
    let d = tempfile::tempdir().unwrap();
    let l = Layout::new(d.path());
    let rr = ReleaseRun::new(spec(), l.clone(), None, BTreeMap::new());
    written(&l);
    std::fs::remove_file(l.example_out().join("0").join(OUTPUTS[3])).unwrap();
    let e = rr.forge(FORGE_COLLECT_EXAMPLE, &["0".into()], &mut |_| {}).unwrap_err();
    assert!(e.0.contains(OUTPUTS[3]), "{e}");
    assert!(!l.out().join(OUTPUTS[0]).exists(), "nothing collected from a refused step");

    // undeclared
    let d = tempfile::tempdir().unwrap();
    let l = Layout::new(d.path());
    let rr = ReleaseRun::new(spec(), l.clone(), None, BTreeMap::new());
    written(&l);
    write(&l.example_out().join("0/guests/extra.wasm"), b"x");
    let e = rr.forge(FORGE_COLLECT_EXAMPLE, &["0".into()], &mut |_| {}).unwrap_err();
    assert!(e.0.contains("extra.wasm") && e.0.contains("not declared"), "{e}");
    assert!(!l.out().join(OUTPUTS[0]).exists());

    // a link
    let d = tempfile::tempdir().unwrap();
    let l = Layout::new(d.path());
    let rr = ReleaseRun::new(spec(), l.clone(), None, BTreeMap::new());
    written(&l);
    let f = l.example_out().join("0").join(OUTPUTS[5]);
    std::fs::remove_file(&f).unwrap();
    std::os::unix::fs::symlink("/etc/hostname", &f).unwrap();
    let e = rr.forge(FORGE_COLLECT_EXAMPLE, &["0".into()], &mut |_| {}).unwrap_err();
    assert!(e.0.contains("not a regular file"), "{e}");
    assert!(!l.out().join(OUTPUTS[0]).exists());
}

// ───────── the whole release, with a stand-in for the sealed containers ─────────

fn git(dir: &Path, args: &[&str]) -> String {
    let o = Command::new("git")
        .arg("-C")
        .arg(dir)
        .args(["-c", "user.name=t", "-c", "user.email=t@example.invalid", "-c", "commit.gpgsign=false"])
        .args(args)
        .output()
        .unwrap();
    assert!(o.status.success(), "git {args:?}: {}", String::from_utf8_lossy(&o.stderr));
    String::from_utf8_lossy(&o.stdout).trim().to_string()
}

/// A repository `heimdall` with a workspace lock and the guest's own lock.
fn repo(from: &Path) -> String {
    let r = from.join("heimdall");
    for (p, body) in [
        ("Cargo.toml", "[workspace]\nmembers = []\n"),
        ("Cargo.lock", "version = 4\n"),
        ("guests/verify-bundle/Cargo.toml", "[package]\nname = \"g\"\nversion = \"0.1.0\"\nedition = \"2021\"\n[workspace]\n"),
        ("guests/verify-bundle/Cargo.lock", "version = 4\n\n[[package]]\nname = \"g\"\nversion = \"0.1.0\"\n"),
        ("guests/verify-bundle/examples/dist.rs", "fn main() {}\n"),
    ] {
        write(&r.join(p), body.as_bytes());
    }
    git(&r, &["init", "-q"]);
    git(&r, &["add", "-A"]);
    git(&r, &["commit", "-q", "-m", "heimdall"]);
    git(&r, &["rev-parse", "HEAD"])
}

struct StandIn {
    leave_out: Option<&'static str>,
}

impl Executor for StandIn {
    fn execute(&self, p: &Pipeline, run: Arc<ReleaseRun>, run_id: &str) -> Result<RunRecord> {
        let l = &run.layout;
        let mut steps = Vec::new();
        for st in &p.stages {
            for s in &st.steps {
                let ok = match s {
                    Step::Forge { action, args } => run
                        .forge(action, args, &mut |m| eprintln!("  [{}] {m}", st.name))
                        .map_err(|e| eprintln!("{}: {e}", st.name))
                        .is_ok(),
                    Step::Sandbox { cmd, mounts, .. } => {
                        match cmd[1].as_str() {
                            "vendor" => std::fs::create_dir_all(l.vendor().join("crates")).unwrap(),
                            "generate-lockfile" => std::fs::write(
                                l.probe().join("Cargo.lock"),
                                "version = 4\n\n[[package]]\nname = \"dwarves-slsa-probe\"\nversion = \"0.0.0\"\n",
                            )
                            .unwrap(),
                            "run" => {
                                // what `dist` writes into /example-out
                                let m = mounts.iter().find(|m| m.path == "/example-out").unwrap();
                                for o in OUTPUTS.iter().filter(|o| Some(**o) != self.leave_out) {
                                    write(&Path::new(&m.host).join(o), format!("bytes of {o}").as_bytes());
                                }
                            }
                            other => panic!("{other}"),
                        }
                        true
                    }
                    other => panic!("{other:?}"),
                };
                steps.push(StepRecord {
                    stage: st.name.clone(),
                    step: "s".into(),
                    status: if ok { "passed" } else { "failed" }.into(),
                    duration_ms: 1,
                    log: None,
                });
                if !ok {
                    return Ok(RunRecord { run_id: run_id.into(), pipeline: p.name.clone(), green: false, steps, reason: Some(format!("{} failed", st.name)) });
                }
            }
        }
        Ok(RunRecord { run_id: run_id.into(), pipeline: p.name.clone(), green: true, steps, reason: None })
    }
}

fn opts(run_dir: &Path, from: &Path) -> RunOptions {
    RunOptions {
        run_dir: run_dir.into(),
        from: Some(from.into()),
        jobs: 2,
        rebuild_published: false,
        keep: false,
        run_id: Some("heimdall-guests-0.2.1-test".into()),
        builder_version: BTreeMap::new(),
    }
}

#[test]
fn a_release_of_the_guests_has_the_example_outputs_as_its_subjects() {
    let d = tempfile::tempdir().unwrap();
    let from = d.path().join("from");
    let commit = repo(&from);
    let run_dir = d.path().join("run");
    let s = ReleaseSpec::parse(&spec_text(&commit, "")).unwrap();
    let st = runner::run(&s, &opts(&run_dir, &from), &StandIn { leave_out: None }).unwrap();
    let v: serde_json::Value = serde_json::from_slice(&std::fs::read(&st).unwrap()).unwrap();
    let names: BTreeSet<&str> = v["subject"].as_array().unwrap().iter().map(|x| x["name"].as_str().unwrap()).collect();
    assert_eq!(names, OUTPUTS.into_iter().collect::<BTreeSet<_>>());
    for x in v["subject"].as_array().unwrap() {
        let f = run_dir.join("out").join(x["name"].as_str().unwrap());
        assert_eq!(x["digest"]["sha256"], dwarves_slsa::subjects::sha256_file(&f).unwrap());
    }
    let pred = &v["predicate"]["buildDefinition"];
    assert_eq!(pred["externalParameters"]["workspaceExamples"][0]["example"], "dist");
    // both locks are recorded
    let res = pred["resolvedDependencies"].as_array().unwrap();
    for lock in ["heimdall/Cargo.lock", "heimdall/guests/verify-bundle/Cargo.lock"] {
        assert!(res.iter().any(|r| r["name"] == lock), "{lock} recorded");
    }
    assert!(!run_dir.join("example-out").exists(), "the example's scratch output is cleaned like target/");
}

#[test]
fn a_release_whose_example_left_out_an_output_is_red_and_writes_no_statement() {
    let d = tempfile::tempdir().unwrap();
    let from = d.path().join("from");
    let commit = repo(&from);
    let run_dir = d.path().join("run");
    let s = ReleaseSpec::parse(&spec_text(&commit, "")).unwrap();
    let e = runner::run(&s, &opts(&run_dir, &from), &StandIn { leave_out: Some(OUTPUTS[4]) }).unwrap_err();
    assert!(e.0.contains("red"), "{e}");
    assert!(!run_dir.join("release.statement.json").exists());
}