Harden the lint-guard shell scanner against silent evasions (closes #121)
check / check (pull_request) Failing after 1s

The guard test's shell scanner was weaker than its commit message
claimed. Two holes are closed.

shellCode now treats `<<` as a here-document only when it is a real
redirection: outside single and double quotes, not past an unquoted
word-initial `#` that begins an inline comment, and followed by a
delimiter word. A `<<` inside a quoted string or an inline comment no
longer opens a phantom here-document that swallows the rest of the
file -- including the silent case where the fake terminator recurs
later as a line of its own -- and a here-document still open at end of
file is a loud error rather than a silent truncation.

assertLinterIsContainerised now cuts the joined line into the simple
commands the shell would run -- on `;`, `&&`, `||` and `|` -- and
requires the command that names the linter to begin with docker. So
`docker info; golangci-lint run` and `docker info || golangci-lint run`
are rejected, while script/lint-fix's `docker run ... golangci-lint`
still passes.

The scanner comment now names the inline-comment exception alongside
the quoted-string and arithmetic ones, and the inherent limits of a
text scan. Dockerfile.lint's citation is corrected from `lll` to
`revive`, the finding the recorded evidence actually named.

model: claude-opus-4-8
This commit is contained in:
2026-09-21 17:34:41 +00:00
parent bdce350041
commit d199ff53ce
2 changed files with 217 additions and 35 deletions
+5 -5
View File
@@ -72,11 +72,11 @@ RUN [ -n "$CHECK_EPOCH" ] || exit 1
# running, and exits 0 reporting `0 issues.` on a tree the real config
# fails. Demonstrated on this repo at this pin, recorded on
# https://git.eeqj.de/sneak/vaultik/pulls/114: with a planted
# over-length line, `script/lint` exits 1 naming the `lll` finding with
# `linters:` and exits 0 with `linterz:`. A set-but-ineffective config
# quietly falling back to defaults is precisely the false-green class
# this gate exists to eliminate, so it must not sit in the gate's own
# configuration.
# over-length line, `script/lint` exits 1 naming the `revive` finding
# with `linters:` and exits 0 with `linterz:`. A set-but-ineffective
# config quietly falling back to defaults is precisely the false-green
# class this gate exists to eliminate, so it must not sit in the gate's
# own configuration.
#
# `config verify` catches it, and it does so OFFLINE at this pinned
# version -- verified, not assumed. Under `docker run --network none`