server/lib/tests/critical_profile.rs
//! Fail-closed validation of the `Critical` security profile. These exercise the
//! pure `Holger::validate_profile` seam (no listeners) via the RON config path.
use holger_server_lib::read_ron_config;
fn load(ron: &str, tmp: &std::path::Path) -> holger_server_lib::Holger {
let path = tmp.join("h.ron");
std::fs::write(&path, ron).unwrap();
read_ron_config(&path).expect("RON must parse")
}
/// A Critical config that satisfies every requirement (dummy TLS paths — the
/// validator checks presence/shape, not that the files load).
fn base_critical() -> String {
r#"(
repositories: [],
exposed_endpoints: [
(
ron_name: "grpc",
ron_url: "127.0.0.1:50051",
ron_tls: Some((cert: "cert.pem", key: "key.pem", client_ca: Some("ca.pem"), crl: Some("crl.pem"))),
),
],
storage_endpoints: [],
auth: Some((
methods: [ Mtls(ca_cert: "ca.pem") ],
require_auth_for_reads: true,
default_role: Some(Reader),
)),
audit: (dir: "audit", enabled: true),
security_profile: Critical,
)"#
.to_string()
}
#[test]
fn standard_profile_needs_nothing() {
let tmp = tempfile::tempdir().unwrap();
// No auth, no TLS on loopback — fine under Standard (the default). The audit log
// stays on in every profile (HOL-NCSC, `holger-audit-13`).
let ron = r#"(
repositories: [],
exposed_endpoints: [ (ron_name: "g", ron_url: "127.0.0.1:1") ],
storage_endpoints: [],
audit: (dir: "a", enabled: true),
)"#;
let h = load(ron, tmp.path());
h.validate_profile()
.expect("standard profile always validates");
}
#[test]
fn full_critical_config_validates() {
let tmp = tempfile::tempdir().unwrap();
let h = load(&base_critical(), tmp.path());
h.validate_profile()
.expect("a fully-configured critical profile validates");
}
fn assert_critical_rejects(ron: &str, needle: &str) {
let tmp = tempfile::tempdir().unwrap();
let h = load(ron, tmp.path());
let err = h
.validate_profile()
.expect_err("critical profile must reject this config");
let msg = format!("{err:#}");
assert!(
msg.contains(needle),
"expected error to mention {needle:?}, got: {msg}"
);
}
#[test]
fn critical_rejects_audit_disabled() {
let ron = base_critical().replace("enabled: true", "enabled: false");
assert_critical_rejects(&ron, "audit.enabled");
}
#[test]
fn critical_rejects_no_auth() {
let ron = base_critical().replace(
"auth: Some((\n methods: [ Mtls(ca_cert: \"ca.pem\") ],\n require_auth_for_reads: true,\n default_role: Some(Reader),\n )),",
"auth: None,",
);
assert_critical_rejects(&ron, "auth configuration");
}
#[test]
fn critical_rejects_read_gate_off() {
// HCON-5: an estate build gates reads by default; the declaration opens them by name.
let ron = base_critical()
.replace(
"require_auth_for_reads: true",
"require_auth_for_reads: false",
)
.replace(
" auth: Some((",
" access: (reads: Open, front: Open),\n auth: Some((",
);
assert_critical_rejects(&ron, "require_auth_for_reads");
}
#[test]
fn critical_rejects_no_tls() {
let ron = base_critical().replace(
"ron_tls: Some((cert: \"cert.pem\", key: \"key.pem\", client_ca: Some(\"ca.pem\"), crl: Some(\"crl.pem\"))),",
"",
);
assert_critical_rejects(&ron, "must configure TLS");
}
/// HOL-NCSC (NCSC P3): a Critical endpoint that verifies client certificates names their
/// revocation list.
#[test]
fn critical_rejects_mtls_without_a_crl() {
let ron = base_critical().replace(", crl: Some(\"crl.pem\")", "");
assert_critical_rejects(&ron, "ron_tls.crl");
}
#[test]
fn critical_rejects_no_client_ca() {
let ron = base_critical().replace("client_ca: Some(\"ca.pem\")", "client_ca: None");
assert_critical_rejects(&ron, "client_ca");
}
#[test]
fn critical_rejects_static_bearer_only() {
let ron = base_critical().replace(
"methods: [ Mtls(ca_cert: \"ca.pem\") ],",
"methods: [ StaticToken(subject: \"ci\", token: Some(\"secret\")) ],",
);
assert_critical_rejects(&ron, "static bearer must not be the only");
}
#[test]
fn critical_with_a_failing_repo_hard_fails_instantiate() {
// Passes validate_profile (valid TLS/auth/audit) but has a znippy repo whose
// archive does not exist — in Critical, a single failing repo fails startup.
let tmp = tempfile::tempdir().unwrap();
let ron = r#"(
repositories: [
(
ron_name: "fw",
ron_repo_type: "rust",
ron_upstreams: [],
ron_in: None,
ron_out: None,
ron_archive_path: Some("/nonexistent/does-not-exist.znippy"),
),
],
exposed_endpoints: [
(
ron_name: "grpc",
ron_url: "127.0.0.1:50051",
ron_tls: Some((cert: "cert.pem", key: "key.pem", client_ca: Some("ca.pem"), crl: Some("crl.pem"))),
),
],
storage_endpoints: [],
auth: Some(( methods: [ Mtls(ca_cert: "ca.pem") ], require_auth_for_reads: true, default_role: Some(Reader) )),
audit: (dir: "audit", enabled: true),
security_profile: Critical,
)"#;
let mut h = load(ron, tmp.path());
let err = h
.instantiate_backends()
.expect_err("critical profile must hard-fail on a disabled repository");
let msg = format!("{err:#}");
assert!(
msg.contains("critical profile") && msg.contains("failed to initialize"),
"got: {msg}"
);
}
#[test]
fn standard_with_a_failing_repo_disables_only_that_repo() {
// Contrast: under Standard the same failing repo is disabled, not fatal.
let tmp = tempfile::tempdir().unwrap();
let ron = r#"(
repositories: [
(ron_name: "ok", ron_repo_type: "raw", ron_upstreams: [], ron_in: None, ron_out: None, ron_data_dir: Some("DATA")),
(ron_name: "bad", ron_repo_type: "rust", ron_upstreams: [], ron_in: None, ron_out: None, ron_archive_path: Some("/nonexistent/x.znippy")),
],
exposed_endpoints: [ (ron_name: "g", ron_url: "127.0.0.1:1") ],
storage_endpoints: [],
)"#
.replace("DATA", &tmp.path().join("raw").to_string_lossy());
let mut h = load(&ron, tmp.path());
// Not all repos fail (raw is fine), so Standard serves on.
h.instantiate_backends()
.expect("standard profile disables only the failing repo");
}
/// **Critical without an intake chain refuses a writable or proxy repository** (SEC-REVIEW
/// 2026-11-01 P6). `scan_required_why` reads `promotion.intake`, so a Critical config with no
/// chain booted with every writable repository open: no side door was refused and nothing
/// scanned what they served. A repository that only serves a sealed archive is no door.
#[test]
fn critical_without_an_intake_chain_refuses_a_writable_or_proxy_repository() {
for repo in [
r#"(ron_name: "bundles", ron_repo_type: "raw", ron_upstreams: [], ron_in: None, ron_out: None, ron_data_dir: Some("d"))"#,
r#"(ron_name: "bundles", ron_repo_type: "rust", ron_upstreams: [], ron_in: None, ron_out: None, ron_writable_archive: Some("w.znippy"))"#,
r#"(ron_name: "bundles", ron_repo_type: "rust-remote", ron_base_url: Some("https://index.crates.io"), ron_upstreams: [], ron_in: None, ron_out: None)"#,
] {
let ron = base_critical().replace("repositories: [],", &format!("repositories: [{repo}],"));
let tmp = tempfile::tempdir().unwrap();
let err = load(&ron, tmp.path())
.validate_profile()
.expect_err("critical without a chain refuses it");
let msg = format!("{err:#}");
assert!(
msg.contains("holger-intake-18")
&& msg.contains("bundles")
&& msg.contains("promotion.intake"),
"{repo}: {msg}"
);
}
let sealed = r#"(ron_name: "fw", ron_repo_type: "rust", ron_upstreams: [], ron_in: None, ron_out: None, ron_archive_path: Some("x.znippy"))"#;
let ron = base_critical().replace("repositories: [],", &format!("repositories: [{sealed}],"));
let tmp = tempfile::tempdir().unwrap();
load(&ron, tmp.path())
.validate_profile()
.expect("a sealed archive takes no writes");
}