harden(server): explicit verify-commit success gate + non-member/genesis hook tests
- verify_org_signer now rejects on a non-zero git verify-commit exit instead of relying on the stderr fingerprint regex alone (PM hardening note 1). - org_hook_signed: add commit_signed_by_non_member_is_rejected (exercises the signature rejection path) and genesis_bootstrap_with_sole_owner_is_accepted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -239,6 +239,22 @@ fn verify_org_signer(commit: &str, members: &OrgMembers) -> OrgMember {
|
||||
};
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
|
||||
// The org hook builds allowed_signers from EVERY current member, so a clean
|
||||
// `git verify-commit` exit IS the security gate: a non-zero exit means the
|
||||
// commit was unsigned, tampered, or signed by a non-member. Make that
|
||||
// property explicit rather than relying on the stderr regex alone (regex
|
||||
// output is fragile across git versions). The fingerprint parse + member
|
||||
// mapping below then identifies WHICH member signed.
|
||||
if !output.status.success() {
|
||||
eprintln!(
|
||||
"REJECT: org commit {commit} — signature did not verify against current members \
|
||||
(git verify-commit exit {}): {}",
|
||||
output.status.code().unwrap_or(-1),
|
||||
stderr.trim()
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
// Parse the SHA-256 fingerprint from stderr (same regex as verify_commit).
|
||||
let re = regex::Regex::new(r"key (SHA256:[A-Za-z0-9+/]+)").expect("static regex");
|
||||
let signing_fp = match re.captures(&stderr).and_then(|c| c.get(1)) {
|
||||
|
||||
Reference in New Issue
Block a user