Skip to content

test(invariant): machine-check the exec_real_git reachability whitelist#22

Merged
suzuke merged 2 commits into
mainfrom
test/exec-reachability-invariant
Jul 6, 2026
Merged

test(invariant): machine-check the exec_real_git reachability whitelist#22
suzuke merged 2 commits into
mainfrom
test/exec-reachability-invariant

Conversation

@suzuke

@suzuke suzuke commented Jul 6, 2026

Copy link
Copy Markdown
Owner

What

Adds crates/agentic-git/tests/exec_reachability_invariant.rs: a syn-based AST invariant test that machine-checks the call-site whitelist of the shim's terminal passthrough capability — exec_real_git and exec_with_conflict_guidance — plus a syn dev-dependency (already in the dep tree transitively via serde_derive; no new crate compiled).

Why

Outcome of the adversarial design discussion between claude-agentic-git and fugu (decision d-20260706152615194350-1): fugu's original finding proposed splitting main.rs into six modules; the debate concluded the opposite — main.rs's zero-pub, single-file layout is an audit asset for a security gate (the "which paths reach exec without passing classify?" question is a one-file grep), and the right investment is a machine-checked guardrail, not a structural split. This PR is that guardrail:

  • Any new / relocated / aliased reference to the exec fns anywhere under src/ fails the test until the whitelist is consciously updated in the same PR — the review hook is the point.
  • The whitelist is keyed by (file, enclosing fn, AST-derived structural context: match-arm variant + guard, or if-condition descriptor) — never line numbers, so ordinary code motion above the call sites doesn't churn it.
  • The classification is verified, not decorative: moving a call out of its dispatch arm changes its context string and fails.
  • The multiset comparison is exact in both directions: deleting a whitelisted call site also fails (no silent whitelist rot).

Whitelisted exec surface (current, verified)

# Callee Context
1 exec_real_git shim_main @ if should_bypass (bypass early path)
2 exec_real_git shim_main @ if is_empty (no-agent early path)
3 exec_real_git shim_main @ dispatch-arm Passthrough (unguarded)
4 exec_real_git shim_main @ dispatch-arm ChdirPass (unguarded)
5 exec_real_git shim_main @ dispatch-arm CleanupAndChdirPushPass (unguarded)
6 exec_with_conflict_guidance shim_main @ dispatch-arm ChdirPass (guarded by is_conflict_capable)

Covered evasions (each with an injection counter-test proven red)

  • Bare new call point in main.rs
  • Crate-qualified call from a sibling module (crate::exec_real_git(...) in snapshot.rs/cli.rs)
  • Alias import (use crate::exec_real_git as rg) — both rename directions
  • Fn-pointer harvest (let f = exec_real_git;)
  • Macro token-stream smuggling (conservative string scan on macro tokens — the one deliberate non-AST check, fail-closed)
  • Shadow re-definition of the fn name in a sibling module (confusion vector)
  • Whitelist rot: removed call site → MISSING failure

Scope boundary (deliberate)

Integration tests under tests/ exec the built binary and cannot link these private fns; agentic-git-core cannot reach them. Internal Command::new(git) query sites (fixed argv, e.g. current_branch_of) are a different, lower-risk class — not covered.

Verification

  • cargo test -p agentic-git --test exec_reachability_invariant → 8/8 green
  • End-to-end falsification on the REAL tree: appended fn sneaky_e2e_probe(args: &[String]) { crate::exec_real_git(args, None); } to cli.rs → invariant FAILED naming cli.rs :: sneaky_e2e_probe :: call exec_real_git @ no-context; reverted → green
  • cargo test --workspace → 15/15 suites ok, 0 failed
  • cargo clippy --workspace -- -D warnings → clean
  • cargo package -p agentic-git --allow-dirty (publish gate) → passes
  • New file is rustfmt-clean (repo baseline has pre-existing fmt diffs elsewhere; untouched)

🤖 Generated with Claude Code

suzuke and others added 2 commits July 7, 2026 00:03
`exec_real_git` / `exec_with_conflict_guidance` are the shim's terminal
passthrough capability — the only two functions handing the caller's argv
to real git. Their reachability was auditable by a single-file grep only
because main.rs keeps zero `pub` items; this encodes that audit as a
syn-based AST invariant so it survives future refactors (decision
d-20260706152615194350-1, the agreed alternative to a big-bang module
split).

The test walks every src/ file's AST and compares ALL references to the
two exec fns against an exact whitelist keyed by (file, enclosing fn,
structural context — enclosing match-arm variant + guard, or if-condition
descriptor; never line numbers). Covered evasions, each with an injection
counter-test proven red: bare new call points, crate-qualified calls from
sibling modules, `use ... as` alias imports, fn-pointer harvest, macro
token-stream smuggling (conservative string scan — the one non-AST check),
shadow re-definitions, and silent whitelist rot (removed call sites fail
too — the multiset is exact in both directions).

End-to-end falsification: injecting a bare `crate::exec_real_git` call
into cli.rs on the real tree fails the invariant naming the exact site
(`cli.rs :: sneaky_e2e_probe :: call exec_real_git`); reverting restores
green.

syn was already in the dependency tree (via serde_derive); the dev-dep
adds no new crate to the build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sible_match under --all-targets)

fugu REJECTED @1437ccc: cargo clippy --all-targets -D warnings fails on
the Rename arm nested if. Detection semantics unchanged (8/8 invariant
tests still green, incl. the alias counter-test); local gate widened to
--all-targets which CI (workspace-only clippy) does not cover.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@suzuke suzuke merged commit d789c83 into main Jul 6, 2026
4 checks passed
@suzuke suzuke deleted the test/exec-reachability-invariant branch July 6, 2026 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant