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

nordisk/holger

server/lib/tests/bundle_import_runs_in_a_job.rs

//! **Every bundle that comes in is verified in a job, never in the server** (lane
//! HOL-SANDBOX-TRAIT, owner 2026-10-30).
//!
//! The import door (the inbox sweep, the HTTP and gRPC doors and the CLI all run it)
//! links the bundle into a `verify-bundle` job's input, runs the job through the
//! server's ONE runner, believes only the signed result (the pinned key, its own job
//! id, the listed outputs), and reads back only the verified files the job wrote.
//! Here the runner is the in-process test runner (`sandbox: off`, a test build),
//! wrapped by spies and forgers:
//!
//! * a good bundle imports through the job, which ran `verify-bundle`;
//! * a tampered one is refused by the job, with its reason in the audit chain;
//! * a malformed one, or a job that crashes, fails only that job: the next import works;
//! * the server process never opens the bundle file (inotify on the file's inode:
//!   every open happens inside the job);
//! * a forged or a replayed signed result is refused, and nothing lands.

use std::io::Write as _;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};

use holger_server_lib::import_door::ImportDoor;
use holger_server_lib::job_runner::heimdall_core::{
    Features, Job, JobRunner, RunError, SignedResult, VerifyingKey,
};
use holger_server_lib::job_runner::holger_job_runner::wire::{Answer, Dirs, WireJob};
use holger_server_lib::job_runner::holger_job_runner::InProcessRunner;
use holger_server_lib::job_runner::{holger_sandbox, programs, Jobs, SandboxRule};
use holger_server_lib::{read_ron_config, wire_holger, Holger};
use rcgen::{CertificateParams, DnType, KeyPair, PKCS_ED25519};
use update_bundle::core::{PolicyResult, SourceProvenance};
use update_bundle::digest::sha256_hex;
use update_bundle::policy::{
    ApprovedSigner, CertificateValidity, ExpiredPosture, TrustMode, TrustPolicy,
};
use update_bundle::prepare::{PayloadInput, Preparation};
use update_bundle::schema::POLICY_SCHEMA;
use update_bundle::verify::TargetSpec;

// ───────────────────────── signers, policy, bundles ─────────────────────────

struct Signer {
    cert: Vec<u8>,
    key: Vec<u8>,
    fingerprint: String,
}

fn mint(cn: &str) -> Signer {
    let key = KeyPair::generate_for(&PKCS_ED25519).unwrap();
    let mut params = CertificateParams::new(vec![]).unwrap();
    params.distinguished_name.push(DnType::CommonName, cn);
    let cert = params.self_signed(&key).unwrap();
    Signer {
        cert: cert.der().to_vec(),
        key: key.serialize_der(),
        fingerprint: sha256_hex(cert.der()),
    }
}

struct Pki {
    reviewer: Signer,
    owner: Signer,
    release: Signer,
}

impl Pki {
    fn new() -> Self {
        Pki {
            reviewer: mint("security-reviewer"),
            owner: mint("system-owner"),
            release: mint("release"),
        }
    }

    fn policy(&self) -> TrustPolicy {
        TrustPolicy {
            schema_version: POLICY_SCHEMA.into(),
            trust_mode: Some(TrustMode::PinnedSigners),
            trusted_roots: vec![],
            certificate_validity: CertificateValidity::Enforce,
            approved_signers: vec![
                ApprovedSigner {
                    fingerprint: self.reviewer.fingerprint.clone(),
                    subject: None,
                    roles: vec!["security-reviewer".into()],
                },
                ApprovedSigner {
                    fingerprint: self.owner.fingerprint.clone(),
                    subject: None,
                    roles: vec!["system-owner".into()],
                },
            ],
            required_approval_roles: vec!["security-reviewer".into(), "system-owner".into()],
            minimum_distinct_approvers: 2,
            trusted_release_signers: vec![self.release.fingerprint.clone()],
            release_signer_must_be_distinct: true,
            maximum_bundle_age_secs: None,
            require_expiry: true,
            on_expired_metadata: Some(ExpiredPosture::Refuse),
            require_sbom: false,
            require_vex: false,
            require_scan: false,
            allowed_products: vec!["ExampleDrive".into()],
            allowed_models: vec![],
        }
    }
}

