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

nordisk/heimdall

crates/heimdall-cli/tests/trace_cli.rs

// SPDX-License-Identifier: AGPL-3.0-only
//! `heimdall trace diff|verify`: exit codes and output. Heimdall is a
//! defensive sandbox; `heimdall trace run` needs a kernel and runs in
//! crates/heimdall-trace/tests/kernel.rs's setup (scripts/trace-tests.sh).

use std::path::{Path, PathBuf};
use std::process::Command;

use heimdall_core::*;
use heimdall_trace::record::Recorder;
use heimdall_trace::*;

fn bin() -> Command {
    Command::new(env!("CARGO_BIN_EXE_heimdall"))
}

fn scratch() -> PathBuf {
    let d = std::env::temp_dir().join(format!("heimdall-cli-trace-{}", JobId::random()));
    std::fs::create_dir_all(&d).unwrap();
    d
}

fn trace(connect: Option<&str>) -> Vec<u8> {
    let mut r = Recorder::new(vec!["/usr/bin/python3".into()], 1 << 20);
    if let Some(a) = connect {
        r.net(NetEvent {
            program: "/usr/bin/python3.13".into(),
            op: "connect".into(),
            family: "inet".into(),
            address: a.into(),
            refused: true,
        });
    }
    r.trace_mut().exit = "exit 0".into();
    r.finish().1
}

fn write(d: &Path, n: &str, b: &[u8]) -> PathBuf {
    let p = d.join(n);
    std::fs::write(&p, b).unwrap();
    p
}

#[test]
fn trace_diff_exits_0_on_the_same_behaviour_and_1_on_a_new_connect() {
    let d = scratch();
    let base = write(&d, "base.json", &trace(None));
    let same = write(&d, "same.json", &trace(None));
    let evil = write(&d, "evil.json", &trace(Some("10.0.0.1:443")));
    let o = bin()
        .args(["trace", "diff"])
        .arg(&base)
        .arg(&same)
        .output()
        .unwrap();
    assert_eq!(
        o.status.code(),
        Some(0),
        "{}",
        String::from_utf8_lossy(&o.stderr)
    );
    let o = bin()
        .args(["trace", "diff"])
        .arg(&base)
        .arg(&evil)
        .output()
        .unwrap();
    assert_eq!(o.status.code(), Some(1));
    assert!(String::from_utf8_lossy(&o.stdout).contains("10.0.0.1:443"));
    let bad = write(&d, "bad.json", b"{\"format\": 1}");
    let o = bin()
        .args(["trace", "diff"])
        .arg(&base)
        .arg(&bad)
        .output()
        .unwrap();
    assert_eq!(o.status.code(), Some(3));
    let _ = std::fs::remove_dir_all(&d);
}

#[test]
fn trace_verify_exits_0_on_a_signed_trace_and_1_when_one_bit_of_it_flips() {
    let d = scratch();
    let out = d.join("out");
    std::fs::create_dir_all(&out).unwrap();
    let t = trace(Some("10.0.0.1:443"));
    std::fs::write(out.join(TRACE_FILE), &t).unwrap();
    let key = BootKey::generate();
    let result = JobResult {
        job_id: JobId::random(),
        nonce: Nonce::random(),
        verdict: Verdict::Accepted,
        outputs: vec![Output::of(TRACE_FILE, &t)],
        exit: ExitInfo::exited(0),
        started_unix: 1,
        finished_unix: 2,
        runner: Features {
            runner: "heimdall".into(),
            container: true,
            seccomp: Protection::Default,
            landlock: true,
            broker: false,
            wasm: false,
            image_verified_each_start: true,
        },
        events: vec![],
        kernel: None,
    };
    let signed = key.sign(result).unwrap();
    let res = write(&d, "result.signed", &signed.to_bytes());
    let pin = key.pinned().to_text();
    let run = || {
        bin()
            .args(["trace", "verify"])
            .arg(&res)
            .args(["--key", &pin, "--files"])
            .arg(&out)
            .output()
            .unwrap()
    };
    let o = run();
    assert_eq!(
        o.status.code(),
        Some(0),
        "{}",
        String::from_utf8_lossy(&o.stdout)
    );
    assert!(String::from_utf8_lossy(&o.stdout).contains("\"traced\""));
    let mut f = t.clone();
    f[10] ^= 0x01;
    std::fs::write(out.join(TRACE_FILE), &f).unwrap();
    let o = run();
    assert_eq!(o.status.code(), Some(1));
    assert!(String::from_utf8_lossy(&o.stdout).contains("\"unknown\""));
    let _ = std::fs::remove_dir_all(&d);
}