server/lib/tests/the_estate_declaration_runs_the_provenance_scanner.rs
//! **The declaration every Holger estate ships runs the provenance scanner** (lane
//! HOL-MVP-FORMATS, 2026-10-30: test2.holger.rs's Security page said "Provenance: off, Not
//! installed").
//!
//! The provenance scanner (`holger-provenance`: cosign keyless, DSSE and SLSA attestations,
//! PEP 740, npm attestations, verified offline) is built into every estate binary
//! (`supply-chain`), but the intake chain installs it only when the declaration's
//! `promotion.intake.scan` names a `provenance` block, and the estate declaration named none.
//! It now names one with `default: prefer`: every package's provenance material is verified
//! and recorded, and a package without any is not refused for that (the floor decides; a
//! source may still `require`). The server boots holger-recipe's `estate_ron::medium()` as the
//! box does, and its intake chain reports the provenance slot installed.
use std::io::Write;
use holger_recipe::estate_ron;
use holger_server_lib::scanner::ScannerKind;
use holger_server_lib::{read_ron_config, wire_holger, Holger};
use traits::test_support::hold_port;
const ESTATE: &str = "test3.holger.rs";
const CONSOLE_TOKEN: &str = "console-bearer-of-sufficient-length-0123456789";
struct Running {
_data: tempfile::TempDir,
_cfg: tempfile::NamedTempFile,
holger: Holger,
}
fn start() -> Running {
let mut last = String::new();
for _ in 0..8 {
let (grpc, http) = (hold_port(), hold_port());
match try_start(grpc, http) {
Ok(r) => return r,
Err(e) => last = e,
}
}
panic!("server never started; last error: {last}");
}
/// The intel database a box gets by import, installed by hand: no advisory for
/// the demo wheel, a fresh snapshot.
fn install_intel(dir: &std::path::Path) {
use holger_intel::chrono::Utc;
use holger_intel::{AdvisorySet, InstalledIntel, LocalIntelDb};
let set = AdvisorySet {
schema: holger_intel::set::SET_SCHEMA.into(),
generated_at: Utc::now(),
sources: vec![],
advisories: vec![],
};
let db = LocalIntelDb::open(dir);
let bytes = set.to_json_bytes().unwrap();
db.stage_generation(1, &bytes).unwrap();
db.activate(&InstalledIntel {
schema: holger_intel::db::INSTALLED_SCHEMA.into(),
bundle_id: "holger-intel-1".into(),
sequence: 1,
supplier: holger_intel::DEFAULT_SUPPLIER.into(),
snapshot_at: Utc::now(),
imported_at: Utc::now(),
candidate_digest: "00".into(),
release_signer_fingerprint: "00".into(),
advisories_file: LocalIntelDb::generation_file(1),
advisories_sha256: holger_server_lib::security::blob_digest_hex(&bytes),
advisories: 0,
})
.unwrap();
}
fn try_start(grpc: u16, http: u16) -> Result<Running, String> {
let data = tempfile::tempdir().unwrap();
let d = data.path().display().to_string();
// The medium's own file, its paths moved under the temporary directory.
let medium = estate_ron::medium();
let ron = medium
.replace(
&format!("\"{}/", holger_recipe::packaging::BOX_HOLGER_DATA),
&format!("\"{d}/holger/"),
)
.replace(
&format!("\"{}/", estate_ron::BOX_APPLIANCE_ROOT),
&format!("\"{d}/"),
)
.replace(
&format!("\"{}/", estate_ron::RELEASE_ROOT),
&format!("\"{d}/"),
)
// A test build scans in-process and runs no clamd; the shipped declaration keeps the
// defaults (`sandbox: required`, ClamAV required). The medium already carries the
// `rules` key, so it is REPLACED, never added beside it (RON refuses a duplicate key).
.replace("rules: (sandbox: required),", "rules: (sandbox: off),")
.replace("required: true)))", "required: false)))")
.replace("\"0.0.0.0:50051\"", &format!("\"127.0.0.1:{grpc}\""))
.replace("\"0.0.0.0:8443\"", &format!("\"127.0.0.1:{http}\""));
assert!(!ron.contains("\"/data"), "every path moved:\n{ron}");
// What the installer feeds and the medium stages.
std::fs::write(
data.path().join(estate_ron::CONSOLE_TOKEN_FILE),
CONSOLE_TOKEN,
)
.unwrap();
std::fs::set_permissions(
data.path().join(estate_ron::CONSOLE_TOKEN_FILE),
<std::fs::Permissions as std::os::unix::fs::PermissionsExt>::from_mode(0o600),
)
.unwrap();
std::fs::write(
data.path().join(estate_ron::KORP_ROOT_STAGED),
estate_ron::KORP_ROOT_PEM,
)
.unwrap();
install_intel(&data.path().join("holger/intel"));
let mut cfg = tempfile::NamedTempFile::new().unwrap();
cfg.write_all(ron.as_bytes()).unwrap();
let mut holger: Holger = read_ron_config(cfg.path()).map_err(|e| format!("config: {e:#}"))?;
// The box: the estate's name from the join reply.
holger.resolve_estate(Some(ESTATE));
holger
.instantiate_backends()
.map_err(|e| format!("backends: {e:#}"))?;
wire_holger(&mut holger).map_err(|e| format!("wire: {e:#}"))?;
holger.start().map_err(|e| format!("start: {e:#}"))?;
Ok(Running {
_data: data,
_cfg: cfg,
holger,
})
}
#[tokio::test(flavor = "multi_thread")]
async fn the_estate_declaration_installs_the_provenance_scanner() {
let r = start();
let chain = r
.holger
.intake()
.expect("the estate declares the intake chain");
let pipeline = chain.scanner().expect("the estate declares its scans");
let installed = pipeline.scanners().installed(ScannerKind::Provenance);
assert!(
installed.iter().any(|e| e.contains("holger-provenance")),
"the Security page's \"Provenance\" row is on, its engine named: {installed:?}"
);
}