Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .github/workflows/govulncheck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: govulncheck

on:
push:
branches:
- main
pull_request:

jobs:
govulncheck:
runs-on: ubuntu-latest

steps:
# 1.25.x tracks the same Go minor the Dockerfile builds with
# (golang:1.25, pulled fresh), so CI and a local `kool run govulncheck`
# do not disagree about which stdlib advisories still apply.
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: 1.25.x

- name: Checkout code
uses: actions/checkout@v4

# Installed rather than `go run`, which collapses every non-zero exit to 1
# and would hide the difference between "vulnerabilities found" and
# "the scan itself broke".
- name: Install govulncheck
run: go install golang.org/x/vuln/cmd/govulncheck@latest

# Report-only on purpose. govulncheck has no suppression file (unlike
# grype's .grype.yaml), and GO-2026-5932 -- x/crypto/openpgp is
# unmaintained, reached through rhysd/go-github-selfupdate -- has no fix
# available, so gating on it would pin the build red until that dependency
# is replaced. Stdlib findings also churn with every Go patch release.
# A genuine tool failure still fails the job.
- name: Run govulncheck
run: |
set +e
govulncheck ./... > govulncheck.out 2>&1
status=$?
set -e

cat govulncheck.out

{
echo "## govulncheck"
echo
case "$status" in
0) echo "No reachable vulnerabilities found." ;;
3) echo "Reachable vulnerabilities found. Report-only: this does not fail the build." ;;
*) echo "govulncheck failed to run (exit $status)." ;;
esac
echo
echo "<details><summary>Full output</summary>"
echo
echo '```'
cat govulncheck.out
echo '```'
echo
echo "</details>"
} >> "$GITHUB_STEP_SUMMARY"

# 3 means vulnerabilities were found, which we report without failing.
# Anything else non-zero is the scan breaking, and should fail.
if [ "$status" -ne 0 ] && [ "$status" -ne 3 ]; then
exit "$status"
fi
39 changes: 38 additions & 1 deletion .github/workflows/scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ jobs:
grype:
runs-on: ubuntu-latest

env:
SEVERITY_CUTOFF: critical

steps:
- name: Checkout code
uses: actions/checkout@v4
Expand All @@ -18,8 +21,42 @@ jobs:
run: docker build --pull -t kooldev/kool:4scan .

- name: Scan image
id: scan
uses: anchore/scan-action@v6
with:
image: "kooldev/kool:4scan"
fail-build: true
severity-cutoff: critical
severity-cutoff: ${{ env.SEVERITY_CUTOFF }}
# report CVE-2026-46595 rather than GHSA-x527-x647-q7gg
by-cve: true

# The scan step writes its SARIF report to a temp file that is discarded
# when the job ends, and on failure logs only "Failed minimum severity
# level" with no CVE, package or version. Print the findings so a red
# build is diagnosable from the log alone. `always()` is required: the
# scan step fails the job, so anything without it never runs -- precisely
# when the output is needed.
- name: Report findings
if: always()
env:
SARIF: ${{ steps.scan.outputs.sarif }}
run: |
if [ ! -f "$SARIF" ]; then
echo "No SARIF report produced - the scan did not run to completion."
exit 0
fi

echo "Findings by severity:"
jq -r '[.runs[].results[].message.text
| capture("^A (?<s>[a-z]+) vulnerability").s]
| group_by(.) | map(" \(.[0]): \(length)")[]' "$SARIF"

echo
echo "Findings at or above the '$SEVERITY_CUTOFF' cutoff (these fail the build):"
jq -r --arg cutoff "$SEVERITY_CUTOFF" '
["negligible", "low", "medium", "high", "critical"] as $order
| ($order | index($cutoff)) as $min
| .runs[].results[]
| (.message.text | capture("^A (?<s>[a-z]+) vulnerability").s) as $sev
| select(($order | index($sev)) >= $min)
| " \(.ruleId)\n \(.message.text)"' "$SARIF"
15 changes: 6 additions & 9 deletions .grype.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@
# and cannot fix ourselves β€” each entry must link to the upstream tracker and
# be revisited (and ideally removed) when upstream rebuilds.
#
# Anything in our own code or our own Go dependencies gets fixed, not ignored.
#
# See docs/01-Getting-Started/5-CI-Integration.md#known-security-caveat for the
# user-facing context.

