fix(guardrails): scope block-convention-violation's effective_dir to git's own globals - #2152
Conversation
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fab759ed38
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Claude finished @kyle-sexton's task in 3m 2s —— View job Security Review — PR #2152
Scope: the 4 files changed in this PR ( SummaryNo new vulnerabilities. This PR tightens an existing guardrail — it closes a fail-open hole where Note on the Codex review comment (not a security finding)Codex's inline comment on line 349 (wrapper cwd not preserved through Verdict: No security vulnerabilities identified in the changed files. |
|
Claude finished @kyle-sexton's task in 6m 30s —— View job Review completeTodo list
SummaryThe core fix is sound: One confirmed gap (posted inline, and it independently corroborates Codex's finding on line 349): the two This is distinct from the gap the PR's own "Scope" section already discloses (a relative Other notes:
|
|
Claude has reviewed this PR 4 times. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
Adversarial verification: PARTIALLY UPHELDSecond, independent pass. Brief was to refute, not confirm. I could not break the fix — but the ticket's stated acceptance is not literally met, and I am recording that rather than rounding up. Trees pinned: Hook pristineness, by hash rather than by diff — Only 1. Are the shipped controls dead in some third way?This is the third attempt at fixtures on this file, so I treated every
Both flip to blocked. The alias lookup genuinely reaches The dead-control shape you flagged reproduces exactly as documented: 2. The lead control's mechanism, re-derived independentlyI sourced
3. Does the 2/2 row discriminate against a double-apply?Yes — and I proved it rather than argued it. Mutant M3 duplicates the replay (
A relative wrapper dir composes One caveat worth recording: a double-apply is invisible when the wrapper dir is absolute — Per-case discrimination table (the 6 new
|
| # | New case | gr-base | gr-2113 | Discriminates? |
|---|---|---|---|---|
| 1 | env -u -C git <alias>: alias resolves in the TRUE repo (blocked) |
0 | 2 | YES |
| 2 | env -u -C git <alias>: conforming subject still allowed |
0 | 0 | no |
| 3 | env -u -C git <alias>: decoy repo at <cwd>/git is not read |
2 | 0 | YES |
| 4 | post-subcommand -C is not a chdir (decoy repo not read) |
2 | 0 | YES |
| 5 | env -C <dir> git <alias>: wrapper chdir is replayed (blocked) |
2 | 2 | no |
| 6 | env -C <dir> git <alias>: conforming subject still allowed |
0 | 0 | no |
This is the whole of the PARTIALLY. Case 5 does not fail against the current implementation — base's every-word scan hits the same -C inner by accident — so the ticket's "Every test carries a control that fails against the current implementation" is not literally met for the wrapper-replay claim. It is a wording/acceptance gap, not a test-quality gap: M1 and M3 show case 5 is load-bearing in both directions.
Cheapest close: add env --chdir=inner git <alias> as case 5's control. Same replay path, and it does discriminate (base 0 → fixed 2), because base's scan does not recognize the = spelling.
Correctness attack — 21 probe cases x 4 trees
M1 = replay stripped. M2 = both call sites reverted to effective_dir "${w[@]}".
| base | fix | M1 | M2 | case |
|---|---|---|---|---|
| 0 | 2 | 2 | 0 | A env -u -C git <alias> (alias in TRUE repo) |
| 2 | 0 | 0 | 2 | C alias only in decoy <cwd>/git |
| 2 | 0 | 0 | 2 | D git qs -C dec, alias only in decoy |
| 2 | 2 | 0 | 2 | E env -C inner git <alias> |
| 0 | 2 | 2 | 0 | C′ / D′ twins (alias in TRUE repo) |
| 2 | 2 | 0 | 2 | W1 env -C 'my dir' — space in directory |
| 0 | 2 | 0 | 0 | W2 env --chdir=inner |
| 0 | 2 | 0 | 0 | W3 env -Cinner (attached) |
| 0 | 2 | 0 | 0 | W4 env -vC inner (clustered peel) |
| 0 | 2 | 0 | 0 | W5 env -C nope -C inner (last-wins) |
| 2 | 2 | 0 | 2 | W6 env -C <ABS> |
| 0 | 2 | 0 | 0 | W7 env -C inner -S 'git qc -F -…' |
| 2 | 2 | 2 | 2 | W8 git -C inner <alias> |
| 2 | 0 | 0 | 2 | S1 sudo -C 3 git <alias> (close-from, not chdir) |
| 0 | 2 | 2 | 0 | S1′ twin: sudo -C 3, alias in TRUE repo |
| 0 | 2 | 0 | 0 | S2 sudo -D inner git <alias> |
| 2 | 0 | 0 | 2 | Q1 env -u -C git commit -F - mid-merge (sequencer, :392) |
| 0 | 0 | 0 | 0 | Z1 git commit -C HEAD -F - (exempt before effective_dir) |
The fixed column matches the wanted answer on every row. Three results carry the weight:
- M2 reproduces
gr-baseon all 21 rows. The only behavioral delta in this diff iseffective_dir's argument list — nothing incidental (docblock, thewrapper_cdloop's mere existence, the CHANGELOG) is doing work. - W1 = 2 on the fixed tree proves
${wrapper_cd[@]+"${wrapper_cd[@]}"}does not word-split. A split would compose<cwd>/my, find no alias, return 0. On Windows this was the likeliest way the fix could be quietly wrong; it isn't. - Q1 shows the second consumer (
sequencer_in_progress,:392) is fixed too: base 2 → fixed 0. No committed case covers that consumer.
Windows path forms, since fixtures elsewhere in this repo hit 8.3: effective_dir never compares path forms, it only composes, so the shortname risk here is a decoy — but I tested it. With cwd delivered as /tmp/…, as C:/Users/KYLESE~1/…, and as C:/…/TMP~1.MHG/REPO~1.J5F, the lead case reads 0/0 on base and 2/2 on fixed. The ^[A-Za-z]:[\/] branch works on real Windows operands.
git -Cinner (attached git -C) is not a gap — git rejects it: unknown option: -Csub, exit 129, git 2.54.0.windows.1.
One pre-existing defect found — not this PR's, do not block on it
Identical on both trees:
| base | fixed | case |
|---|---|---|
| 0 | 0 | git -C inner commit -F -, MERGE_HEAD in inner → ALLOW (correct) |
| 2 | 2 | git -C inner qc -F - (alias), MERGE_HEAD in inner → should ALLOW |
| 0 | 0 | git qc -F - (alias), MERGE_HEAD in true repo → ALLOW (correct) |
The alias-recursion argv rebuild at :341/:361 — check_segment "${w[@]:0:gi+1}" <expansion> "${w[@]:sub_idx+1}" — drops git's own globals, so -C inner is lost in the recursed frame and the sequencer is probed in the wrong repo. Wrapper words survive (they live below gi); git's own -C does not. Fails closed (over-blocks a legitimate mid-merge alias commit). Present on origin/main, untouched here. Worth its own ticket — it is the same positional-context family as this fix, one frame deeper.
Regressions
bash scripts/affected-tests.sh --run on gr-2113: exit 0, 3 selected suites — resolve-convention-pattern all PASS, block-convention-violation PASS=37 FAIL=0, commit-msg-convention PASS=15 FAIL=0.
Read-vs-run inventory
Ran (real exit codes, both trees unless noted): the full committed suite on gr-base and gr-2113; a 21-case adversarial probe on base + fixed + three mutants; a 3-case pre-existing-gap probe on both; a 4-case Windows-path-form probe on both; a direct resolver derivation sourcing hook-utils.sh; git -Csub acceptance against real git; affected-tests.sh --run; index-vs-worktree hashes on both hook files.
Read only, did not run — hook::git_resolve_index's nice/nohup/timeout/eval/command -v wrapper branches; hook::git_alias_expansion's rc-2 --config-env refusal; first_herestring_subject.
What I did NOT test
- The PowerShell surface. No case drives
ps::classify_git_commandwith a wrapper chdir. Everything above istool_name: "Bash". !-shell aliases (:333-337inline,:351-355gitconfig). Both re-parse viahook::bash_parse_segmentsrather than callingeffective_dir, so I judged them out of blast radius — but I ran none. This is adjacent to the known-and-deliberately-unfixed relative--C-inside-a-shell-alias-body item, which I am not reporting as new.--git-dir=/--work-tree=as alternative repo relocations.effective_dirdoes not model them at all, on either tree. Untested, unfixed, pre-existing.sudo's declared clustered-flag gap (sudo -bD dir git …), documented fail-open athook-utils.sh:1223-1225. I tested only unclustered-D/-C.- The sequencer consumer at
:392beyond my single Q1 shape. - M3's tail. The double-apply probe covered rows through W9 and then hit a fork failure from box saturation (
dofork: child -1 … exit code 0xC0000142) — S1/S1′/S2/U1/Q1/Z1 were not measured under M3. Every row the double-apply question actually turns on (E, W1, W6) completed. - The full repository suite — only
affected-tests.sh's 3 selected suites. gh pr create --titleand the kill switch beyond the suite's existing coverage.
Verdict
PARTIALLY UPHELD. The fix is correct — 28 constructed cases, zero counterexamples, and the two mechanisms you asked me to doubt both re-derive independently. Ship it. The only outstanding item is the case-5 acceptance wording: either add the env --chdir=inner control (one line, genuinely base-failing) or amend the ticket to say the wrapper-replay claim is mutation-proven rather than base-failing. Do not silently round that to a clean discharge — that is the exact shape this file has already produced twice.
Verified by an adversarial reviewer instructed to refute. Probe scripts and mutants retained locally under the session scratchpad.
…parse Review finding on #2152, raised by Codex and confirmed independently by Claude with a concrete mechanism. Both are right, and I reproduced it before changing anything. A `!` alias body re-parses as a NEW top-level command via hook::bash_parse_segments, so its argv carries neither the wrapper that moved git nor git's own globals. `env -C inner git qc` therefore resolved alias.qc in `inner` correctly and then evaluated the alias body's sequencer probe against the payload cwd. With a merge in progress in `inner`, the commit git is about to make carries a prepared message, and the docblock promises an exemption for exactly that -- it was gated instead. effective_dir's base now falls back to HOOK_EFFECTIVE_BASE, which the caller sets to the resolved directory around each `!` reparse and restores after. That is the mechanism block-noncanonical-commit.sh already uses; this file previously had no way at all to carry a directory across a shell-alias hop. Measured with the shipped test file, run against this branch's OWN previous commit (1550363 -- the scoping fix, without this one): case pre post ! alias + wrapper chdir sees the moved-to sequencer 2 0 ! alias + wrapper chdir, no sequencer: still gated 2 2 ! alias + wrapper chdir, conforming subject allowed 0 0 PASS=39 FAIL=1 against 1550363, exactly the one discriminating case. The two controls are the point. A lone "now exempt" assertion passes for any reason that makes `!` aliases unreachable, which is how the two dead fixtures earlier in this PR read as green. The no-MERGE_HEAD twin proves the exemption is conditional on real sequencer state in the right repository rather than blanket, and a separate no-wrapper mid-merge control proves the exemption machinery and the fixture's MERGE_HEAD are real -- so the wrapper is the only variable. Scope, stated rather than implied: this composes the CALLER's directory, where the sibling asks git for the alias's real launch directory (git starts a `!` body at the work tree's top level). For this guard's two consumers the two agree -- `config --get` and `rev-parse --absolute-git-dir` answer identically from anywhere inside one repository. They diverge only when a separate repository is nested below the composed path, which is deliberately not modelled here. Pre-existing, not introduced by the scoping fix; that fix only made the path reachable enough to demonstrate. The PR's Scope note named the missing HOOK_EFFECTIVE_BASE but framed the symptom too narrowly, and is corrected. Suite: 40 pass, 0 fail. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 18s —— View job Security Review — PR #2152
Scope: the 4 files changed in this PR, diffed SummaryNo security vulnerabilities identified. This PR closes an existing fail-open guardrail hole: Two findings from earlier automated reviews on this PR (Codex's inline comment and the prior Claude Points checked, no issue found
Verdict: No security vulnerabilities identified in the changed files. This is a guardrail |
|
Claude finished @kyle-sexton's task in 5m 16s —— View job Review completeTodo list
What's new since the last review passCommit I traced the fix by hand (I could not execute
No new correctness issues found in this commit. Two test-coverage gaps remain, posted inline:
Neither blocks merge — both are pre-identified, honestly disclosed gaps in an already very thoroughly adversarially-tested PR, not newly discovered defects.
|
|
Claude finished @kyle-sexton's task in 1m 49s —— View job Security Review — PR #2152
Scope: the 4 files changed in this PR, diffed Verdict: no security vulnerabilities identifiedThis PR tightens an existing fail-open guardrail:
This is a guardrail-strengthening change (closes a fail-open path), and I found nothing that introduces a new vulnerability or weakens an existing security boundary. |
|
Claude finished @kyle-sexton's task in 3m 7s —— View job Code Review — PR #2152
SummaryI did an independent trace of the diff at What I traced and confirms as correct:
One pre-existing, out-of-scope observation (not a finding on this PR — the code path is untouched by the diff, so I'm not filing it as a blocking comment): the I was not able to execute Verdict: No correctness, architecture, or test-coverage issues found in this push. Recommend merge; the one observation above is a separate, pre-existing, non-blocking item. |
|
Claude finished @kyle-sexton's task in 1m 46s —— View job Security Review — PR #2152
Scope: the 4 files changed in this PR, diffed Verdict: no security vulnerabilities identifiedThis PR tightens an existing fail-open guardrail.
This is a guardrail-strengthening change (closes a fail-open path in a commit-message convention check), not a regression. No CI/workflow files are touched by this PR, so the Actions trust-boundary checks in this lane's brief don't apply here. |
|
Claude finished @kyle-sexton's task in 2m 12s —— View job Code Review — PR #2152 (sync at
|
…git's own globals `effective_dir` scanned EVERY word of the command for `-C`. No `[git, subcommand)` slice, no wrapper replay -- the pre-#1785 shape, and the last un-migrated caller after #2100 finished the sibling in `block-dangerous-git`. It failed in the OPPOSITE direction from that sibling: not blind to a chdir, but inventing chdirs that were never there. `env -u -C git <alias>` moves nothing -- GNU env's `-u NAME` consumes `-C` as the variable to unset -- yet the every-word scan composed `<cwd>/git` and read that directory's aliases. The reachable consumer is the gitconfig alias lookup, which has neither a stdin-form gate nor an exemption gate and fails OPEN: the wrong repository's config silently misses the expansion, the guard never learns the subcommand is `commit`, and the convention goes unenforced. The sequencer probe at the same call site is corrected with it. `effective_dir` now takes git's own globals only -- the slice from the resolved git token to the subcommand -- preceded by any genuine wrapper chdir replayed from HOOK_GIT_RESOLVED_WRAPPER_DIRS, which is the one parser that can tell a real `env -C <dir>` from the `-C` in `env -u -C git`. Controls measured against origin/main before the fix, and they flip in OPPOSITE directions, which a single-direction fixture cannot fake: case pre post env -u -C git qc, alias in the TRUE repo 0 2 env -u -C git qc, alias in a DECOY <cwd>/git 2 0 git qs -C dec (post-subcommand -C) 2 0 env -C inner git qc (genuine wrapper chdir) 2 2 git qc (no wrapper; machinery liveness) 2 2 `git commit -C HEAD` is deliberately NOT the control. `-C` sets the reuse-message exemption and the hook returns before `effective_dir` is ever called, so that invocation answers "allowed" on both trees and reads as already fixed. The positional case runs through the alias lookup instead, with an alias ending in `--` so the appended `-C dec` cannot re-trigger the exemption in the recursed frame -- without that it answered 0 on both trees for an unrelated reason. HOOK_GIT_RESOLVED_WRAPPER_DIRS was printed and confirmed EMPTY for `env -u -C git`, so the lead control passes because the slice is right and not because the resolver invented a compensating wrapper dir. Suite: 37 pass, 0 fail; all 3 affected suites green. Closes #2113 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…parse Review finding on #2152, raised by Codex and confirmed independently by Claude with a concrete mechanism. Both are right, and I reproduced it before changing anything. A `!` alias body re-parses as a NEW top-level command via hook::bash_parse_segments, so its argv carries neither the wrapper that moved git nor git's own globals. `env -C inner git qc` therefore resolved alias.qc in `inner` correctly and then evaluated the alias body's sequencer probe against the payload cwd. With a merge in progress in `inner`, the commit git is about to make carries a prepared message, and the docblock promises an exemption for exactly that -- it was gated instead. effective_dir's base now falls back to HOOK_EFFECTIVE_BASE, which the caller sets to the resolved directory around each `!` reparse and restores after. That is the mechanism block-noncanonical-commit.sh already uses; this file previously had no way at all to carry a directory across a shell-alias hop. Measured with the shipped test file, run against this branch's OWN previous commit (1550363 -- the scoping fix, without this one): case pre post ! alias + wrapper chdir sees the moved-to sequencer 2 0 ! alias + wrapper chdir, no sequencer: still gated 2 2 ! alias + wrapper chdir, conforming subject allowed 0 0 PASS=39 FAIL=1 against 1550363, exactly the one discriminating case. The two controls are the point. A lone "now exempt" assertion passes for any reason that makes `!` aliases unreachable, which is how the two dead fixtures earlier in this PR read as green. The no-MERGE_HEAD twin proves the exemption is conditional on real sequencer state in the right repository rather than blanket, and a separate no-wrapper mid-merge control proves the exemption machinery and the fixture's MERGE_HEAD are real -- so the wrapper is the only variable. Scope, stated rather than implied: this composes the CALLER's directory, where the sibling asks git for the alias's real launch directory (git starts a `!` body at the work tree's top level). For this guard's two consumers the two agree -- `config --get` and `rev-parse --absolute-git-dir` answer identically from anywhere inside one repository. They diverge only when a separate repository is nested below the composed path, which is deliberately not modelled here. Pre-existing, not introduced by the scoping fix; that fix only made the path reachable enough to demonstrate. The PR's Scope note named the missing HOOK_EFFECTIVE_BASE but framed the symptom too narrowly, and is corrected. Suite: 40 pass, 0 fail. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
8877836 to
e0ddf59
Compare
…ailing Closes the one outstanding item from this PR's adversarial verification (PARTIALLY UPHELD) and the two review threads that independently raised the same two gaps. 1. The `env -C inner git <alias>` wrapper-replay row answered 2 on BOTH trees -- origin/main's every-word scan catches that particular `-C inner` by accident. It was load-bearing only under mutation (stripping the replay, or double- applying it), never against the unfixed hook, so the "every test carries a control that fails against the current implementation" bar was not literally met for that claim. Added the `--chdir=` spelling, which the old scan does not recognize because it matched the literal word `-C` only: base 0 -> fixed 2. The `-C` row STAYS. It is the row that catches a double-applied replay, which composes <cwd>/inner/inner and drops to 0. Its directory stays RELATIVE for the same reason -- an absolute wrapper dir makes a double-apply idempotent and that guard silently evaporates. Both facts are now comments in the file. 2. sequencer_in_progress, the OTHER effective_dir consumer, had no committed case reaching it through a wrapper chdir; every pre-existing `sequencer:` case probes the payload cwd's own repo with no wrapper at all. Added one, plus its no-MERGE_HEAD discriminator so "exempt" cannot be confused with "unreachable". Measured with the shipped test file against the unmodified origin/main hook (verified pristine by `git diff --stat`, empty): PASS=37 FAIL=6 -- and all six failures are the six discriminating cases: env -u -C git <alias>: alias resolves in the TRUE repo 0 -> 2 env -u -C git <alias>: decoy repo at <cwd>/git is not read 2 -> 0 post-subcommand -C is not a chdir 2 -> 0 env --chdir=<dir> git <alias>: attached long form replayed 0 -> 2 NEW wrapper chdir reaches the sequencer probe 2 -> 0 NEW ! alias through a wrapper chdir sees the sequencer 2 -> 0 Against the fixed hook: PASS=43 FAIL=0. Every behavioural claim this PR makes now has a control that fails before the fix, in both directions, with non-discriminating rows kept only as named must-not-regress controls rather than as evidence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
e0ddf59 to
79d278e
Compare
|
Warning Automated security review did not complete — this is an infrastructure failure, not a review verdict. Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."
The check is green on purpose, and it is not evidence. It certifies that a security pass ran, and this one did not complete — but the cause is outside this PR's control, so merging is deliberately left unblocked rather than locking every merge for the length of the outage. Nothing was reviewed at this head. Where this check is required, it is satisfied without that evidence; a human should review security-sensitive changes here before merging. Re-run the job to retry the review; a new push also retries it only if the caller's Re-running does NOT help for every class:
|
Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
|
Warning Automated review did not complete — this is an infrastructure failure, not a review verdict. Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."
Re-run the job to retry the review. A new push re-triggers this lane only if the caller's |
What changed
block-convention-violation.sh'seffective_dirscanned every word of the command for-C—no
[git, subcommand)slice, no wrapper replay. That is the pre-#1785 shape, and this hook was thelast un-migrated caller after #2100 finished the sibling in
block-dangerous-git.sh.It failed in the opposite direction from that sibling. #2100's hole was blindness to a chdir
that really happened. This one is indiscriminate: it invents chdirs that are not there. In
env -u -C git <alias> …GNU env's-u NAMEconsumes-Cas the variable to unset, so git nevermoves — yet the every-word scan composed
<cwd>/gitand read that directory's aliases.The reachable consumer is the gitconfig alias lookup (
:310), which has neither a stdin-formgate nor an exemption gate, and which fails open: reading the wrong repository's config silently
misses the expansion, so the guard never learns the real subcommand is
commitand the teamconvention goes unenforced. The
sequencer_in_progressprobe at the same call site is correctedwith it.
effective_dirnow receives git's own globals only — the slice from the resolved git token (gi)to the subcommand — preceded by any genuine wrapper chdir replayed from
HOOK_GIT_RESOLVED_WRAPPER_DIRS. That resolver is the one parser able to tell a realenv -C <dir>from the
-Cinenv -u -C git. This is exactly the shapeblock-noncanonical-commit.shandblock-dangerous-git.shalready use; the docblock points at the sibling's rationale rather thanrestating it.
Verification
Every case was written as a standalone fixture and run against a pristine
origin/mainworktreebefore the fix existed, so both columns below are measured, not reasoned about. The controls flip
in opposite directions, which a single-direction fixture cannot fake.
env -u -C git qc, alias in the true repoenv -u -C git qc, alias only in a decoy<cwd>/gitgit qs -C dec(post-subcommand-C)env -C inner git qc(genuine wrapper chdir)git qc(no wrapper; machinery liveness)Liveness. The
git qcbaseline row is the proof the alias machinery is actually wired and thefixture repositories are real git repos with a real
.claude/source-control.md— without it, auniformly silent hook would read as "all controls pass". The decoy/true pair is the second liveness
proof: the hook speaks when the alias sits in the composed directory and goes quiet when it does
not, which can only happen if it genuinely read a repository rather than echoing the payload.
git commit -C HEADis deliberately NOT the control, per the issue's reachability section.-Csetsexempt=1at:343-345and:350-351returns beforeeffective_diris ever called, sothat invocation answers "allowed" on both trees and reads as already fixed. The positional case runs
through the alias lookup instead, with an alias ending in
--so the-C decgit appends to theexpansion cannot re-trigger the reuse-message exemption in the recursed frame. Without that
--,the case answered 0 on both trees for a reason unrelated to
effective_dir— that first draft wascaught and discarded.
HOOK_GIT_RESOLVED_WRAPPER_DIRSwas printed and confirmed empty forenv -u -C git, so thelead control passes because the slice is right, not because the resolver invented a compensating
wrapper directory.
Suite: 37 pass, 0 fail; all 3 affected suites green (
scripts/affected-tests.sh --run).Scope
A relative
-Cinside a!-shell-alias body still composes from the payload cwd here, because thishook has no
HOOK_EFFECTIVE_BASEtracking the wayblock-noncanonical-commit.shdoes. That is adistinct pre-existing gap, not this defect, and is deliberately left alone.
Adversarial verification status
A fresh-context adversarial verifier was spawned for this PR and did not return a verdict before
the authoring session ended — the machine was saturated by concurrent agents and every spawned
verifier stalled inside a long test sweep. Treat this PR as carrying the author's own evidence only.
What partially substitutes for it, and why it is not nothing: the PRE column in the table above was
produced by running the shipped test file against the unmodified
origin/mainhook in apristine worktree, which is precisely the headline check such a verifier performs. What is still
unverified by a second party is the "can you break it" attack surface and the payload-supply
question called out below.
Closes #2113
Related
block-dangerous-git.sh, and where this was foundhooks/hook-utils.shand migrated the first callerguardrailsfor fix(guardrails): block-dangerous-git still clears an unsafe --force-with-lease via payload cwd and env -S #2124; this branch bumps 0.23.1 → 0.24.0and the second of the two to merge will need to re-bump.