/// A signed update bundle (not an airgap export) carrying one firmware payload.
fn bundle(pki: &Pki, dir: &Path, sequence: u64) -> PathBuf {
    let now = chrono::Utc::now();
    let payload: Vec<u8> = (0..4096u32)
        .map(|i| (i.wrapping_mul(2654435761) >> 13) as u8 ^ sequence as u8)
        .collect();
    let prep = Preparation {
        bundle_id: format!("fw-{sequence}"),
        sequence,
        created_at: now,
        expires_at: Some(now + chrono::Duration::days(30)),
        producer: "intake-station".into(),
        supplier: "ExampleCorp".into(),
        product: "ExampleDrive".into(),
        release_version: format!("1.2.{sequence}"),
        supported_hardware_models: vec![],
        supported_hardware_revisions: vec![],
        source_provenance: SourceProvenance {
            source_kind: "vendor-portal".into(),
            source_uri: None,
            source_repository: None,
            source_asset_path: None,
            fetched_at: Some(now),
            reported_size: Some(payload.len() as u64),
            vendor_checksum: None,
            vendor_signature_status: None,
            tls_origin: None,
            transfer_receipt_digest: None,
        },
        policy_result: PolicyResult {
            policy_digest: sha256_hex(b"trust-policy-v1"),
            advisory_snapshot_digest: None,
            verdict: "pass".into(),
            non_production: true,
        },
        supersedes: vec![],
        withdraws: vec![],
        payloads: vec![PayloadInput {
            relative_path: "firmware.bin".into(),
            bytes: payload,
            media_type: "application/octet-stream".into(),
        }],
        evidence: vec![],
    };
    let mut members = prep.build().unwrap().members;
    update_bundle::finalize::add_approval(
        &mut members,
        "security-reviewer",
        &pki.reviewer.cert,
        &pki.reviewer.key,
        None,
        now,
    )
    .unwrap();
    update_bundle::finalize::add_approval(
        &mut members,
        "system-owner",
        &pki.owner.cert,
        &pki.owner.key,
        None,
        now,
    )
    .unwrap();
    update_bundle::finalize::finalize(
        &mut members,
        &pki.policy(),
        &pki.release.cert,
        &pki.release.key,
        now,
    )
    .unwrap();
    let out = dir.join(format!("fw-{sequence}.holger.znippy"));
    update_bundle::container::pack_bundle(&members, &out).unwrap();
    out
}

/// One payload byte changed, every signature as it was.
fn tampered(path: &Path) -> PathBuf {
    let mut members = update_bundle::container::load_bundle(path).unwrap();
    let payload = members
        .keys()
        .find(|k| k.starts_with("payload/"))
        .cloned()
        .unwrap();
    members.get_mut(&payload).unwrap()[0] ^= 0x01;
    let out = path.with_extension("tampered.znippy");
    update_bundle::container::pack_bundle(&members, &out).unwrap();
    out
}

// ───────────────────────── the server ─────────────────────────

struct Site {
    dir: tempfile::TempDir,
    data: PathBuf,
    pki: Pki,
    holger: Holger,
    _cfg: tempfile::NamedTempFile,
}

fn site() -> Site {
    let dir = tempfile::tempdir().unwrap();
    let pki = Pki::new();
    let policy = dir.path().join("bundle-trust.ron");
    std::fs::write(&policy, pki.policy().to_ron().unwrap()).unwrap();
    let data = dir.path().join("data");
    std::fs::create_dir_all(&data).unwrap();
    let d = data.display().to_string();
    let repo = |name: &str| {
        format!(
            r#"(ron_name: "{name}", ron_repo_type: "raw", ron_upstreams: [], ron_in: None,
         ron_out: Some((ron_storage_endpoint: "storage", ron_exposed_endpoint: "door")),
         ron_data_dir: Some("{d}/{name}")),"#
        )
    };
    let ron = format!(
        r#"(
    repositories: [ {} {} {} {} ],
    exposed_endpoints: [ (ron_name: "door", ron_url: "127.0.0.1:1") ],
    storage_endpoints: [ (ron_name: "storage", ron_storage_type: "znippy", ron_path: "{d}") ],
    audit: (dir: "{d}/audit", enabled: true),
    promotion: Some((
        ladder_name: "intake",
        stages: [
            (name: "incoming", repo: "incoming"),
            (name: "quarantine", repo: "quarantine"),
            (name: "approved", repo: "approved"),
            (name: "production", repo: "production"),
        ],
        intake: Some((rules: (sandbox: off),
            incoming: "incoming", quarantine: "quarantine", approved: "approved", production: "production",
            trust_policy: Some("{}"),
        )),
    )),
)"#,
        repo("incoming"),
        repo("quarantine"),
        repo("approved"),
        repo("production"),
        policy.display(),
    );
    let mut cfg = tempfile::NamedTempFile::new().unwrap();
    cfg.write_all(ron.as_bytes()).unwrap();
    let mut holger: Holger = read_ron_config(cfg.path()).unwrap();
    holger.instantiate_backends().unwrap();
    wire_holger(&mut holger).unwrap();
    Site {
        dir,
        data,
        pki,
        holger,
        _cfg: cfg,
    }
}

