Development record
A here-document delimiter that is not letter-led fell to the arithmetic-shift branch, so the DOCUMENT was tokenized as commands and one apostrophe in the body raised ErrUnparsableCommand — which the pre-tool-use hook maps to fail-OPEN, leaving any hazard on a later line to run unguarded. bash accepts a WORD after <<: 'cat <<20', 'cat <<$D' and the '<<20' of 'let mask=1<<20' are all here-documents (the shell tokenizes the redirection before the let builtin sees arithmetic), and each of those spellings reached exit 2 / NOT CHECKED on v0.7.0. Evidence symbol: isDelimStart (internal/core/guard/tokenize.go), consulted at the '<<' branch of tokenize. It also falsified the claim both front doors make — that a quote inside a here-document body is document text and never unparsable. The fix must read the conservative superset of delimiter words (letter, digit, underscore, $-led) so the body is data, with the enclosing arithmetic context left as the sole shift discriminator.
A here-document delimiter that is not letter-led fell to the arithmetic-shift branch, so the DOCUMENT was tokenized as commands and one apostrophe in the body raised ErrUnparsableCommand — which the pre-tool-use hook maps to fail-OPEN, leaving any hazard on a later line to run unguarded. bash accepts a WORD after <<: 'cat <<20', 'cat <<$D' and the '<<20' of 'let mask=1<<20' are all here-documents (the shell tokenizes the redirection before the let builtin sees arithmetic), and each of those spellings reached exit 2 / NOT CHECKED on v0.7.0. Evidence symbol: isDelimStart (internal/core/guard/tokenize.go), consulted at the '<<' branch of tokenize. It also falsified the claim both front doors make — that a quote inside a here-document body is document text and never unparsable. The fix must read the conservative superset of delimiter words (letter, digit, underscore, $-led) so the body is data, with the enclosing arithmetic context left as the sole shift discriminator.
Grounds
- pursued: the failing direction was fail-OPEN on the hook, which is the one direction adr-42 rules out, and the delimiter word is a bash fact rather than a design choice — bash tokenizes the redirection before any builtin sees arithmetic, which is why let mask=1<<20 is a here-document there too. Taking the conservative superset rather than every word bash accepts keeps a << reached in an unmodelled arithmetic context from swallowing the rest of the input on the strength of an operator.
Frontmatter
| id | iss-2609020543544634 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | major |
.abcd/work/issues/resolved/iss-2609020543544634-a-here-document-delimiter-that-is-not.md
open on GitHub ↗ · commit history ↗
mentions1
- adr-42The guard's parse layer is a mistake filter, not a security boundary — matching is two-tier, and completeness is abandoned as a goal