You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Surfaced while verifying #1835 (which closes #1833). Out of that issue's scope (#1833 is specifically about Go standard library CVEs from the toolchain) but found in the same scan, and it affects the shipped binary.
What
govulncheck -mode=binary against /app/cudly extracted from the image built on the #1835 branch, on both architectures:
Your code is affected by 2 vulnerabilities from 2 modules.
Standard-library findings: 0 (that is #1833, now fixed). The two remaining are dependency advisories:
GO-2026-5158
GO-2026-5932
At least one traces into golang.org/x/crypto.
Why it matters
These are in the binary we deploy, and they are reachable enough that govulncheck's symbol analysis surfaced them rather than filtering them out as unused code paths. /app/cudly is the API server.
Note also that CI's govulncheck runs in source mode over the six modules, so these should be visible there too. Check whether the CI job currently passes despite them, and if so, why. Either it is not failing on non-stdlib findings, or the source-mode analysis reaches a different conclusion from the binary-mode one. Both possibilities are worth understanding, because they determine whether anything would catch the next such advisory.
Fix direction
Confirm both advisories against the live vulnerability database and identify the fixed versions.
Bump the affected modules (go get -u, then go mod tidy) across every module in the workspace that pulls them in: this is a go.work monorepo with 6 modules (., pkg, providers/{aws,azure,gcp}, tests/e2e), and ./... only covers the current one.
Re-scan the built binary, not just the source, and assert with a sound method.
Verification bar
Assert on govulncheck's summary line, or use -format json and count OSV entries. Do not use a bare grep -c 'Module: stdlib': govulncheck v1.1.4 does not emit that string, it prints a bare Standard library line, so that pattern returns 0 whether or not findings exist. That vacuous check was found on #1835 and is exactly how an artifact ships dirty behind a green result.
Related: #1836 (nothing in the pipeline scans the image we ship), #1833 (the stdlib half, fixed by #1835).
Re-scoping this issue. PR #1840 clears one of the two advisories; the other cannot be fixed by any bump.#1840 deliberately says Refs rather than Closes so this stays open on its own merits.
GO-2026-5158: fixed
go.opentelemetry.io/otel, "baggage parsing no longer caps raw header length". Affected ranges 1.41.0-1.42.0 and 1.43.0-1.44.0; we were on v1.43.0, inside the second window. Fixed in v1.44.0.
Arrives transitively via cloud.google.com/go/storage from providers/gcp/services/cloudstorage, so the fix was bumping the indirect require rather than adding a direct one.
GO-2026-5932: no fix exists, and this is the decision
golang.org/x/crypto, "golang.org/x/crypto/openpgp is unmaintained, unsafe by design". The advisory has introduced: "0" and no fixed event, so every version of the module matches, permanently. There is no version to bump to, now or later.
It is not reachable. Two independent checks:
go list -deps ./... across all six modules finds no openpgp package anywhere. The repo uses x/crypto for bcrypt, chacha20poly1305, cryptobyte, hkdf and pkcs12.
The shipped binary's symbol table contains zero occurrences of openpgp. That method was validated against a known-called symbol before its absence was trusted: bcrypt.CompareHashAndPassword and bcrypt.GenerateFromPassword both resolve at function level, with 20 x/crypto/bcrypt references, so the check can demonstrably detect presence.
So the options are to accept it with the reasoning recorded, or to stop depending on x/crypto entirely, which is not a proportionate response to an unmaintained subpackage we never import.
Recommendation: accept and document, do not suppress. No ignore list, no -scan narrowing, no allowlist. If #1836 adds image scanning that fails on binary-mode findings, this advisory is the one case that will need an explicit, justified exception at that point, and it should be argued there rather than hidden here.
Why CI is green today, and why that is not a bug
CI runs govulncheck in source mode, which exits 0 when findings sit only at "packages you import" / "modules you require" level rather than "your code calls this". Every module returned exit 0 both before and after this work, while all three advisories appeared in the output. The gate is correctly scoped, not misconfigured.
Binary mode on the same tree exits 3. That gap between what we scan and what we ship is #1836, and #1840 does not close it.
Third advisory found, not in this issue's original scope
GO-2026-5841, OOB read in compress/s2, github.com/klauspost/compress 1.16.0-1.18.7, we are on v1.18.5, fixed in v1.18.7. Arrives transitively through testcontainers-go -> moby/go-archive from internal/database/postgres/testhelpers, so it is test-only and not in the shipped binary. Not bumped in #1840 to keep that PR to one concern. Worth a separate decision.
Reopened. This was auto-closed by the #1840 squash merge, and the cause was my wording rather than the intent.
The squash body contained the sentence "This does not close #1837". GitHub's closing-keyword parser matches close #1837 and does not read the negation, so a sentence written specifically to say the issue stays open is what closed it.
Nothing about the disposition changed: #1840 clears GO-2026-5158 only. GO-2026-5932 has introduced: "0" and no fixed event, so no bump can clear it, and it remains in the shipped binary while being unreachable. The re-scoping comment above stands and this issue is now scoped to that accept-or-remove decision.
Worth noting for anyone writing PR text here: phrase such statements without the trigger word, for example "#1840 does not resolve this issue" or "Refs #1837, partial".
Verified against origin/main @ 84a884e: no bump is left to make
Both advisories were checked against the live vuln DB (govulncheck v1.1.4, DB updated 2026-08-25) and against a real image build. Result: one is already fixed on main, the other has no published fix and never will. The shipped image today carries zero advisories with an available fix.
GO-2026-5932's OSV entry has a single range starting at 0 with no fixed event, and govulncheck prints Fixed in: N/A. The package is unmaintained by design; upstream's remedy is migrating off it, not a release. No version of golang.org/x/crypto clears it.
All six modules enumerated
Every module in the go.work workspace resolves go.opentelemetry.io/otel to v1.44.0, above both affected ranges:
$ docker build -t cudly:issue1837 . # exit 0
$ bash scripts/scan-shipped-image.sh cudly:issue1837 # exit 0
==> 2 Go binary/binaries in cudly:issue1837
-> /app/cudly (built with go1.26.6)
no fix GO-2026-5932 in golang.org/x/crypto (tolerated: no published fix)
=> /app/cudly: clean (1 advisory/advisories without a published fix)
-> /usr/local/bin/migrate (built with go1.26.6)
=> /usr/local/bin/migrate: clean (0 advisory/advisories without a published fix)
The count on /app/cudly is 1, not 2. GO-2026-5158 is gone.
GO-2026-5932 is a binary-mode artifact, not reachable code
The issue reasoned that these surfaced "because they are reachable enough that govulncheck's symbol analysis surfaced them rather than filtering them out as unused code paths". That does not hold for 5932.
go mod why golang.org/x/crypto/openpgp reports (main module does not need package golang.org/x/crypto/openpgp). Nothing in the import graph reaches it.
Control experiment: a 1.7MB program whose entire body is one bcrypt.GenerateFromPassword call, requiring golang.org/x/crypto v0.53.0 and importing no openpgp package, produces the identical finding, down to the same "Vulnerable symbols found" list:
The reason: GO-2026-5932's OSV entry lists affected import paths with no symbols array. With no symbols to match, govulncheck treats each listed package as wholly vulnerable and matches on module presence at a vulnerable version. Since the range is introduced: 0 with no fix, every binary that links any part of golang.org/x/crypto reports it. The /* suffix on each reported "symbol" is that package-level wildcard, not a real function found in the binary.
Why the source-mode CI job passes
Not because it ignores non-stdlib findings. Source mode has the import graph, sees that openpgp is never imported, and correctly reports nothing at all for 5932. Binary mode has no source to do that with, so it falls back to module-presence matching and produces the finding. Source mode is right here and binary mode is the noisy one, which is the opposite of the direction this issue assumed.
The separate point about exit codes does hold and is worth recording: govulncheck ./... exits 0 when findings are only at the "packages you import" or "modules you require" level, and non-zero only when a symbol is called. The current root-module scan ends with This scan also found 0 vulnerabilities in packages you import and 2 vulnerabilities in modules you require, but your code doesn't appear to call these vulnerabilities, and those two contribute nothing to the exit code.
Assertion soundness, demonstrated in both directions
Two binaries built from identical source, one against otel v1.43.0 and one against v1.44.0:
input
distinct OSV ids (-format json)
summary line
exit
grep -c 'Module: stdlib'
otel v1.43.0
1 (GO-2026-5158)
Your code is affected by 1 vulnerability from 1 module.
3
0
otel v1.44.0
0
No vulnerabilities found.
0
0
The JSON OSV count and the summary line separate the two cases. The Module: stdlib pattern returns 0 against a genuinely vulnerable binary, confirming on real input that it certifies anything.
The image gate's own teeth are covered by scripts/test-scan-shipped-image.sh, which passes 9/9 and asserts both directions, including PASS: stale-toolchain binary fails, and names its fixable advisories.
Conclusion
Nothing to change. scripts/scan-shipped-image.sh already names GO-2026-5932 in its "WHAT IT TOLERATES, AND WHY" header, correctly, including that it is unreachable in this binary. Suggest closing.
Separately: a new advisory is about to turn the source-mode job red
govulncheck ./... on the root module currently exits 3 on GO-2026-6253 (github.com/moby/go-archive, fixed in v0.3.0, published 2026-08-25). It is symbol-reachable via testcontainers from internal/database/postgres/testhelpers and from internal/config/store_postgres.go. Unrelated to this issue and left alone here; it needs its own.
Closing on the evidence posted above. No code change is needed, and the issue's central premise turned out to be wrong in an instructive way.
The two advisories
GO-2026-5158 (go.opentelemetry.io/otel) was already fixed on main by commit 5108b13c8. All six workspace modules resolve otel to v1.44.0, above both affected ranges ([1.41.0,1.42.0) and [1.43.0,1.44.0)). Enumerated per module, not sampled.
GO-2026-5932 (golang.org/x/crypto, openpgp) has no published fix. Its OSV entry carries a single range starting at introduced: 0 with no fixed event; govulncheck prints Fixed in: N/A. No bump clears it, so the fix direction in this issue was not achievable as written.
So the count is 1, not 2, and the remaining one is unfixable by upgrading.
The premise that did not hold
This issue assumed both advisories were "reachable enough that govulncheck's symbol analysis surfaced them rather than filtering them out as unused code paths". For 5932 that is false.
go mod why golang.org/x/crypto/openpgp reports (main module does not need package ...). A 1.7MB control binary whose entire body is one bcrypt call, importing no openpgp package, produces the identical finding including the same symbol list. The cause: 5932's OSV entry lists affected import paths with no symbols array, so govulncheck matches at module-presence granularity. The /* suffix on each apparent "symbol" is that wildcard, not a function located in the binary.
Why source mode passes, corrected
Not the exit-code mechanism, for this advisory. Source mode has the import graph, sees openpgp is never imported, and correctly reports nothing. Binary mode has no source to analyse and falls back to module presence. Source mode is right here; binary mode is the noisy one - the opposite of what this issue assumed.
The exit-code behaviour is real but separate: the root scan ends with 0 vulnerabilities in packages you import and 2 vulnerabilities in modules you require, but your code doesn't appear to call these, and those contribute nothing to the exit code.
The vacuous-check warning was worth stating, and was re-proven
Built from identical source against otel v1.43.0 and v1.44.0:
input
distinct OSV ids
summary line
exit
grep -c 'Module: stdlib'
v1.43.0
1 (GO-2026-5158)
Your code is affected by 1 vulnerability from 1 module.
3
0
v1.44.0
0
No vulnerabilities found.
0
0
The vacuous pattern returns 0 against a genuinely vulnerable binary. Assert on the summary line or the JSON OSV count.
One methodology note worth keeping
An initial go tool nm probe returned zero openpgp symbols, which looked like confirmation of unreachability. It had actually failed - no symbol section, because the binary is -s -w stripped. An empty result from a broken probe is not evidence. The control-binary experiment replaced it.
Current state
scripts/scan-shipped-image.sh (landed for #1836) already names GO-2026-5932 in its tolerance header, correctly, including that it is unreachable in this binary. CI builds the image and runs that scanner plus its self-test (9/9 passing). There is no bump, no doc and no guard left to add.
The separate advisory found during this work - GO-2026-6253 in moby/go-archive, which is symbol-reachable and does have a fix - is filed as #1894 (p0, currently red on main) and is being fixed there.
Activity
sec(build): build the shipped image on go1.26.6, including the migrat…
cristim commentedon Aug 18, 2026
Re-scoping this issue. PR #1840 clears one of the two advisories; the other cannot be fixed by any bump. #1840 deliberately says
Refsrather thanClosesso this stays open on its own merits.GO-2026-5158: fixed
go.opentelemetry.io/otel, "baggage parsing no longer caps raw header length". Affected ranges 1.41.0-1.42.0 and 1.43.0-1.44.0; we were on v1.43.0, inside the second window. Fixed in v1.44.0.Arrives transitively via
cloud.google.com/go/storagefromproviders/gcp/services/cloudstorage, so the fix was bumping the indirect require rather than adding a direct one.GO-2026-5932: no fix exists, and this is the decision
golang.org/x/crypto, "golang.org/x/crypto/openpgp is unmaintained, unsafe by design". The advisory hasintroduced: "0"and nofixedevent, so every version of the module matches, permanently. There is no version to bump to, now or later.It is not reachable. Two independent checks:
go list -deps ./...across all six modules finds no openpgp package anywhere. The repo usesx/cryptofor bcrypt, chacha20poly1305, cryptobyte, hkdf and pkcs12.openpgp. That method was validated against a known-called symbol before its absence was trusted:bcrypt.CompareHashAndPasswordandbcrypt.GenerateFromPasswordboth resolve at function level, with 20x/crypto/bcryptreferences, so the check can demonstrably detect presence.So the options are to accept it with the reasoning recorded, or to stop depending on
x/cryptoentirely, which is not a proportionate response to an unmaintained subpackage we never import.Recommendation: accept and document, do not suppress. No ignore list, no
-scannarrowing, no allowlist. If #1836 adds image scanning that fails on binary-mode findings, this advisory is the one case that will need an explicit, justified exception at that point, and it should be argued there rather than hidden here.Why CI is green today, and why that is not a bug
CI runs govulncheck in source mode, which exits 0 when findings sit only at "packages you import" / "modules you require" level rather than "your code calls this". Every module returned exit 0 both before and after this work, while all three advisories appeared in the output. The gate is correctly scoped, not misconfigured.
Binary mode on the same tree exits 3. That gap between what we scan and what we ship is #1836, and #1840 does not close it.
Third advisory found, not in this issue's original scope
GO-2026-5841, OOB read in
compress/s2,github.com/klauspost/compress1.16.0-1.18.7, we are on v1.18.5, fixed in v1.18.7. Arrives transitively throughtestcontainers-go->moby/go-archivefrominternal/database/postgres/testhelpers, so it is test-only and not in the shipped binary. Not bumped in #1840 to keep that PR to one concern. Worth a separate decision.cristim commentedon Aug 18, 2026
Reopened. This was auto-closed by the #1840 squash merge, and the cause was my wording rather than the intent.
The squash body contained the sentence "This does not close #1837". GitHub's closing-keyword parser matches
close #1837and does not read the negation, so a sentence written specifically to say the issue stays open is what closed it.Nothing about the disposition changed: #1840 clears GO-2026-5158 only. GO-2026-5932 has
introduced: "0"and nofixedevent, so no bump can clear it, and it remains in the shipped binary while being unreachable. The re-scoping comment above stands and this issue is now scoped to that accept-or-remove decision.Worth noting for anyone writing PR text here: phrase such statements without the trigger word, for example "#1840 does not resolve this issue" or "Refs #1837, partial".
sec(ci): scan the Go binaries in the shipped image, not just the sour…
cristim commentedon Aug 25, 2026
Verified against
origin/main@ 84a884e: no bump is left to makeBoth advisories were checked against the live vuln DB (govulncheck v1.1.4, DB updated 2026-08-25) and against a real image build. Result: one is already fixed on main, the other has no published fix and never will. The shipped image today carries zero advisories with an available fix.
Per-advisory
go.opentelemetry.io/otel[1.41.0, 1.42.0)and[1.43.0, 1.44.0)golang.org/x/crypto(openpgp)introduced: 0, nofixedeventGO-2026-5932's OSV entry has a single range starting at
0with nofixedevent, and govulncheck printsFixed in: N/A. The package is unmaintained by design; upstream's remedy is migrating off it, not a release. No version ofgolang.org/x/cryptoclears it.All six modules enumerated
Every module in the
go.workworkspace resolvesgo.opentelemetry.io/otelto v1.44.0, above both affected ranges:No module needed a change.
Shipped-image scan (real image, not source)
The count on
/app/cudlyis 1, not 2. GO-2026-5158 is gone.GO-2026-5932 is a binary-mode artifact, not reachable code
The issue reasoned that these surfaced "because they are reachable enough that govulncheck's symbol analysis surfaced them rather than filtering them out as unused code paths". That does not hold for 5932.
go mod why golang.org/x/crypto/openpgpreports(main module does not need package golang.org/x/crypto/openpgp). Nothing in the import graph reaches it.Control experiment: a 1.7MB program whose entire body is one
bcrypt.GenerateFromPasswordcall, requiringgolang.org/x/crypto v0.53.0and importing no openpgp package, produces the identical finding, down to the same "Vulnerable symbols found" list:The reason: GO-2026-5932's OSV entry lists affected import paths with no
symbolsarray. With no symbols to match, govulncheck treats each listed package as wholly vulnerable and matches on module presence at a vulnerable version. Since the range isintroduced: 0with no fix, every binary that links any part ofgolang.org/x/cryptoreports it. The/*suffix on each reported "symbol" is that package-level wildcard, not a real function found in the binary.Why the source-mode CI job passes
Not because it ignores non-stdlib findings. Source mode has the import graph, sees that
openpgpis never imported, and correctly reports nothing at all for 5932. Binary mode has no source to do that with, so it falls back to module-presence matching and produces the finding. Source mode is right here and binary mode is the noisy one, which is the opposite of the direction this issue assumed.The separate point about exit codes does hold and is worth recording:
govulncheck ./...exits 0 when findings are only at the "packages you import" or "modules you require" level, and non-zero only when a symbol is called. The current root-module scan ends withThis scan also found 0 vulnerabilities in packages you import and 2 vulnerabilities in modules you require, but your code doesn't appear to call these vulnerabilities, and those two contribute nothing to the exit code.Assertion soundness, demonstrated in both directions
Two binaries built from identical source, one against otel v1.43.0 and one against v1.44.0:
-format json)grep -c 'Module: stdlib'Your code is affected by 1 vulnerability from 1 module.No vulnerabilities found.The JSON OSV count and the summary line separate the two cases. The
Module: stdlibpattern returns 0 against a genuinely vulnerable binary, confirming on real input that it certifies anything.The image gate's own teeth are covered by
scripts/test-scan-shipped-image.sh, which passes 9/9 and asserts both directions, includingPASS: stale-toolchain binary fails, and names its fixable advisories.Conclusion
Nothing to change.
scripts/scan-shipped-image.shalready names GO-2026-5932 in its "WHAT IT TOLERATES, AND WHY" header, correctly, including that it is unreachable in this binary. Suggest closing.Separately: a new advisory is about to turn the source-mode job red
govulncheck ./...on the root module currently exits 3 on GO-2026-6253 (github.com/moby/go-archive, fixed in v0.3.0, published 2026-08-25). It is symbol-reachable viatestcontainersfrominternal/database/postgres/testhelpersand frominternal/config/store_postgres.go. Unrelated to this issue and left alone here; it needs its own.cristim commentedon Aug 25, 2026
Closing on the evidence posted above. No code change is needed, and the issue's central premise turned out to be wrong in an instructive way.
The two advisories
GO-2026-5158 (
go.opentelemetry.io/otel) was already fixed onmainby commit5108b13c8. All six workspace modules resolve otel to v1.44.0, above both affected ranges ([1.41.0,1.42.0)and[1.43.0,1.44.0)). Enumerated per module, not sampled.GO-2026-5932 (
golang.org/x/crypto, openpgp) has no published fix. Its OSV entry carries a single range starting atintroduced: 0with nofixedevent; govulncheck printsFixed in: N/A. No bump clears it, so the fix direction in this issue was not achievable as written.So the count is 1, not 2, and the remaining one is unfixable by upgrading.
The premise that did not hold
This issue assumed both advisories were "reachable enough that govulncheck's symbol analysis surfaced them rather than filtering them out as unused code paths". For 5932 that is false.
go mod why golang.org/x/crypto/openpgpreports(main module does not need package ...). A 1.7MB control binary whose entire body is onebcryptcall, importing no openpgp package, produces the identical finding including the same symbol list. The cause: 5932's OSV entry lists affected import paths with nosymbolsarray, so govulncheck matches at module-presence granularity. The/*suffix on each apparent "symbol" is that wildcard, not a function located in the binary.Why source mode passes, corrected
Not the exit-code mechanism, for this advisory. Source mode has the import graph, sees openpgp is never imported, and correctly reports nothing. Binary mode has no source to analyse and falls back to module presence. Source mode is right here; binary mode is the noisy one - the opposite of what this issue assumed.
The exit-code behaviour is real but separate: the root scan ends with
0 vulnerabilities in packages you import and 2 vulnerabilities in modules you require, but your code doesn't appear to call these, and those contribute nothing to the exit code.The vacuous-check warning was worth stating, and was re-proven
Built from identical source against otel v1.43.0 and v1.44.0:
grep -c 'Module: stdlib'Your code is affected by 1 vulnerability from 1 module.No vulnerabilities found.The vacuous pattern returns 0 against a genuinely vulnerable binary. Assert on the summary line or the JSON OSV count.
One methodology note worth keeping
An initial
go tool nmprobe returned zero openpgp symbols, which looked like confirmation of unreachability. It had actually failed -no symbol section, because the binary is-s -wstripped. An empty result from a broken probe is not evidence. The control-binary experiment replaced it.Current state
scripts/scan-shipped-image.sh(landed for #1836) already names GO-2026-5932 in its tolerance header, correctly, including that it is unreachable in this binary. CI builds the image and runs that scanner plus its self-test (9/9 passing). There is no bump, no doc and no guard left to add.The separate advisory found during this work - GO-2026-6253 in
moby/go-archive, which is symbol-reachable and does have a fix - is filed as #1894 (p0, currently red onmain) and is being fixed there.sec(build): build the shipped image on go1.26.6, including the migrat…
sec(deps): bump go.opentelemetry.io/otel to v1.44.0 for GO-2026-5158
sec(ci): scan the Go binaries in the shipped image, not just the sour…