This is the record of an abcd-managed repository

abcdBetav0.16.2 · 2026-11-06

Development record

guard tokenizer heredoc misparse: an unquoted arithmetic left-shift with an identifier operand (e.g. `$((1<<shift))`) is misparsed as a here-document start by isDelimStart (tokenize.go:206, accepts any word starting with a letter/underscore) plus readHeredocDelim (tokenize.go:249, terminates the delimiter word on ')'). skipHeredocBodies (tokenize.go:264) then scans for a line equal to the bogus delimiter, finds none, and silently swallows every subsequent line of the command before it reaches command position. A dangerous command on a later line (git push --force, rm -rf, gh repo delete, etc.) is never seen by Registry.Check and the guard returns VerdictAllow with no error — a silent fail-open, not the loud could-not-answer path the shim reserves for unparsable input. Confirmed live via both CLI front doors (internal/surface/cli/guard.go: guard check and guard hook). The literal-digit form (1<<20) is unaffected; only identifier/variable operands trip it. Reproducing test: internal/core/guard/tokenize_test.go, TestArithmeticShiftByIdentifierIsNotAHeredoc.

issueresolvedcriticaliss-184

guard tokenizer heredoc misparse: an unquoted arithmetic left-shift with an identifier operand (e.g. $((1<<shift))) is misparsed as a here-document start by isDelimStart (tokenize.go:206, accepts any word starting with a letter/underscore) plus readHeredocDelim (tokenize.go:249, terminates the delimiter word on ')'). skipHeredocBodies (tokenize.go:264) then scans for a line equal to the bogus delimiter, finds none, and silently swallows every subsequent line of the command before it reaches command position. A dangerous command on a later line (git push --force, rm -rf, gh repo delete, etc.) is never seen by Registry.Check and the guard returns VerdictAllow with no error — a silent fail-open, not the loud could-not-answer path the shim reserves for unparsable input. Confirmed live via both CLI front doors (internal/surface/cli/guard.go: guard check and guard hook). The literal-digit form (1<<20) is unaffected; only identifier/variable operands trip it. Reproducing test: internal/core/guard/tokenize_test.go, TestArithmeticShiftByIdentifierIsNotAHeredoc.

Correction, 2026-09-29

The resolution was true when it was written, and its part (2) is not true at this tip. 16d50feb ("fix: give the two bash-executable tokenizer states a verdict") changed what a genuine heredoc with no terminator line does: it is no longer ErrUnparsableCommand, because an error reaches the pre-tool-use hook as fail-open, but a segment flag that Check folds into a fail-closed block. The same commit replaced TestTokenizeRejectsUnterminatedHeredoc, which the resolution names, with TestTokenizeFlagsUnterminatedHeredoc in internal/core/guard/tokenize_test.go, which pins the block. Part (1), the arithmetic-shift classification, stands as the resolution describes it, and its two tests are still in the tree.

Frontmatter

idiss-184
typeissue
lifecycleresolved
severitycritical

mentions3