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

nordisk/holger

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