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);
}