Skip to content

ci: scan the release binary, docker image and main build with trivy - #26

Merged
katbyte merged 1 commit into
mainfrom
kt/binary-sec-checks
Sep 23, 2026
Merged

katbyte merged 1 commit into
mainfrom
kt/binary-sec-checks

Conversation

@katbyte

@katbyte katbyte commented Sep 23, 2026

Copy link
Copy Markdown
Owner

Scan the last released binary, the published docker image and a fresh build of main with trivy, on every push to main and weekly. Dependabot moves to monthly grouped PRs. Same setup as katbyte/tctest#146, #149 and katbyte/terrafmt#129, plus an image job since this repo ships one.

Why: govulncheck only reports advisories whose code is actually reachable, so it is green while the shipped v0.3.2 binary and image carry a High x/text advisory that version-based scanners flag. Nothing today turns main red for "what users are running has a known vulnerability".

Three jobs, so the red X says what to do. Each is its own check on the main commit.

  • release binary downloads the latest release's linux_amd64 tarball and scans it. Red means cut a release.
  • release image scans ghcr.io/katbyte/ghp-sync:latest, which covers the go binary plus the alpine packages and the gh binary around it. Red means cut a release to rebuild on current alpine.
  • build of main builds the commit and scans that. Red means bump a dependency.

Release red with main green means the fix is merged and a tag is all that is left.

      - uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
        with:
          scan-type: image
          image-ref: ghcr.io/${{ github.repository }}:latest
          scanners: vuln
          severity: HIGH,CRITICAL
          ignore-unfixed: true
          trivyignores: .github/trivyignore
          exit-code: 1

A release reruns the failed scan in place. A last job in the release workflow finds every binary scan run on main's head that failed and reruns its failed jobs, so the red check runs are replaced rather than joined by a new green one. It waits for both the tarball and the image to publish.

+  rescan:
+    needs: [goreleaser, docker] # the rerun scans both the release tarball and the :latest image
+    permissions:
+      actions: write # rerun workflow jobs

The image upgrades alpine packages when built. Without this, a release rebuild keeps whatever openssl the base image tag was last built with, and the image job could stay red with no way to clear it. Checked locally: on alpine:3.24 the upgrade moves libcrypto3 from 3.5.7-r0, which trivy flags, to 3.5.8-r0.

 FROM alpine:3.24
-RUN apk add --no-cache bash ca-certificates dcron github-cli tzdata
+RUN apk upgrade --no-cache && apk add --no-cache bash ca-certificates dcron github-cli tzdata

x/text is bumped so main starts green. The v0.3.2 binary and image both flag CVE-2026-56852 (x/text 0.28.0, fixed in 0.39.0). ghp-sync never calls the affected code, but the binary scans clean at 0.42.0. Verified locally with the same trivy flags: the v0.3.2 tarball reports the finding, a build of this branch reports none.

Dependabot runs monthly with one PR per ecosystem. The codeql-only group is subsumed by the catch-all, which still keeps init and analyze on the same version.

     schedule:
-      interval: "weekly"
+      interval: "monthly"
     groups:
-      codeql-action:
+      github-actions:
         patterns:
-          - "github/codeql-action*"
+          - "*"

Things that are fine as they are

  • Expect the release binary and release image jobs to be red until the next release. That is the workflow working. The image job will also flag openssl in the current image until a rebuild.
  • Unfixed findings are skipped and anything triaged out goes in .github/trivyignore with a reason. It is empty.
  • No PR trigger. This gates what ships; govulncheck stays as the reachability check on PRs.
  • The workflow could not be run before merge, since GitHub only registers it for dispatch once it is on the default branch. actionlint, yamllint, typos and zizmor pass on the new file. The image could not be built locally (no buildx here), so the alpine upgrade was checked in a plain alpine:3.24 container instead.

@katbyte
katbyte force-pushed the kt/binary-sec-checks branch from f3ad986 to 9a004ea Compare September 23, 2026 17:06
@katbyte
katbyte merged commit d3e68c1 into main Sep 23, 2026
12 checks passed
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