ignore:
# Inherited from docker:29-cli (Docker 29.4.1). The bundled
# /usr/local/libexec/docker/cli-plugins/{docker-compose,docker-buildx}
# are Go binaries built with go1.25.8; CVE-2026-27143 is fixed in go1.25.9.
# Will clear automatically when docker-library/docker rebuilds the 29-cli
# image with a newer Go toolchain. Tracked upstream:
# https://github.com/docker-library/docker
# Remove this entry once `grype kooldev/kool:<tag>` no longer reports it.
- vulnerability: CVE-2026-27143
# Currently empty: the previous entry (CVE-2026-27143, inherited from the
# docker:29-cli compose/buildx plugins built with go1.25.8) cleared once
# docker-library/docker rebuilt the image with a newer Go toolchain.
ignore: []
4 changes: 2 additions & 2 deletions docs/01-Getting-Started/5-CI-Integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ jobs:

## Known security caveat

The `kooldev/kool` image is built `FROM docker:29-cli`, which bundles `docker-compose` and `docker-buildx` CLI plugins as pre-built Go binaries that we inherit as-is. At any given time those plugins may have been compiled with a Go toolchain that has since received CVE advisories β€” for example a `go1.25.8` build of the bundled compose plugin triggers `CVE-2026-27143` until the upstream [`docker-library/docker`](https://github.com/docker-library/docker) image is rebuilt with a newer Go toolchain.
The `kooldev/kool` image is built `FROM docker:29-cli`, which bundles `docker-compose` and `docker-buildx` CLI plugins as pre-built Go binaries that we inherit as-is. At any given time those plugins may have been compiled with a Go toolchain that has since received CVE advisories, and a scanner will report them against our image. Such findings clear on their own once the upstream [`docker-library/docker`](https://github.com/docker-library/docker) image is rebuilt with a newer Go toolchain β€” there is no action we can take on our side beyond following the base image.

**The advisory is in the Go standard library of Docker's own plugin binaries, not in kool.** For security-sensitive pipelines, consider either:
**Those advisories are in the Go standard library of Docker's own plugin binaries, not in kool.** Anything in kool's own code or dependencies we fix rather than suppress. For security-sensitive pipelines, consider either:

- Installing **kool** as a native binary on the runner (`curl -fsSL https://kool.dev/install | bash`) instead of using the image, which avoids the upstream plugin surface entirely,
- Pinning a specific `kooldev/kool:VERSION` tag and re-scanning when you adopt a new version.
Expand Down
9 changes: 4 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ require (
github.com/rhysd/go-github-selfupdate v1.2.3
github.com/spf13/afero v1.15.0
github.com/spf13/cobra v1.10.2
golang.org/x/net v0.53.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sys v0.43.0
golang.org/x/term v0.42.0
golang.org/x/text v0.36.0 // indirect
golang.org/x/sys v0.45.0
golang.org/x/term v0.43.0
golang.org/x/text v0.39.0 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
Expand Down Expand Up @@ -51,5 +50,5 @@ require (
github.com/tcnksm/go-gitconfig v0.1.2 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/crypto v0.50.0 // indirect
golang.org/x/crypto v0.52.0 // indirect
)
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
Expand All @@ -130,8 +130,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
Expand All @@ -148,20 +148,20 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
Expand Down
5 changes: 5 additions & 0 deletions kool.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ scripts:
install: mv ./kool-cli /usr/local/bin/kool
fmt: kool run go:linux fmt ./...
lint: kool docker --volume=kool_gopath:/go golangci/golangci-lint:v2.11.4 golangci-lint run -v
# Reachability-aware vulnerability scan of our own code and Go dependencies.
# Complements the grype image scan in CI: grype matches Go modules at module
# granularity from the binary's buildinfo and cannot tell which packages are
# actually linked, while govulncheck resolves down to called symbols.
govulncheck: kool run go:linux run golang.org/x/vuln/cmd/govulncheck@latest ./...
test: kool run test:path ./...
test:path: kool run go:linux test -race
test-coverage: kool run go:linux test -v -race -coverprofile=coverage.txt -covermode=atomic ./...
Expand Down
Loading