crates/gunnar-cli/tests/ai_trailers_refused.rs
#![cfg(feature = "server")]
//! **The forge refuses a push that introduces an AI attribution trailer**
//! (owner ruling 2026-09-28), end to end: stock `git` over real ssh into a
//! repository with NO policy configured — the gate is on by default.
//!
//! One repository, in the order an estate meets it:
//!
//! 1. a USER pushes trailer-bearing history into the empty repository:
//! refused, and the client's error names the commit and the fix;
//! 2. the installer's seed — the `installer-seed` MACHINE principal — pushes
//! the same history into the still-empty repository: accepted;
//! 3. the user pushes a historical trailer commit (now on `main`) to a new
//! branch: accepted, it is not new to the forge;
//! 4. the user adds a NEW trailer commit: refused with its sha; a commit that
//! only mentions the tool in prose: accepted.
mod common;
use std::path::{Path, PathBuf};
use common::{tools_present, Harness};
use gunnar_core::{Grant, HashKind, Level, Principal, PrincipalKind};
const NS: &str = "team";
const REPO: &str = "project";
const TRAILER: &str = "Co-Authored-By: Claude <noreply@anthropic.com>";
fn head(harness: &Harness, work: &Path) -> String {
harness
.git(work, &["rev-parse", "HEAD"])
.expect_ok("rev-parse")
.stdout
.trim()
.to_owned()
}
fn commit(harness: &Harness, work: &Path, file: &str, message: &str) -> String {
std::fs::write(work.join(file), message).expect("write");
harness.git(work, &["add", "."]).expect_ok("add");
harness
.git(work, &["commit", "--quiet", "-m", message])
.expect_ok("commit");
head(harness, work)
}
/// The seed's key, enrolled as the `installer-seed` machine principal with
/// the namespace Admin a first push needs — exactly what the installer's
/// `principal.installer-seed = "machine admin <key>"` line would give it.
fn seed_ssh_command(harness: &Harness) -> String {
let private = gunnar_core::mint_ed25519().expect("mint the seed key");
let parsed = gunnar_core::ssh_key::PrivateKey::from_openssh(&private).expect("parses");
let public = parsed.public_key().to_openssh().expect("render");
let path: PathBuf = harness.dir.path().join("seed_key");
std::fs::write(&path, &private).expect("write the seed key");
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt as _;
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o600)).expect("chmod");
}
let auth = harness.stores.auth();
auth.add_principal(
Principal::new("installer-seed", "installer-seed", PrincipalKind::Machine).expect("valid"),
)
.expect("add the seed principal");
auth.register_key("installer-seed", &public, None)
.expect("register the seed key");
auth.grant(Grant::namespace("installer-seed", NS, Level::Admin))
.expect("namespace admin");
harness.ssh_command().replace(
&harness.key_path.display().to_string(),
&path.display().to_string(),
)
}
#[test]
fn a_new_trailer_commit_is_refused_and_history_the_forge_holds_is_not() {
assert!(tools_present(), "this test requires git and ssh");
let mut harness = Harness::start(NS, REPO, HashKind::Sha1);
let url = harness.url(NS, REPO);
// A mirror's history: a trailer commit deep in it.
let work = harness.dir.path().join("work");
harness
.git(
harness.dir.path(),
&[
"init",
"--quiet",
"-b",
"main",
work.to_str().expect("utf-8"),
],
)
.expect_ok("git init");
commit(&harness, &work, "a.txt", "root");
let historical = commit(
&harness,
&work,
"b.txt",
&format!("historical work\n\n{TRAILER}"),
);
commit(&harness, &work, "c.txt", "later, clean");
// 1. A user pushing it into the EMPTY repository is refused.
let refused = harness.git(&work, &["push", &url, "main"]);
assert!(
!refused.ok,
"a user seeded trailer history into an empty repository:\n{}",
refused.stderr
);
assert!(
refused.stderr.contains(&historical),
"the refusal must name the commit: {}",
refused.stderr
);
assert!(
refused.stderr.contains("git commit --amend"),
"the refusal must say how to fix it: {}",
refused.stderr
);
// 2. The seed, the same history, the same still-empty repository.
let seed = seed_ssh_command(&harness);
harness
.git_with_ssh(&work, &seed, &["push", "--quiet", &url, "main"])
.expect_ok("the installer seed into an empty repository");
// 3. A historical trailer commit, already on `main`, to a new branch name.
harness
.git(
&work,
&[
"push",
"--quiet",
&url,
&format!("{historical}:refs/heads/old-work"),
],
)
.expect_ok("history the forge already holds, at a new name");
// 4. New work: a trailer is refused and names its sha; prose passes.
let bad = commit(&harness, &work, "d.txt", &format!("new work\n\n{TRAILER}"));
let refused = harness.git(&work, &["push", &url, "main"]);
assert!(!refused.ok, "a new trailer commit was accepted");
assert!(refused.stderr.contains(&bad), "{}", refused.stderr);
assert!(refused.stderr.contains(TRAILER), "{}", refused.stderr);
harness
.git(&work, &["reset", "--quiet", "--hard", "HEAD~1"])
.expect_ok("drop the trailer commit");
commit(
&harness,
&work,
"d.txt",
"Switch the review bot to Claude\n\nNo Co-Authored-By trailers, ever.",
);
harness
.git(&work, &["push", "--quiet", &url, "main"])
.expect_ok("a prose mention is not a trailer");
harness.stop();
}