From c226710ed4aa4ef2ec84fe9f5aff3ee559c428db Mon Sep 17 00:00:00 2001 From: Benjamin Borbe Date: Sat, 20 Jun 2026 14:47:13 +0200 Subject: [PATCH 1/3] templates: surface real govulncheck errors, swallow only the known x/tools generics panic Maintainer-watcher bot review on multiple downstream PRs flagged the prior '2>/dev/null' pattern as a critical issue: it swallows ALL stderr from govulncheck, so genuine failures (network, bad args, permissions) silently produce a false 'No unignored vulnerabilities found' result. Upgrade both Makefile.service and Makefile.library to a discriminating pattern: capture stderr to a temp file, check the govulncheck exit code, and only swallow when stderr matches the known panic signature from 'golang.org/x/tools' v0.46.0 ('ForEachElement called on type containing *types.TypeParam'). Any other failure mode is surfaced with its exit code propagated. Downstream impact: open Makefile-modernization PRs (cron, backup, ping, git-rest, beactive, ip, mqtt-kafka-connector, task-watcher, git-sync, teamvault-utils) will be re-synced to this pattern in follow-up commits. --- templates/Makefile.library | 29 ++++++++++++++++++++++------- templates/Makefile.service | 29 ++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/templates/Makefile.library b/templates/Makefile.library index 4acfaec..e353c71 100644 --- a/templates/Makefile.library +++ b/templates/Makefile.library @@ -54,17 +54,32 @@ errcheck: VULNCHECK_IGNORE ?= GO-2026-4923 GO-2026-4514 GO-2022-0470 GO-2026-4772 GO-2026-4771 +# Known-benign govulncheck failure modes we swallow. golang.org/x/tools v0.46.0 +# panics on packages containing generic *types.TypeParam during SSA analysis +# (govulncheck v1.3.0+ surface via RuntimeTypes/AllFunctions). We treat that as +# "no findings" because the panic happens AFTER the package scan; any real +# vulnerabilities would have been emitted as JSON on stdout before the panic. +# Any OTHER govulncheck failure (network, bad args, permissions) is surfaced. .PHONY: vulncheck vulncheck: @PKGS="$(shell go list -mod=mod ./... | grep -v /vendor/)"; \ IGNORE_JSON=$$(printf '%s\n' $(VULNCHECK_IGNORE) | jq -R . | jq -s .); \ - REMAIN=$$(go run golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) -format json $$PKGS 2>/dev/null | \ - jq -rs --argjson ignore "$$IGNORE_JSON" \ - '(map(select(.osv != null)) | map({key: .osv.id, value: (.osv.summary // "")}) | from_entries) as $$sum | \ - map(select(.finding != null) | .finding) | \ - map(select(.osv as $$o | $$ignore | index($$o) | not)) | \ - map("\(.osv)\t\(.trace[-1].module)@\(.trace[-1].version) -> \(.fixed_version)\t\($$sum[.osv] // "")") | \ - unique | .[]'); \ + ERR=$$(mktemp); \ + OUT=$$(go run golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) -format json $$PKGS 2>$$ERR); \ + RC=$$?; \ + if [ $$RC -ne 0 ] && ! grep -q "ForEachElement called on type containing" "$$ERR"; then \ + echo "govulncheck failed (exit $$RC):" >&2; \ + cat "$$ERR" >&2; \ + rm -f "$$ERR"; \ + exit $$RC; \ + fi; \ + rm -f "$$ERR"; \ + REMAIN=$$(printf '%s' "$$OUT" | jq -rs --argjson ignore "$$IGNORE_JSON" \ + '(map(select(.osv != null)) | map({key: .osv.id, value: (.osv.summary // "")}) | from_entries) as $$sum | \ + map(select(.finding != null) | .finding) | \ + map(select(.osv as $$o | $$ignore | index($$o) | not)) | \ + map("\(.osv)\t\(.trace[-1].module)@\(.trace[-1].version) -> \(.fixed_version)\t\($$sum[.osv] // "")") | \ + unique | .[]'); \ if [ -n "$$REMAIN" ]; then \ echo "Unexpected vulnerabilities (ignored: $(VULNCHECK_IGNORE)):"; \ printf '%s\n' "$$REMAIN" | column -t -s "$$(printf '\t')"; \ diff --git a/templates/Makefile.service b/templates/Makefile.service index cc5fcd9..c9310fb 100644 --- a/templates/Makefile.service +++ b/templates/Makefile.service @@ -61,17 +61,32 @@ errcheck: VULNCHECK_IGNORE ?= GO-2026-4923 GO-2026-4514 GO-2022-0470 GO-2026-4772 GO-2026-4771 +# Known-benign govulncheck failure modes we swallow. golang.org/x/tools v0.46.0 +# panics on packages containing generic *types.TypeParam during SSA analysis +# (govulncheck v1.3.0+ surface via RuntimeTypes/AllFunctions). We treat that as +# "no findings" because the panic happens AFTER the package scan; any real +# vulnerabilities would have been emitted as JSON on stdout before the panic. +# Any OTHER govulncheck failure (network, bad args, permissions) is surfaced. .PHONY: vulncheck vulncheck: @PKGS="$(shell go list -mod=mod ./... | grep -v /vendor/)"; \ IGNORE_JSON=$$(printf '%s\n' $(VULNCHECK_IGNORE) | jq -R . | jq -s .); \ - REMAIN=$$(go run golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) -format json $$PKGS 2>/dev/null | \ - jq -rs --argjson ignore "$$IGNORE_JSON" \ - '(map(select(.osv != null)) | map({key: .osv.id, value: (.osv.summary // "")}) | from_entries) as $$sum | \ - map(select(.finding != null) | .finding) | \ - map(select(.osv as $$o | $$ignore | index($$o) | not)) | \ - map("\(.osv)\t\(.trace[-1].module)@\(.trace[-1].version) -> \(.fixed_version)\t\($$sum[.osv] // "")") | \ - unique | .[]'); \ + ERR=$$(mktemp); \ + OUT=$$(go run golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) -format json $$PKGS 2>$$ERR); \ + RC=$$?; \ + if [ $$RC -ne 0 ] && ! grep -q "ForEachElement called on type containing" "$$ERR"; then \ + echo "govulncheck failed (exit $$RC):" >&2; \ + cat "$$ERR" >&2; \ + rm -f "$$ERR"; \ + exit $$RC; \ + fi; \ + rm -f "$$ERR"; \ + REMAIN=$$(printf '%s' "$$OUT" | jq -rs --argjson ignore "$$IGNORE_JSON" \ + '(map(select(.osv != null)) | map({key: .osv.id, value: (.osv.summary // "")}) | from_entries) as $$sum | \ + map(select(.finding != null) | .finding) | \ + map(select(.osv as $$o | $$ignore | index($$o) | not)) | \ + map("\(.osv)\t\(.trace[-1].module)@\(.trace[-1].version) -> \(.fixed_version)\t\($$sum[.osv] // "")") | \ + unique | .[]'); \ if [ -n "$$REMAIN" ]; then \ echo "Unexpected vulnerabilities (ignored: $(VULNCHECK_IGNORE)):"; \ printf '%s\n' "$$REMAIN" | column -t -s "$$(printf '\t')"; \ From 5a6b38244fd744c15998793cd201236cce0dc7b5 Mon Sep 17 00:00:00 2001 From: Benjamin Borbe Date: Sat, 20 Jun 2026 14:58:54 +0200 Subject: [PATCH 2/3] templates(vulncheck): trap on temp file to survive Ctrl-C between mktemp and rm Local pr-review on downstream cron PR (go-quality-assistant agent) flagged the temp-file leak path: if the recipe is interrupted between 'ERR=$(mktemp)' and the explicit 'rm -f', the file persists in $TMPDIR. Replace the two explicit cleanup calls with 'trap rm -f "$ERR" EXIT INT TERM' so cleanup fires on every exit path (normal, SIGINT, SIGTERM) and we lose one source line in each branch. --- templates/Makefile.library | 3 +-- templates/Makefile.service | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/templates/Makefile.library b/templates/Makefile.library index e353c71..f8ef7b5 100644 --- a/templates/Makefile.library +++ b/templates/Makefile.library @@ -65,15 +65,14 @@ vulncheck: @PKGS="$(shell go list -mod=mod ./... | grep -v /vendor/)"; \ IGNORE_JSON=$$(printf '%s\n' $(VULNCHECK_IGNORE) | jq -R . | jq -s .); \ ERR=$$(mktemp); \ + trap 'rm -f "$$ERR"' EXIT INT TERM; \ OUT=$$(go run golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) -format json $$PKGS 2>$$ERR); \ RC=$$?; \ if [ $$RC -ne 0 ] && ! grep -q "ForEachElement called on type containing" "$$ERR"; then \ echo "govulncheck failed (exit $$RC):" >&2; \ cat "$$ERR" >&2; \ - rm -f "$$ERR"; \ exit $$RC; \ fi; \ - rm -f "$$ERR"; \ REMAIN=$$(printf '%s' "$$OUT" | jq -rs --argjson ignore "$$IGNORE_JSON" \ '(map(select(.osv != null)) | map({key: .osv.id, value: (.osv.summary // "")}) | from_entries) as $$sum | \ map(select(.finding != null) | .finding) | \ diff --git a/templates/Makefile.service b/templates/Makefile.service index c9310fb..5cf8886 100644 --- a/templates/Makefile.service +++ b/templates/Makefile.service @@ -72,15 +72,14 @@ vulncheck: @PKGS="$(shell go list -mod=mod ./... | grep -v /vendor/)"; \ IGNORE_JSON=$$(printf '%s\n' $(VULNCHECK_IGNORE) | jq -R . | jq -s .); \ ERR=$$(mktemp); \ + trap 'rm -f "$$ERR"' EXIT INT TERM; \ OUT=$$(go run golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) -format json $$PKGS 2>$$ERR); \ RC=$$?; \ if [ $$RC -ne 0 ] && ! grep -q "ForEachElement called on type containing" "$$ERR"; then \ echo "govulncheck failed (exit $$RC):" >&2; \ cat "$$ERR" >&2; \ - rm -f "$$ERR"; \ exit $$RC; \ fi; \ - rm -f "$$ERR"; \ REMAIN=$$(printf '%s' "$$OUT" | jq -rs --argjson ignore "$$IGNORE_JSON" \ '(map(select(.osv != null)) | map({key: .osv.id, value: (.osv.summary // "")}) | from_entries) as $$sum | \ map(select(.finding != null) | .finding) | \ From d6a2c62a1454811a507df12136e6b49c52f7b87e Mon Sep 17 00:00:00 2001 From: Benjamin Borbe Date: Sat, 20 Jun 2026 16:07:44 +0200 Subject: [PATCH 3/3] templates(vulncheck): robust panic detection + Go 1.26.3 stdlib ignores Address bot review on coding#57, ip#3, task-watcher#1, backup#11: - grep pattern: match the typesinternal.ForEachElement stack-frame name OR the panic message (was message-only). Stack frame is stable across x/tools versions; if the message text ever changes, the package+function name still matches. - VULNCHECK_IGNORE: extend with GO-2026-5037/5038/5039 (Go 1.26.3 stdlib vulns in crypto/x509, mime, net/textproto; fixed in 1.26.4). Marked with a removal comment so they're cleared once CI/base images move to 1.26.4+. --- templates/Makefile.library | 7 +++++-- templates/Makefile.service | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/templates/Makefile.library b/templates/Makefile.library index f8ef7b5..3aa02f0 100644 --- a/templates/Makefile.library +++ b/templates/Makefile.library @@ -52,7 +52,10 @@ vet: errcheck: go run github.com/kisielk/errcheck@$(ERRCHECK_VERSION) -ignore '(Close|Write|Fprint)' $(shell go list -mod=mod ./... | grep -v /vendor/ | grep -v k8s/client) -VULNCHECK_IGNORE ?= GO-2026-4923 GO-2026-4514 GO-2022-0470 GO-2026-4772 GO-2026-4771 +# GO-2026-5037/5038/5039 are Go 1.26.3 stdlib vulns (crypto/x509, mime, net/textproto), +# fixed in 1.26.4 — remove from this list once all CI runners and base images are on 1.26.4+. +VULNCHECK_IGNORE ?= GO-2026-4923 GO-2026-4514 GO-2022-0470 GO-2026-4772 GO-2026-4771 \ + GO-2026-5037 GO-2026-5038 GO-2026-5039 # Known-benign govulncheck failure modes we swallow. golang.org/x/tools v0.46.0 # panics on packages containing generic *types.TypeParam during SSA analysis @@ -68,7 +71,7 @@ vulncheck: trap 'rm -f "$$ERR"' EXIT INT TERM; \ OUT=$$(go run golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) -format json $$PKGS 2>$$ERR); \ RC=$$?; \ - if [ $$RC -ne 0 ] && ! grep -q "ForEachElement called on type containing" "$$ERR"; then \ + if [ $$RC -ne 0 ] && ! grep -qE "typesinternal\.ForEachElement|ForEachElement called on type containing" "$$ERR"; then \ echo "govulncheck failed (exit $$RC):" >&2; \ cat "$$ERR" >&2; \ exit $$RC; \ diff --git a/templates/Makefile.service b/templates/Makefile.service index 5cf8886..39d8615 100644 --- a/templates/Makefile.service +++ b/templates/Makefile.service @@ -59,7 +59,10 @@ vet: errcheck: go run github.com/kisielk/errcheck@$(ERRCHECK_VERSION) -ignore '(Close|Write|Fprint)' $(shell go list -mod=mod ./... | grep -v /vendor/ | grep -v k8s/client) -VULNCHECK_IGNORE ?= GO-2026-4923 GO-2026-4514 GO-2022-0470 GO-2026-4772 GO-2026-4771 +# GO-2026-5037/5038/5039 are Go 1.26.3 stdlib vulns (crypto/x509, mime, net/textproto), +# fixed in 1.26.4 — remove from this list once all CI runners and base images are on 1.26.4+. +VULNCHECK_IGNORE ?= GO-2026-4923 GO-2026-4514 GO-2022-0470 GO-2026-4772 GO-2026-4771 \ + GO-2026-5037 GO-2026-5038 GO-2026-5039 # Known-benign govulncheck failure modes we swallow. golang.org/x/tools v0.46.0 # panics on packages containing generic *types.TypeParam during SSA analysis @@ -75,7 +78,7 @@ vulncheck: trap 'rm -f "$$ERR"' EXIT INT TERM; \ OUT=$$(go run golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) -format json $$PKGS 2>$$ERR); \ RC=$$?; \ - if [ $$RC -ne 0 ] && ! grep -q "ForEachElement called on type containing" "$$ERR"; then \ + if [ $$RC -ne 0 ] && ! grep -qE "typesinternal\.ForEachElement|ForEachElement called on type containing" "$$ERR"; then \ echo "govulncheck failed (exit $$RC):" >&2; \ cat "$$ERR" >&2; \ exit $$RC; \