Skip to content

fix(hooks): stop heredoc bodies false-blocking the commit gate (H-09b)#139

Merged
SUaDtL merged 1 commit into
mainfrom
fix/commit-gate-heredoc-falseblock
Jun 26, 2026
Merged

fix(hooks): stop heredoc bodies false-blocking the commit gate (H-09b)#139
SUaDtL merged 1 commit into
mainfrom
fix/commit-gate-heredoc-falseblock

Conversation

@SUaDtL

@SUaDtL SUaDtL commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

What

Fixes a false-positive in the H-09b/H-10b/H-14 commit gate discovered while landing #138: a git commit -F - <<EOF … EOF (the recommended multi-line-commit form) was blocked. COMMIT_RE captures git-commit args greedily across newlines, so the heredoc body was parsed as pathspecs; git diff HEAD -- <body tokens> then errored and the gate failed closed.

Fix

  • _strip_heredoc_bodies(args) — join \-newline continuations, then remove only the heredoc operator+body+delimiter, re-emitting the operator-line tail (a pathspec after <<WORD is a real git arg).
  • commit_pathspecs skips redirect-operator tokens.
  • Applied at both gate call sites. The --cached staged scan and the worktree-union over-include for real pathspecs are untouched, so the v2.rev.0015 pathspec-bypass guard is preserved.

Why this was risky — and how the review earned its keep

This touches the load-bearing security commit gate, so it went through adversarial security review, which caught two ways an earlier cut silently weakened the gate (both shifted BLOCK→ALLOW vs. baseline, both now closed and locked by tests):

  1. A first-line split("\n",1)[0] also truncated at the literal newline inside a quoted -m "subject\n\nbody" message and dropped a trailing pathspec — reopening the worktree-union bypass.
  2. The heredoc strip swallowed a pathspec sitting on the operator line after <<WORD.

The shipped form strips only the heredoc body.

Tests

test_hook_guards.py:

  • Unit assertions for _strip_heredoc_bodies / commit_pathspecs, including the two non-weakening cases above (multi-line message + pathspec, and operator-line pathspec).
  • Integration 6l (benign heredoc allows), 6m (staged-crypto heredoc still blocks), 6n (unstaged crypto via a multi-line -m pathspec commit still blocks).
  • 102 guard assertions, 0 failed; migration backstop + full hook suite green.

Known residual (not introduced here, errs closed)

COMMIT_RE's [^|;&]* truncates the captured args at a |/;/& inside a heredoc body, so a body line containing those chars can leave an unterminated heredoc that survives the strip and can still false-block. This errs closed (fail-safe), so it is not a weakening — noted for a future follow-up.

https://claude.ai/code/session_011nnQYBPd5Dt1rmtf9CHNfU

COMMIT_RE captures git-commit args greedily across newlines, so a
`git commit -F - <<EOF ... EOF` heredoc body was parsed as pathspecs;
`git diff HEAD -- <body tokens>` then errored and the H-09b/H-10b/H-14
gate failed CLOSED - false-blocking the recommended multi-line-commit
form (which surfaced landing this very branch).

Add _strip_heredoc_bodies(): join `\`-newline continuations, then remove
only the heredoc operator+body+delimiter, re-emitting the operator-line
tail. Skip redirect-operator tokens in commit_pathspecs. Applied at both
gate sites. The --cached staged scan and the worktree-union OVER-include
for real pathspecs are untouched, so the v2.rev.0015 pathspec-bypass
guard is preserved.

Adversarial security review caught two ways an earlier cut weakened the
gate, both now closed and locked by tests: (1) bounding to the first
newline truncated at the literal newline inside a quoted
`-m "subject\n\nbody"` message and dropped a trailing pathspec; (2) the
heredoc strip swallowed a pathspec sitting on the operator line after
`<<WORD`. Both shifted BLOCK->ALLOW vs baseline; the shipped form strips
only the heredoc body.

Tests: test_hook_guards.py gains unit assertions for _strip_heredoc_
bodies / commit_pathspecs (multi-line-message + pathspec, and the
operator-line pathspec, both non-weakening) and integration 6l (benign
heredoc allows), 6m (staged-crypto heredoc blocks), 6n (unstaged crypto
via a multi-line `-m` pathspec commit still blocks). 102 guard
assertions + migration suite green.

CHANGELOG: the commit gate no longer false-blocks multi-line
`git commit -F - <<EOF` commit messages.

Claude-Session: https://claude.ai/code/session_011nnQYBPd5Dt1rmtf9CHNfU
@SUaDtL
SUaDtL merged commit cafeb8e into main Jun 26, 2026
22 checks passed
@SUaDtL
SUaDtL deleted the fix/commit-gate-heredoc-falseblock branch June 26, 2026 15:13
SUaDtL added a commit that referenced this pull request Jun 27, 2026
* docs(changelog): roll the 2.6.0 release notes

Reconcile the [2.6.0] CHANGELOG section for the release: broaden the intro
beyond the farm work, add context-drift provenance (#145) and file-scoped
context injection (#146) under Added, note the MIT to AGPLv3 relicense (#147,
ADR-0009) under Changed, and add a Fixed section for #143, #139, and #138.
Dated 2026-06-27.

Also flips v2.feature.0001 to done now that file-scoped context injection has
shipped and merged; the flip rides this release commit per ADR-0008.

No version bump: plugin.json is already 2.6.0 and the ca-scoped commit window is
unchanged. The relicense and docs commits touch no plugins/ca/ payload.

Claude-Session: https://claude.ai/code/session_01QaLw7nTrRbHwygFar2R6zu

* docs(license): reserve the dual-licensing right without offering it yet

Soften the README "Commercial licensing" wording. The prior text implied
proprietary licenses were actively on offer; the intent is narrower: the owner
retains sole ownership and reserves the right to dual-license, but is not
offering commercial licenses at this time. Inquiries may still be sent through
GitHub and will be considered if and when a commercial path is established.

The legal substance is unchanged. AGPLv3 plus sole copyright already secures the
right; this only corrects the public statement so it does not overstate an
active commercial offering. ADR-0009, the CLA, and the CHANGELOG already use the
reserves-the-right framing, so no other file changes.

Claude-Session: https://claude.ai/code/session_01QaLw7nTrRbHwygFar2R6zu

---------

Co-authored-by: SUaDtL <SUaDtL@users.noreply.github.com>
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