impl Site {
    /// The door over `runner`, with the admin's first-import grant for `sequence`.
    fn door(&self, runner: Arc<dyn JobRunner>, sequence: u64) -> ImportDoor {
        let door = ImportDoor::of(&self.holger)
            .unwrap()
            .with_jobs(Arc::new(Jobs::new(runner)));
        door.allow_first_import("ExampleCorp", "ExampleDrive", sequence, "adm", "test")
            .unwrap();
        door
    }

    fn audit_text(&self) -> String {
        let mut out = String::new();
        for e in std::fs::read_dir(self.data.join("audit"))
            .unwrap()
            .flatten()
        {
            out.push_str(&std::fs::read_to_string(e.path()).unwrap_or_default());
        }
        out
    }

    fn landed(&self) -> usize {
        walk(&self.data.join("holger-prod-store").join("lineage")).len()
    }
}

fn walk(dir: &Path) -> Vec<PathBuf> {
    let mut out = Vec::new();
    let mut stack = vec![dir.to_path_buf()];
    while let Some(d) = stack.pop() {
        for e in std::fs::read_dir(&d).into_iter().flatten().flatten() {
            if e.path().is_dir() {
                stack.push(e.path());
            } else {
                out.push(e.path());
            }
        }
    }
    out
}

fn test_runner() -> Arc<InProcessRunner> {
    Arc::new(InProcessRunner::new(Arc::new(programs::run)))
}

fn import(
    door: &ImportDoor,
    bundle: &Path,
) -> Result<
    holger_server_lib::import_door::BundleImported,
    holger_server_lib::import_door::ImportError,
> {
    door.import_bundle(bundle, None, &TargetSpec::default(), "imp", "test")
}

// ───────────────────────── spies and forgers ─────────────────────────

/// The real runner, and what it was asked to run.
struct Spy {
    inner: Arc<dyn JobRunner>,
    jobs: Mutex<Vec<Job>>,
}

impl JobRunner for Spy {
    fn run(&self, job: Job) -> Result<SignedResult, RunError> {
        self.jobs.lock().unwrap().push(job.clone());
        self.inner.run(job)
    }
    fn verifying_key(&self) -> VerifyingKey {
        self.inner.verifying_key()
    }
    fn features(&self) -> Features {
        self.inner.features()
    }
}

#[test]
fn a_good_bundle_imports_through_the_verify_bundle_job() {
    let s = site();
    let spy = Arc::new(Spy {
        inner: test_runner(),
        jobs: Mutex::new(Vec::new()),
    });
    let door = s.door(spy.clone(), 1);
    let b = bundle(&s.pki, s.dir.path(), 1);
    let r = import(&door, &b).unwrap();
    assert_eq!(r.outcome, "new");
    assert_eq!(r.artifacts.len(), 1, "{r:?}");
    let jobs = spy.jobs.lock().unwrap();
    assert_eq!(jobs.len(), 1, "one job per bundle");
    match &jobs[0].kind {
        holger_server_lib::job_runner::heimdall_core::JobKind::Native { argv, .. } => {
            assert_eq!(argv, &["verify-bundle"])
        }
        k => panic!("{k:?}"),
    }
    assert!(jobs[0].policy.net.is_none(), "a verify job has no network");
    assert!(s.landed() > 0);
    assert!(
        !s.data
            .join(holger_server_lib::job_runner::JOBS_DIR)
            .join(jobs[0].id.to_hex())
            .exists(),
        "the job's directories are removed"
    );
}

