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

nordisk/gunnar

crates/gunnar-cli/tests/forge_spine.rs

#![cfg(feature = "server")]

//! **The forge's evidence spine, through the real stores** (roadmap phase 1).
//!
//! The claim under test is the one the roadmap makes about this phase:
//!
//! > An approval gets the same evidential weight as a commit.
//!
//! So the arms are about *evidence*, not about features. What is asserted is
//! that an approval is signed by the reviewer's own key, that it names the
//! commit it approved, that a key gunnar does not know cannot write one at all,
//! and — the one that matters most — that **a force-push does not inherit a
//! review**.

mod common;

use common::Harness;
use gunnar_core::forge::{fold, sign_event, verify_event, Event, EventProblem, ForgeEvent};
use gunnar_core::HashKind;

fn read_events(harness: &Harness) -> Vec<gunnar_core::SignedEvent> {
    harness
        .stores
        .forge_log()
        .lines_of()
        .expect("read the forge log")
        .iter()
        .map(|line| serde_json::from_str(line).expect("decode a forge event"))
        .collect()
}

fn append(harness: &Harness, signed: &gunnar_core::SignedEvent) {
    use std::io::Write as _;
    let log = harness.stores.forge_log();
    let mut line = serde_json::to_string(signed).expect("json");
    line.push('\n');
    let mut file = std::fs::OpenOptions::new()
        .create(true)
        .append(true)
        .open(log.records_path())
        .expect("open the forge log");
    file.write_all(line.as_bytes()).expect("append");
}

fn event(pull: u64, actor: &str, at: i64, event: Event) -> ForgeEvent {
    ForgeEvent {
        repo: "team/reviewed".into(),
        pull,
        actor: actor.into(),
        at_unix_ms: at,
        event,
    }
}

/// **An approval is checkable against the identity key table, later, by anybody.**
///
/// The harness's key is registered, so the approval it signs is evidence; a key
/// nobody registered produces a signature with nothing to check it against,
/// which is the state the `forge` verb refuses to create and this arm shows the
/// consequence of.
#[test]
fn an_approval_is_signed_by_a_registered_key_and_verifies_against_the_table() {
    let harness = Harness::start("team", "reviewed", HashKind::Sha1);
    let commit = "a".repeat(40);

    let opened = sign_event(
        &harness.private_key,
        event(
            1,
            "tester",
            1_700_000_000_000,
            Event::Commented {
                body: "here is why".into(),
                path: String::new(),
                line: 0,
                blob_oid: String::new(),
            },
        ),
    )
    .expect("sign");
    let approved = sign_event(
        &harness.private_key,
        event(
            1,
            "tester",
            1_700_000_001_000,
            Event::Approved {
                commit: commit.clone(),
            },
        ),
    )
    .expect("sign");
    append(&harness, &opened);
    append(&harness, &approved);

    // A reader with the identity table and nothing else can check it.
    let registered: Vec<gunnar_core::ssh_key::PublicKey> = harness
        .stores
        .auth()
        .signing_keys()
        .expect("keys")
        .iter()
        .filter_map(|row| gunnar_core::ssh_key::PublicKey::from_openssh(&row.openssh).ok())
        .collect();
    assert!(
        !registered.is_empty(),
        "the harness registers a key; without one this arm proves nothing"
    );

    let stored = read_events(&harness);
    let approval = stored
        .iter()
        .find(|e| matches!(e.event.event, Event::Approved { .. }))
        .expect("the approval was written");
    let key = registered
        .iter()
        .find(|k| {
            k.fingerprint(gunnar_core::ssh_key::HashAlg::Sha256)
                .to_string()
                == approval.key_fingerprint
        })
        .expect("the signing key is in the identity table");
    assert_eq!(verify_event(approval, key), Ok(()));

    // …and a different registered key must not verify it.
    let stranger =
        gunnar_core::ssh_key::PrivateKey::from_openssh(gunnar_core::mint_ed25519().expect("mint"))
            .expect("parse");
    assert!(matches!(
        verify_event(approval, stranger.public_key()),
        Err(EventProblem::WrongKey { .. })
    ));
}

/// **A force-push does not inherit a review.**
///
/// The approval names the commit it approved. Move the branch and the approval
/// still names the old one — visibly, in the fold — which is exactly the fact a
/// mutable `approved: bool` destroys and the reason this log is events.
#[test]
fn an_approval_does_not_follow_the_branch_when_it_moves() {
    let harness = Harness::start("team", "reviewed", HashKind::Sha1);
    let reviewed = "1".repeat(40);
    let force_pushed = "2".repeat(40);

    for signed in [
        sign_event(
            &harness.private_key,
            event(
                1,
                "tester",
                1,
                Event::Commented {
                    body: String::new(),
                    path: String::new(),
                    line: 0,
                    blob_oid: String::new(),
                },
            ),
        )
        .expect("sign"),
        sign_event(
            &harness.private_key,
            event(
                1,
                "tester",
                2,
                Event::Approved {
                    commit: reviewed.clone(),
                },
            ),
        )
        .expect("sign"),
    ] {
        append(&harness, &signed);
    }

    let events: Vec<ForgeEvent> = read_events(&harness).into_iter().map(|s| s.event).collect();
    let review = fold(1, &events);

    assert_eq!(
        review.approvers_of(&reviewed),
        vec!["tester"],
        "the commit that was reviewed must be approved"
    );
    assert!(
        review.approvers_of(&force_pushed).is_empty(),
        "the branch moved and the approval followed it. A review of one commit is not a review \
         of another, and a forge that lets it follow is one where a force-push after approval \
         ships unreviewed code"
    );
}

/// The log is covered by the **same** transparency machinery, so a rewritten
/// approval is caught the same way a rewritten push attestation is.
#[test]
fn the_forge_log_is_covered_by_a_signed_tree_head() {
    let harness = Harness::start("team", "reviewed", HashKind::Sha1);
    let commit = "3".repeat(40);
    for signed in [
        sign_event(
            &harness.private_key,
            event(
                1,
                "tester",
                1,
                Event::Commented {
                    body: String::new(),
                    path: String::new(),
                    line: 0,
                    blob_oid: String::new(),
                },
            ),
        )
        .expect("sign"),
        sign_event(
            &harness.private_key,
            event(1, "tester", 2, Event::Approved { commit }),
        )
        .expect("sign"),
    ] {
        append(&harness, &signed);
    }

    let log = harness.stores.forge_log();
    let key = harness.stores.transparency_key().expect("key");
    log.issue_head(&key, 1_700_000_000_000).expect("head");
    assert!(
        log.audit(key.public_key()).expect("audit").is_consistent(),
        "the control: a freshly headed log must audit clean"
    );

    // Rewrite the approval to name a different commit — the edit an attacker
    // with the disk would actually make.
    let path = log.records_path().to_path_buf();
    let text = std::fs::read_to_string(&path).expect("read");
    let tampered = text.replacen(&"3".repeat(40), &"4".repeat(40), 1);
    assert_ne!(tampered, text, "the rewrite changed nothing");
    std::fs::write(&path, tampered).expect("rewrite");

    assert!(
        !log.audit(key.public_key()).expect("audit").is_consistent(),
        "an approval was rewritten under a signed head and the audit did not notice"
    );
}