Skip to content

fix: a build without the tags refuses, and every analysis job passes them - #65

Merged
donislawdev merged 3 commits into
mainfrom
security/build-tags
Sep 6, 2026
Merged

fix: a build without the tags refuses, and every analysis job passes them#65
donislawdev merged 3 commits into
mainfrom
security/build-tags

Conversation

@donislawdev

Copy link
Copy Markdown
Owner

Two halves of one gap. Findings S2 and S5 of the outside security review, both confirmed here before anything was written.

A build without the tags now stops at the compiler

internal/format/avif/avif.go has said since the encoder arrived that its AVX2 path reads past the end of a buffer and takes the process down on some picture sizes - measured at 640x256, in two runs out of three. The tag that turns it off lives in .github/build-tags, every workflow step passes it, and two guards keep those steps honest.

None of that reached somebody building the program themselves. README.md offered three ways and not one carried the tag, so go install ...@latest, a distribution packager and a contributor running from a checkout all got that path. Both binaries reach it - internal/format/all registers AVIF unconditionally and go list -deps says cmd/tfg links it.

Failing at build time is the honest place: the alternative is a binary that works until it meets the wrong picture size. The other shapes were weighed and turned down - registering AVIF as unavailable keeps go install working but makes tfg formats answer differently depending on how the binary was made, and inverting the tag is a larger change to CI and the byte contract for no more safety. Owner's call.

Three analysis jobs read a build we do not ship

govulncheck, staticcheck and golangci-lint are each fetched with go run and pointed at ./..., which loads and type checks our packages exactly as the compiler does. The guard could not see them because it recognised only go build, go test and go vet, and said in its own comment that somebody else's tool was not ours to tag.

Two more came out with them: release.yml read the version with go run ./cmd/tfg version and no tags, and tools/preflight.py ran -race, vet, golangci-lint and govulncheck without them - so the local gate and CI were measuring different builds. All three tools were run with the flags before this was written (-tags for two, --build-tags for the linter), all clean.

What the refusal then found

Turning the missing tag into a compile error made four more callers show themselves, and one of them was a guard that skipped instead of failing when its build broke - the silent shape this project has paid for before. Two others matched the literal text of a workflow line and read an added flag as a job that had disappeared.

Measured while fixing them: the tags do not change the dependency set of cmd/tfg - go list -deps returns the same packages either way, zero lines differing. What they change is which files are compiled, which is why the analysis jobs needed them and the go list guards did not.

Guards

TestABuildWithoutTheBuildTagsRefusesAndSaysWhy asks the compiler rather than the source. TestTheInstallInstructionsCarryTheBuildTags keeps the one copy of the tag that cannot read the file honest, since it is typed before the repository exists. compilesOurCode now recognises go run pointed at our packages. Three new mutations, all caught, plus two existing ones repaired rather than counted.

🤖 Generated with Claude Code

Base automatically changed from security/recipe-depth to main September 6, 2026 13:26
donislawdev and others added 3 commits September 6, 2026 15:27
…them

Two halves of one gap, both found by an outside review on 2026-09-05 and
confirmed here.

A build without the tags now stops at the compiler. The AVIF encoder's
AVX2 path reads past the end of a buffer and takes the process down on
some picture sizes - measured at 640x256, in two runs out of three - and
the tag that turns it off has been in .github/build-tags since the encoder
arrived. Every workflow step passes it. None of the three ways README.md
offered for building it yourself did, so go install, a distribution
package and a build from a checkout all got that path. Both binaries reach
it: internal/format/all registers AVIF unconditionally.

Failing at build time is the honest place. The alternatives were weighed:
registering AVIF as unavailable keeps go install working but makes "tfg
formats" answer differently depending on how the binary was made, and
inverting the tag is a larger change to CI and the byte contract for no
more safety. Owner's call.

Three CI jobs analysed a build we do not ship. govulncheck, staticcheck
and golangci-lint are each fetched with "go run" and pointed at ./..., and
that loads and type checks our packages exactly as the compiler does - so
the reachability analysis ran through files no released binary contains.
The guard could not see them because it recognised only go build, go test
and go vet, and said in its own comment that somebody else's tool was not
ours to tag.

Two more places came out with them: release.yml read the version with
"go run ./cmd/tfg version" and no tags, and cmd/tfg links AVIF.

All three tools were run with the flags before this was written: -tags for
govulncheck and staticcheck, --build-tags for golangci-lint, all clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both matched the exact text of a workflow line, so adding the build tags
in front of a package pattern read as a job that had disappeared. They ask
for the package and the verb now, which is what the job has to do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two built cmd/tfg without the build tags, which stopped compiling when the
missing tag became a refusal - and one of them reported that as a SKIP
rather than as a failure, which is the silent shape this project has paid
for before. The third matched the literal text of a workflow line and read
an added flag as a job that had disappeared.

Measured while fixing them: the tags do NOT change the dependency set of
cmd/tfg - go list -deps returns the same packages either way, zero lines
differing - so the guards that ask what the binary links were answering
correctly. What the tags change is which files are compiled, which is why
the analysis jobs needed them and these builds do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@donislawdev
donislawdev merged commit 1853137 into main Sep 6, 2026
18 checks passed
@donislawdev
donislawdev deleted the security/build-tags branch September 6, 2026 13:48
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