#[test]
fn a_tampered_bundle_is_refused_by_the_job_and_the_audit_chain_says_why() {
    let s = site();
    let door = s.door(test_runner(), 1);
    let bad = tampered(&bundle(&s.pki, s.dir.path(), 1));
    let e = import(&door, &bad).unwrap_err();
    assert!(e.message.contains("bundle verification failed"), "{e}");
    assert_eq!(s.landed(), 0, "nothing lands");
    let audit = s.audit_text();
    assert!(
        audit.contains("verify-bundle job") && audit.contains("bundle verification failed"),
        "the refusal and its reason are audited:\n{audit}"
    );
}

#[test]
fn a_malformed_bundle_or_a_crashed_job_fails_only_that_job() {
    let s = site();
    let garbage = s.dir.path().join("garbage.holger.znippy");
    std::fs::write(&garbage, b"ZNIPPY\x00\x01 this is not an archive at all").unwrap();
    let door = s.door(test_runner(), 1);
    let e = import(&door, &garbage).unwrap_err();
    assert!(e.message.contains("bundle verification failed"), "{e}");

    let crashing: Arc<dyn JobRunner> = Arc::new(InProcessRunner::new(Arc::new(
        |_: &WireJob, _: &Dirs| -> Answer { panic!("the index parser fell over") },
    )));
    let crashed = ImportDoor::of(&s.holger)
        .unwrap()
        .with_jobs(Arc::new(Jobs::new(crashing)));
    let e = import(&crashed, &bundle(&s.pki, s.dir.path(), 1)).unwrap_err();
    assert_eq!(e.code.code, "holger-sandbox-9", "{e}");
    assert!(s.audit_text().contains("the index parser fell over"));

    // The server is still up: the next bundle imports.
    let r = import(&door, &bundle(&s.pki, s.dir.path(), 1)).unwrap();
    assert_eq!(r.outcome, "new");
}

/// The real runner, counting every open of one file's inode outside its jobs.
struct Watched {
    inner: Arc<dyn JobRunner>,
    fd: i32,
    outside: AtomicUsize,
    inside: AtomicUsize,
}

impl Watched {
    fn new(inner: Arc<dyn JobRunner>, file: &Path) -> Self {
        use std::os::unix::ffi::OsStrExt as _;
        // SAFETY: plain inotify syscalls on a descriptor this test owns.
        let fd = unsafe { libc::inotify_init1(libc::IN_NONBLOCK | libc::IN_CLOEXEC) };
        assert!(fd >= 0);
        let c = std::ffi::CString::new(file.as_os_str().as_bytes()).unwrap();
        // SAFETY: as above; `c` outlives the call.
        let wd =
            unsafe { libc::inotify_add_watch(fd, c.as_ptr(), libc::IN_OPEN | libc::IN_ACCESS) };
        assert!(wd >= 0);
        Self {
            inner,
            fd,
            outside: AtomicUsize::new(0),
            inside: AtomicUsize::new(0),
        }
    }

    /// How many events arrived since the last look.
    fn drain(&self) -> usize {
        let mut n = 0;
        let mut buf = [0u8; 4096];
        loop {
            // SAFETY: reading into a buffer this test owns.
            let got = unsafe { libc::read(self.fd, buf.as_mut_ptr().cast(), buf.len()) };
            if got <= 0 {
                return n;
            }
            let mut at = 0usize;
            while at + 16 <= got as usize {
                let len = u32::from_ne_bytes(buf[at + 12..at + 16].try_into().unwrap()) as usize;
                n += 1;
                at += 16 + len;
            }
        }
    }
}

impl JobRunner for Watched {
    fn run(&self, job: Job) -> Result<SignedResult, RunError> {
        self.outside.fetch_add(self.drain(), Ordering::SeqCst);
        let r = self.inner.run(job);
        self.inside.fetch_add(self.drain(), Ordering::SeqCst);
        r
    }
    fn verifying_key(&self) -> VerifyingKey {
        self.inner.verifying_key()
    }
    fn features(&self) -> Features {
        self.inner.features()
    }
}

