fix: NUL-delimit verify-build's dist walk so no path escapes the check (closes #223) #225
Reference in New Issue
Block a user
Delete Branch "fix/issue-223-verify-build-nul-walk"
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?
Closes #223.
What changed
check_unlisted_bundlesinscript/verify-buildreaddist/line by line(
find -printintowhile read -r), so two unlisted, marker-carrying pathswere never checked while the script still exited 0.
find dist \( -type f -o -type l \) -print0into amktemplisting, whose
findexit status is still checked separately (the statuscannot be read off a pipeline), and
xargs -0hands the paths back to thisscript as arguments, where the same
is_listedandhas_markerrun onthem. The per-path half is a distinct internal entry point
(
--scan-dist-paths) rather than a second copy of those two functions, sothey cannot drift. The listing is removed by an
EXITtrap.is_listednow answers "not listed" for any path containing a newlinewithout asking
grep. This is load-bearing and was not obvious: NULdelimiting alone did not fix the newline case.
grepreads a patterncontaining a newline as two patterns, so the part before the newline still
matched the listed line and the file was skipped. The manifest is
line-delimited and cannot name such a path at all, so "not listed" is the
only true answer. Proven below — the first fixed run still passed this case.
dist/being a symlink is now its own check with its own message, assertedin
mainbefore anything reads through it. Previously it failed onlybecause GNU
grepexits 2 on a directory (see the pre-fix output below,which literally reports
grep: dist: Is a directory); under agrepthatexits 1 instead, the whole cross-check would have collapsed into a pass.
dist/is searched" now states whatis actually guaranteed — every regular file and every symlink, which is the
whole of what a build emits — and lists the four properties that coverage
rests on as enforced rather than assumed. Other file types are excluded on
purpose: a build emits none of them, and
grepon a fifo would hang ratherthan fail.
Everything #180 hardened is
preserved: the non-zero
findstatus guard, symlinks walked rather thanskipped (dangling and directory-pointing links failing closed through
has_marker's exit-2 path), and the exit-2 treatment inhas_markerandis_listed.Only the
sortstep is gone, and not for portability. It existed fordeterministic reporting order, and that order does not matter to the only
consumer: traversal order decides nothing but which offender is named first
when there are several.
sort -zis a GNU extension, but so arefind -print0and
xargs -0, which this change adopts because the issue mandates them, soportability was never the reason to drop it.
sh -nanddash -nclean;/bin/shisdashon the machine this ran on, sothe battery below executed under
dash. Also run underbashonce as aportability check.
Evidence
Every case below was constructed against a real
make buildtree and run,as a non-root user (uid 1000 — under root
chmod 000does not stopfind).Full battery: 18 cases, 1 control that must pass and 17 that must fail.
Before the fix (
script/verify-buildas it is onnext)Trailing space — passes, which is the bug:
Newline in the name — same, exit 0.
dist/replaced by a symlink — fails, but only incidentally, and the outputnames the reason:
After the fix — the three new cases
NEW 1, a marker-carrying copy at
dist/chrome/src/popup/index.jsplus atrailing space:
NEW 2, a copy whose name is the listed path plus a literal newline (the blank
second line in the message is that newline):
NEW 3,
dist/moved aside and replaced by a symlink to it — now the script'sown check, and
grepis never reached:After the fix — full battery
Case, then exit status. Every failure case still fails; the control still
passes.
Each keeps the diagnostic it had before, e.g. the walk guard:
and the symlink cases still fail closed through
has_marker's exit-2 path(
grep: dist/chrome/dangling.js: No such file or directory,grep: dist/chrome/link-to-dir: Is a directory).The unwalkable-subtree case uses
dist/chrome/src/content, which the manifestdoes not list, so
main's per-bundle loop passes and thefindguard is thething under test.
The battery was run against a freshly rebuilt tree (
make clean, thenmake build) atnext=86cdea5, with no temporary listing file left behindon either the passing or the failing path. It was not re-run after the rebase
onto
next=12acf4d, because that rebase touched onlyTODO.md:script/verify-buildis byte-identical to the version the battery ran against.Verification
make check: green, re-run after rebasing ontonext=12acf4d— 10 testsuites, 252 tests (251 passed, 1 skipped), prettier clean. The earlier body
said "157 tests", which was wrong; this is a real run on the current base.
make cleanthenmake build: green, all 4 bundles verifiedautistmask-build-debug=off.sh -n script/verify-buildanddash -n script/verify-build: clean.Notes
#180; nothing about it is
committed here.
make testisjest, which cannot host a fixture tree whosefilenames contain newlines and 000-mode directories.
fix/issue-223-verify-build-nul-walkrather than theissue-<N>-<slug>form inTODO.md, as instructed when this unit wasassigned.
PASS — 30-case battery re-run independently under
dashandbashas uid 1000 (control 0, every failure case non-zero, no temp file left on any path), plus 12 added attacks (CR/tab/quote/backslash/leading-dash names, newline in a directory component, a name whose first line is exactly a listed manifest entry, 40k-filexargsbatching, missing/unwritableTMPDIR, absentxargs, non-executable$SELF, relative/PATH/symlinked-dir invocation) — no vacuous pass constructible;make checkgreen here (173 tests executed, prettier clean); merges clean intonextwith both TODO entries retained.Two disclosures, neither a defect: the
sortremoval is justified as avoiding a GNU extension whilefind -print0/xargs -0adopted in the same change are equally non-POSIX (the issue mandated them, and nothing reads the listing but the per-path loop, so ordering is genuinely load-free); and the Verification section's "157 tests" does not reproduce — the same commit yields 173 here, so that figure looks stale rather than wrong in kind.22428cd556toc0a9b58e0c