update-bundle/src/tests/verdict.rs
//! B1: the recorded policy verdict decides. A bundle whose reviewers marked it
//! anything but `pass` is refused by name, even when every signature in it is
//! genuine. Before the fix the verifier read only `non_production`, so a bundle
//! prepared with `--verdict block` verified and imported.
use super::testkit::*;
use crate::finalize::finalize;
use crate::schema::{CORE_JSON, RELEASE_MANIFEST_JSON, RELEASE_SIG};
use crate::verify::{verify_control_plane_at, verify_member_map, TargetSpec};
fn verify(b: &BuiltBundle) -> crate::error::Result<crate::verify::VerifyReport> {
verify_member_map(&b.members, &b.policy, &TargetSpec::default(), None, b.now)
}
fn refused_by_verdict(b: &BuiltBundle, verdict: &str) {
let err = verify(b).expect_err("a bundle whose verdict is not pass must not verify");
let msg = err.tagged();
assert!(
msg.contains("verdict") && msg.contains(&format!("{verdict:?}")),
"the refusal must name the verdict it refused: {msg}"
);
assert_eq!(
err.code().code,
holger_errcode::UPDATE_VERDICT_NOT_PASS.code,
"{msg}"
);
}
#[test]
fn a_pass_verdict_still_verifies() {
let b = valid_bundle_with_verdict("pass");
verify(&b).expect("a passed, fully signed bundle verifies");
}
#[test]
fn a_block_verdict_is_refused_although_every_signature_is_genuine() {
refused_by_verdict(&valid_bundle_with_verdict("block"), "block");
}
#[test]
fn an_empty_verdict_is_refused() {
refused_by_verdict(&valid_bundle_with_verdict(""), "");
}
#[test]
fn a_verdict_that_only_looks_like_pass_is_refused() {
for v in ["PASS", "pass ", "pass-with-exceptions", "warn", "hold"] {
refused_by_verdict(&valid_bundle_with_verdict(v), v);
}
}
#[test]
fn the_control_plane_refuses_a_block_verdict_before_anything_is_decompressed() {
let b = valid_bundle_with_verdict("block");
let err = verify_control_plane_at(&b.members, &b.policy, b.now)
.expect_err("the control-plane check must refuse a blocked bundle too");
assert!(err.tagged().contains("verdict"), "{}", err.tagged());
}
#[test]
fn the_verdict_is_signed_so_flipping_block_to_pass_breaks_the_bundle() {
let mut b = valid_bundle_with_verdict("block");
let core = String::from_utf8(b.members[CORE_JSON].clone()).unwrap();
assert!(
core.contains("\"block\""),
"fixture: core.json records the verdict"
);
let flipped = core.replacen("\"block\"", "\"pass\"", 1);
b.members
.insert(CORE_JSON.to_string(), flipped.into_bytes());
let err = verify(&b).expect_err("an edited verdict must break the signatures");
assert!(
!err.tagged().contains("verdict"),
"the edit must fail as tampering, not reach the verdict check: {}",
err.tagged()
);
}
#[test]
fn finalize_refuses_to_release_sign_a_blocked_candidate() {
let b = valid_bundle_with_verdict("block");
let mut members = b.members.clone();
members.remove(RELEASE_MANIFEST_JSON);
members.remove(RELEASE_SIG);
members.remove(crate::schema::RELEASE_CRT);
let before = members.clone();
let err = finalize(
&mut members,
&b.policy,
&b.release.cert_der,
&b.release.key_pkcs8,
b.now,
)
.expect_err("the release signer must not sign a bundle its reviewers blocked");
assert!(err.tagged().contains("verdict"), "{}", err.tagged());
assert_eq!(members, before, "a refused finalize writes no signature");
}