Host-lint guard scanner goes blind on a phantom heredoc and accepts a || host fallback
#115
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
TestNoHostLintPathRemainsincmd/vaultik/lintdocker_test.go(onnext, commitd257f8f) asserts that no script invokesgolangci-lintexcept throughdocker. It does not hold that property. Two evasions, each demonstrated on the third review of pull request #114 by mutating the realscript/lintwith the suite left green.(a) Phantom here-document silently blinds the rest of the file.
heredocTerminatortreats any<<on a joined code line as a here-document opener, andshellCodethen discards every following line until one equals that word. So a<<in a quoted string or an inline comment (only whole-line#comments are stripped), or acat <<-\EOFwhose terminator word becomes\EOF, yields a word that never appears — and scanning of everything below it in that file stops, permanently and silently. Appending to the realscript/lint:leaves
make testgreen. This needs no adversary: an ordinary later edit disarms the guard.(b)
assertLinterIsContainerisedaccepts any line wheredockerappears anywhere before the linter, so a one-line native fallback passes. Inserted intoscript/lint'smain():Same run, same green — the escape hatch in its most natural spelling.
Not a live false green on the tree as it stands: all 17 current scripts were confirmed reachable by the scanner. This is a guard asserting a property it does not hold, which is what a later reader trusts instead of re-deriving the invariant.
Implementation requirements
shellCode, fail loudly when a here-document is still open at end of file (terminate != ""after the loop). That alone converts all three forms in (a) from silent blindness into a named failure.shellCodewill need to report the error to its caller rather than swallow it. Tightening the opener test to an actual redirection is a bonus, not a substitute for this.assertLinterIsContainerised, split the joined line on;,&&,||and|, and require the command containing the linter to begin withdocker.script/lint-fix's continuation-joineddocker run … golangci-lint run …is a single command and must still pass.TestShellCodeSeesCodeAndNotProseto cover an unterminated here-document and a<<that is not one.Dockerfile.lint:73-76: it says the PR recordsscript/lint"exits 1 naming thelllfinding", but that evidence block names arevivefinding and says why. One word.Do not re-litigate the containerised gate itself; it is verified and out of scope here.
Definition of done
1<<3echo plus host lint call, and thecommand -v docker || { …; return; }fallback — inserted into the realscript/lint, each shown failingmake testwith a message naming the offending script and line, then reverted. Output recorded verbatim on the PR.<<-\EOFand quoted-<<forms likewise shown to produce a named failure rather than silence.script/lint-fixstill passes the tightened assertion, and the existing 17 scripts still scan clean.make checkgreen, evidence on the PR.script/cibuildnot required — this touches test code only.(closes #N)with this issue's number; branch offnext, PR based onnext.Plan
Branch
fix-115-lint-scanneroffnext. All changes incmd/vaultik/lintdocker_test.goplus one word inDockerfile.lint.shellCodebecomesfunc shellCode(contents string) ([]string, error).After the loop, a non-empty
terminateis an error naming the linenumber and text of the opening line.
TestNoHostLintPathRemainswrapsit with
require.NoErrorso the failure names the script. The openertest in
heredocTerminatoris deliberately left conservative: any<<still opens a here-document, so a quoted<<, aninline-comment
<<and<<-\EOFall reach end of fileunterminated and fail loudly rather than being silently skipped.
assertLinterIsContainerisedsplits the joined line on&&,||,;and|, then requires the segment containing the linter tobegin with
docker(after stripping leading{/().script/lint-fix'scontinuation-joined
docker run … golangci-lint run …contains none ofthose operators, so it is one segment beginning with
dockerand stillpasses on its merits; no exemption is added. The predicate is extracted
as a pure function so it gets its own table test covering the
||fallback spelling.
TestShellCodeSeesCodeAndNotProsegains cases for an unterminatedhere-document and for a
<<that is not one.Dockerfile.lint:75— the cited evidence names arevivefinding, notlll. One word.Evidence for the PR: both mutations from the third review plus the
<<-\EOFand quoted-<<forms inserted into the realscript/lint, each shown failingmake testverbatim and reverted; all 17current scripts confirmed still scanning clean;
make checkgreen.