Skip to content

docs(rules): an UNQUOTED heredoc executes its body β€” comments included - #1126

Open
ZacxDev wants to merge 1 commit into
mainfrom
docs/rules-unquoted-heredoc
Open

docs(rules): an UNQUOTED heredoc executes its body β€” comments included#1126
ZacxDev wants to merge 1 commit into
mainfrom
docs/rules-unquoted-heredoc

Conversation

@ZacxDev

@ZacxDev ZacxDev commented Aug 30, 2026

Copy link
Copy Markdown
Member

One rule, +416 B, from a real incident in homelab-infra #551.

What happened

cat > "$TMP/prov.sh" <<EOF is unquoted deliberately β€” that is how ${BUCKET}/${AK}/${SK} get substituted. Unquoted expansion also runs `backticks` and $(…) on every line of the body, comments included. A comment containing `mc admin user add` ran it, and ~2 KB of usage text was spliced into the middle of a script that then executed as root inside the MinIO tenant pod.

Nothing was damaged β€” usage text is not valid shell and the generated script's set -e aborted loudly. That is luck, not design.

Why the rule says what it says

The obvious advice ("quote the delimiter") is wrong as a default here: <<'EOF' also kills the ${VAR} interpolation the heredoc was unquoted for, silently breaking provisioning. So the rule leads with keep prose out of the body and names the trade.

Sizing

+416 B against 429 B of spendable headroom before MIN_HEADROOM_BYTES. Two longer drafts (520 B, then 449 B) were refused by test_rules_size.py and trimmed β€” the ceiling was not ratcheted to fit them. test_rules_size.py 5/5.

The mechanical guard lives with the script it protects (scripts/tests/test-provision-heredoc.sh in homelab-infra, with a positive control so a zero can't come from a grep wired to nothing). This is the portable half.

πŸ€– Generated with Claude Code

Learned the hard way in homelab-infra #551. `cat > f <<EOF` is unquoted
deliberately (that is how `${BUCKET}`/`${AK}`/`${SK}` get substituted), and
unquoted expansion runs `backticks` and `$(…)` on EVERY line of the body β€”
comments are not exempt. A comment containing `mc admin user add` RAN it, and
~2 KB of usage text was spliced into the middle of a script that then executed
as root inside a MinIO tenant pod.

Nothing was damaged: the usage text is not valid shell and the generated
script's `set -e` aborted loudly. That is luck, not design.

The rule states the trade rather than just "quote it", because quoting the
delimiter also kills the interpolation the heredoc was unquoted FOR β€” so the
actionable fix is to keep prose OUT of the body, not to reach for `<<'EOF'`
and break provisioning.

Sized to fit: +416 B against 429 B of spendable headroom before
MIN_HEADROOM_BYTES. Two longer drafts (520 B, 449 B) were refused by
test_rules_size.py and trimmed rather than ratcheting the ceiling.
`test_rules_size.py` 5/5.

The mechanical guard lives with the script it protects
(homelab-infra `scripts/tests/test-provision-heredoc.sh`, with a positive
control); this is the portable half.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zGccap8FbunJHXVZCovKp
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