#[test]
fn the_server_never_opens_the_bundle_file() {
    let s = site();
    let b = bundle(&s.pki, s.dir.path(), 1);
    let w = Arc::new(Watched::new(test_runner(), &b));
    let door = s.door(w.clone(), 1);
    import(&door, &b).unwrap();
    w.outside.fetch_add(w.drain(), Ordering::SeqCst);
    assert!(
        w.inside.load(Ordering::SeqCst) > 0,
        "the job read the bundle (the watch is not blind)"
    );
    assert_eq!(
        w.outside.load(Ordering::SeqCst),
        0,
        "the server process opened or read the bundle outside its job"
    );
}

/// The real runner, its results tampered with or replayed.
struct Forger {
    inner: Arc<dyn JobRunner>,
    replay: bool,
    first: Mutex<Option<SignedResult>>,
}

impl JobRunner for Forger {
    fn run(&self, job: Job) -> Result<SignedResult, RunError> {
        let mut r = self.inner.run(job)?;
        if self.replay {
            let mut first = self.first.lock().unwrap();
            match first.as_ref() {
                None => *first = Some(r.clone()),
                Some(old) => r = old.clone(),
            }
        } else {
            // Claim a refused bundle was accepted.
            r.result.verdict = holger_server_lib::job_runner::heimdall_core::Verdict::Accepted;
            r.result.exit.code = Some(0);
        }
        Ok(r)
    }
    fn verifying_key(&self) -> VerifyingKey {
        self.inner.verifying_key()
    }
    fn features(&self) -> Features {
        self.inner.features()
    }
}

#[test]
fn a_forged_or_a_replayed_result_is_refused_and_nothing_lands() {
    let s = site();
    let forger = Arc::new(Forger {
        inner: test_runner(),
        replay: false,
        first: Mutex::new(None),
    });
    let door = s.door(forger, 1);
    let e = import(&door, &tampered(&bundle(&s.pki, s.dir.path(), 1))).unwrap_err();
    assert_eq!(e.code.code, "holger-sandbox-18", "{e}");
    assert_eq!(s.landed(), 0);

    let replayer = Arc::new(Forger {
        inner: test_runner(),
        replay: true,
        first: Mutex::new(None),
    });
    let door = s.door(replayer, 1);
    let good = bundle(&s.pki, s.dir.path(), 1);
    // The first job's genuine result, kept; the import stops at its floor check
    // only after the job, so run it once and then offer that result again.
    let _ = import(&door, &good);
    let landed = s.landed();
    let e = import(&door, &bundle(&s.pki, s.dir.path(), 2)).unwrap_err();
    assert_eq!(e.code.code, "holger-sandbox-18", "{e}");
    assert!(e.message.contains("replayed"), "{e}");
    assert_eq!(s.landed(), landed, "nothing more lands");
}

#[test]
fn the_configured_runner_says_it_is_the_interim_one() {
    let j = Jobs::from_config(SandboxRule::Required, None).unwrap();
    let f = j.features();
    assert_eq!(f.runner, "holger-podman");
    assert!(!f.broker && !f.landlock && !f.wasm);
    assert!(j.is_interim());
    assert!(
        j.words().contains("interim runner, not Heimdall") && j.words().contains("broker"),
        "{}",
        j.words()
    );
}

#[test]
fn a_one_way_box_is_warned_that_its_runner_is_the_interim_one() {
    let mut s = site();
    let j = Jobs::from_config(SandboxRule::Required, None).unwrap();
    s.holger.boundary = Some(holger_server_lib::boundary::Boundary::OneWay);
    let w = s
        .holger
        .interim_runner_warning(&j)
        .expect("strict CDS warns");
    assert!(
        w.contains("strict CDS (boundary: OneWay)")
            && w.contains("not Heimdall")
            && w.contains("broker"),
        "{w}"
    );
    s.holger.boundary = Some(holger_server_lib::boundary::Boundary::Fetch);
    assert!(
        s.holger.interim_runner_warning(&j).is_none(),
        "a fetching box is not strict CDS"
    );
}

// ── engine: heimdall (lane HOL-HEIMDALL-02) ─────────────────────────────────────────

