fix(farm): a raw NUL byte made the repo's largest source file unsearchable (#536) - #548
Merged
Conversation
…hable (#536) farm.ts carried a literal NUL character inside `parts.join(...)` - the actual byte, not the `\0` escape. ripgrep treats any file containing one as BINARY, so every content search across this repo silently skipped its largest source file and returned "binary file matches" instead of results. Hit live while investigating #515: a grep over farm.ts came back empty and the reason was not the query. git was unaffected and still is - its binary sniff reads only the first 8000 bytes and the NUL sat at 50941 - which is why diffs, blame and review always looked normal and nothing ever flagged it. NO behaviour change, and not a payload change: - farm.js rebuilds BYTE-IDENTICAL, so the emitted separator is unchanged. - the payload gate agrees: plugins/ca/tools/ is a build directory and only the declared artifact counts (#435). No ca version bump. - the crypto gate independently executed both forms under Node and confirmed sha256 over a raw-NUL join and an escape join produce the identical digest, so no setup fingerprint is invalidated and no setup cache is busted. Cleared H-09b through the auth-crypto reviewer rather than around it. The line is `createHash("sha256")`, so the guard fires correctly even though the change is one character of source spelling; the recorded pass binds exactly that one line. The first attempt was refused - I had moved the checkout out from under the reviewer mid-review, and minting then would have bound the attestation to a different tree. That refusal was right. AC-3, the durable half: a guard fails if any TRACKED file with a text extension reintroduces a raw NUL. Scoped to every tracked file rather than a list of trees, because a NUL breaks ripgrep wherever it lands and a five-tree pathspec left 335 files unscanned while the message claimed "tracked source files" unqualified. Both halves are proven by mutation, not asserted: restoring the byte is caught, AND a pathspec matching nothing is caught. The second matters because `git ls-files` exits 0 on a pathspec that matches nothing - measured - so the returncode check alone would pass vacuously forever. The scanned-count floor is the control that actually does the work. Confirmed fixed with the tool that could not read it: ripgrep now returns line-numbered matches from farm.ts. Claude-Session: https://claude.ai/code/session_01WJgVfZw7J81PB7mwpHyUxx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #536.
The bug
farm.tscarried a literal NUL character insideparts.join(...)— the actual byte, not the\0escape.ripgrep treats any file containing one as binary, so every content search across this repo silently skipped its largest source file (~3100 lines) and returned
binary file matchesinstead of results. Hit live while investigating #515: a grep overfarm.tscame back empty, and the reason was not the query.git was unaffected and still is — its binary sniff reads only the first 8000 bytes and the NUL sat at 50941. That is why diffs, blame and review always looked normal and nothing ever flagged it. A search tool that quietly excludes a file is worse than one that errors, because the answer looks complete.
No behaviour change, and no payload change
farm.jsrebuilds byte-identical — the emitted separator is unchanged.plugins/ca/tools/is a build directory and only the declared artifact counts (SBX payload-version gate over-fires on dev-only lockfile changes #435). Nocaversion bump.sha256over a raw-NUL join and an escape join produce the identical digest, so no setup fingerprint is invalidated and no setup cache is busted.Clearing H-09b
The changed line is
createHash("sha256"), so the guard fires correctly even though the change is one character of source spelling. I cleared it through the auth-crypto reviewer rather than around it; the recorded pass binds exactly that one line.Worth recording: the first attempt was refused. I had moved the checkout out from under the reviewer mid-review, and minting the marker then would have bound the attestation to a different tree. That refusal was correct, and it is the failure mode ADR-0010 exists to prevent.
AC-3 — the durable half
A guard fails if any tracked file with a text extension reintroduces a raw NUL. Scoped to every tracked file rather than a list of trees: a NUL breaks ripgrep wherever it lands, and a five-tree pathspec left 335 tracked files unscanned while the failure message claimed "tracked source files" without qualification.
Both halves are proven by mutation rather than asserted:
The second matters more than it looks.
git ls-filesexits 0 on a pathspec that matches nothing — measured — so the returncode check alone would have passed vacuously forever. The scanned-count floor is the control that actually does the work.Confirmation
Verified with the tool that could not read it: ripgrep now returns line-numbered matches from
farm.ts.