fix(cve-gate): stderr must not be a tty, or grype returns nothing #99
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/gate-stderr-not-a-tty"
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?
Run 257:
grype produced output this gate cannot parse: unexpected end of JSON input— grype exiting 0, writing a 0-byte report, logging nothing.The mechanism
The gate exec's grype with its stdout on a pipe (it parses the JSON) and passes its own stderr straight through. Under act, that stderr is a terminal — so grype selects its interactive progress UI, and in that mode the report never reaches the pipe.
Measured on the runner in run 261, same binary and arguments throughout:
Redirecting the gate's stderr to a file makes grype choose its plain writer. The file is replayed to stderr immediately after, so
::error::and::warning::annotations still reach the log — the runner parses those from the stream, not from a particular fd.Verified the exit code still propagates through the redirect for all three contract values (
0/1/2) and that stderr survives.Why this only appeared now
The gate had been broken in five earlier ways that all killed it before grype ran — seccomp denying
clone, a 2 GB tmpfs, root-owned volumes, root withoutCAP_CHOWN. Run 257 was grype's first real execution, so it was also the first time this could surface.Worth saying plainly: I guessed twice here — first
--vex, then path resolution — and both were wrong. Each was disproved by a probe rather than by burning a release. The mechanism above came from testing the three stream combinations directly on the runner.⚠️ Not fixed here — VEX is suppressing nothing
The same run gave the first verdict the gate has ever produced:
grype's own summary agrees —
by status: 1 fixed, 2 not-fixed, 0 ignored— despite five reviewed statements being composed and passed with--vex. The finding it should suppress isgithub.com/docker/docker, which is exactly what CVE-2026-34040/41567/42306 are written for.So ADR-0005's VEX mechanism has never actually suppressed anything in a real scan — it couldn't, because no scan ever completed.
Impact: images in
warnmode (ci-runner: 8 critical / 136 high) are unaffected and will sign. cve-gate isenforce 0/0and will still fail. That's a separate defect and deserves its own change rather than being smuggled in here.Run 257 failed with "grype produced output this gate cannot parse: unexpected end of JSON input" — grype exiting 0, writing a 0-byte report, logging nothing at all. The gate exec's grype with its stdout on a PIPE, because it parses the JSON, and passes its own stderr straight through. Under act that stderr IS a terminal, so grype selects its interactive progress UI, and in that mode the report never reaches the pipe. Measured on the runner (run 261), same binary and arguments throughout: stdout=file, stderr=tty -> 29,495 bytes bash redirect: works stdout=pipe, stderr=tty -> 0 bytes the gate: broken stdout=pipe, stderr=file -> 30,006 bytes this fix: works Redirecting the gate's stderr to a file is enough to make grype choose its plain writer. The file is replayed to stderr immediately afterwards so ::error:: and :⚠️: annotations still reach the log — the runner parses those from the stream, not from a particular fd. Verified that the exit code still propagates through the redirect for all three contract values and that stderr survives. This is why the failure only appeared now. The gate has been broken in five earlier ways that all killed it BEFORE grype ran, so grype's first real execution was also the first time this could surface. Two rounds of my own guessing (--vex, then path resolution) were both wrong and both disproved by probe rather than by another release; the mechanism above came from testing the three stream combinations directly. NOT FIXED HERE, and visible for the first time in the same run: VEX is suppressing nothing. The gate reached a verdict of `high=1/0 OVER vex-suppressed=0` on cve-gate, and grype's own summary agrees — "by status: 1 fixed, 2 not-fixed, 0 ignored" — despite five reviewed statements being composed and passed with --vex. The finding it should suppress (github.com/docker/docker) is exactly what CVE-2026-34040/41567/42306 are written for. Images in `warn` mode are unaffected and will sign; cve-gate is `enforce 0/0` and will still fail. That is a separate defect and it gets its own change rather than being smuggled in here.ryangr0 referenced this pull request2026-08-08 05:05:58 +00:00