/// `sandbox: (engine: heimdall, heimdall: (…))` in `dir`: the worker image minted from a
/// worker rootfs tar, the shims at `shim` / `plain_shim`, crun's rootfs when `crun`.
fn heimdall_cfg(
    dir: &Path,
    shim: Option<&Path>,
    plain_shim: Option<&Path>,
    crun: bool,
) -> holger_sandbox::SandboxConfig {
    let tar = dir.join("holger-scan-worker.rootfs.tar");
    std::fs::write(&tar, holger_sandbox::media::worker_rootfs_tar(b"#!worker")).unwrap();
    holger_sandbox::SandboxConfig {
        engine: holger_sandbox::EngineKind::Heimdall,
        rootfs: crun.then(|| dir.join("rootfs").display().to_string()),
        work_dir: Some(dir.join("work").display().to_string()),
        heimdall: Some(holger_sandbox::HeimdallSettings {
            image_layout: dir.join("oci").display().to_string(),
            rootfs_tar: Some(tar.display().to_string()),
            shim: shim.map(|p| p.display().to_string()),
            plain_shim: plain_shim.map(|p| p.display().to_string()),
            work_dir: Some(dir.join("heimdall").display().to_string()),
            ..Default::default()
        }),
        ..Default::default()
    }
}

#[test]
fn engine_heimdall_without_its_block_is_refused_by_name() {
    let cfg = holger_sandbox::SandboxConfig {
        engine: holger_sandbox::EngineKind::Heimdall,
        ..Default::default()
    };
    let e = Jobs::from_config(SandboxRule::Required, Some(cfg)).unwrap_err();
    assert_eq!(e.code().code, "holger-sandbox-10", "{e}");
    assert!(e.to_string().contains("`heimdall:` block"), "{e}");
}

#[cfg(not(feature = "heimdall"))]
#[test]
fn a_build_without_heimdall_falls_back_to_crun_by_name_or_refuses_engine_heimdall() {
    let dir = tempfile::tempdir().unwrap();
    let j = Jobs::from_config(
        SandboxRule::Required,
        Some(heimdall_cfg(dir.path(), None, None, true)),
    )
    .unwrap();
    assert_eq!(j.features().runner, "holger-crun");
    let why = j.fallback().expect("said by name");
    assert!(
        why.contains("feature `heimdall`") && why.contains("interim crun runner"),
        "{why}"
    );
    let dir = tempfile::tempdir().unwrap();
    let e = Jobs::from_config(
        SandboxRule::Required,
        Some(heimdall_cfg(dir.path(), None, None, false)),
    )
    .unwrap_err();
    assert_eq!(e.code().code, "holger-sandbox-10", "{e}");
    assert!(e.to_string().contains("feature `heimdall`"), "{e}");
}

/// Where Heimdall cannot start (here: its shim is not there), the jobs run in youki-plain
/// when its shim is, else in crun over `rootfs`, else the boot is refused; every fallback
/// is said by name, the runner's features say what it is, and strict CDS warns.
#[cfg(feature = "heimdall")]
#[test]
fn engine_heimdall_falls_back_by_name_to_youki_plain_then_crun_then_refuses() {
    let dir = tempfile::tempdir().unwrap();
    let missing = dir.path().join("no-heimdall-container-shim");
    let plain = dir.path().join("heimdall-plain-shim");
    std::fs::write(&plain, b"#!/bin/false\n").unwrap();

    let j = Jobs::from_config(
        SandboxRule::Required,
        Some(heimdall_cfg(dir.path(), Some(&missing), Some(&plain), true)),
    )
    .unwrap();
    assert_eq!(j.features().runner, "youki-plain");
    assert!(j.is_interim());
    let why = j.fallback().expect("the fallback is said");
    assert!(
        why.contains("no-heimdall-container-shim") && why.contains("youki-plain, not Heimdall"),
        "{why}"
    );
    assert!(j.words().contains(why), "{}", j.words());
    assert!(
        dir.path().join("oci/index.json").is_file(),
        "the worker image was minted from the rootfs tar"
    );
    let mut s = site();
    s.holger.boundary = Some(holger_server_lib::boundary::Boundary::OneWay);
    let w = s
        .holger
        .interim_runner_warning(&j)
        .expect("strict CDS warns");
    assert!(
        w.contains("youki-plain") && w.contains("engine: heimdall"),
        "{w}"
    );

    let dir = tempfile::tempdir().unwrap();
    let missing = dir.path().join("no-shim");
    let j = Jobs::from_config(
        SandboxRule::Required,
        Some(heimdall_cfg(
            dir.path(),
            Some(&missing),
            Some(&missing),
            true,
        )),
    )
    .unwrap();
    assert_eq!(j.features().runner, "holger-crun");
    let why = j.fallback().unwrap();
    assert!(
        why.contains("youki-plain shim") && why.contains("interim crun runner"),
        "{why}"
    );

    let dir = tempfile::tempdir().unwrap();
    let missing = dir.path().join("no-shim");
    let e = Jobs::from_config(
        SandboxRule::Required,
        Some(heimdall_cfg(
            dir.path(),
            Some(&missing),
            Some(&missing),
            false,
        )),
    )
    .unwrap_err();
    assert_eq!(e.code().code, "holger-sandbox-1", "{e}");
    assert!(e.to_string().contains("no `rootfs`"), "{e}");
}

// ── the released split guest at the door (lane HOL-HEIMDALL-04) ────────────────────

#[cfg(feature = "heimdall")]
fn repo() -> PathBuf {
    Path::new(env!("CARGO_MANIFEST_DIR")).join("../..")
}

/// The vendored release and the korp root, as the box lays and stages them.
#[cfg(feature = "heimdall")]
fn released_guests() -> holger_server_lib::job_runner::VerifyGuests {
    holger_server_lib::job_runner::VerifyGuests::check(
        &repo().join("guests/heimdall/0.2.1"),
        &repo().join("holger-recipe/trust/korp-root-ca.pem"),
    )
    .expect("the vendored guests are the pinned ones")
}

/// With the guests laid and pinned, the door's verify-bundle job names them: its parameters
/// say `guest`, its one read path is the release, its input carries the korp root; the job
/// verifies the bundle in the split guest and the door lands it.
#[cfg(all(feature = "heimdall", feature = "heimdall-wasm"))]
#[test]
fn the_door_verifies_a_bundle_in_the_released_split_guest() {
    let s = site();
    let spy = Arc::new(Spy {
        inner: test_runner(),
        jobs: Mutex::new(Vec::new()),
    });
    let door = ImportDoor::of(&s.holger).unwrap().with_jobs(Arc::new(
        Jobs::new(spy.clone()).with_guests(Ok(released_guests())),
    ));
    door.allow_first_import("ExampleCorp", "ExampleDrive", 1, "adm", "test")
        .unwrap();
    let b = bundle(&s.pki, s.dir.path(), 1);
    let r = import(&door, &b).unwrap();
    assert_eq!(r.outcome, "new");
    assert!(s.landed() > 0);
    let jobs = spy.jobs.lock().unwrap();
    assert_eq!(
        jobs[0].policy.read,
        vec![repo().join("guests/heimdall/0.2.1")]
    );
    assert_eq!(jobs[0].params["guest"], serde_json::json!({"read": 0}));
    drop(jobs);
    // A tampered bundle is the guest's refusal, with its reason in the audit chain.
    let t = tampered(&bundle(&s.pki, s.dir.path(), 2));
    let e = import(&door, &t).unwrap_err();
    assert_eq!(e.status, 422, "{e}");
    assert!(
        s.audit_text().contains("verify-bundle job"),
        "the refusal is audited"
    );
}

/// Guests that are not the pinned ones refuse every bundle by name, before any job runs.
#[test]
fn guests_that_are_not_the_pinned_ones_refuse_every_import_by_name() {
    let s = site();
    let spy = Arc::new(Spy {
        inner: test_runner(),
        jobs: Mutex::new(Vec::new()),
    });
    let door = ImportDoor::of(&s.holger)
        .unwrap()
        .with_jobs(Arc::new(Jobs::new(spy.clone()).with_guests(Err(
            "heimdall-verify-bundle-decoder.wasm: sha256 00".into(),
        ))));
    door.allow_first_import("ExampleCorp", "ExampleDrive", 1, "adm", "test")
        .unwrap();
    let e = import(&door, &bundle(&s.pki, s.dir.path(), 1)).unwrap_err();
    assert_eq!((e.code.code, e.status), ("holger-sandbox-20", 503), "{e}");
    assert!(e.message.contains("not the pinned ones"), "{e}");
    assert!(spy.jobs.lock().unwrap().is_empty(), "no job ran");
    assert_eq!(s.landed(), 0);
}