From b5e56af66d9296fc261357576c56efcb60ced46b Mon Sep 17 00:00:00 2001 From: kt Date: Tue, 22 Sep 2026 12:58:47 -0700 Subject: [PATCH 1/2] ci: scan the release and main binaries with trivy Two jobs on push to main, weekly, and after a release publishes, so the red X says what to do: the release job scans the last published binary (red means cut a release) and the main job scans a fresh build of the commit (red means bump the dependency). rootfs mode because fs mode does not read go binaries; unfixed findings are skipped and anything triaged out goes in .github/trivyignore. Same workflow as katbyte/tctest#146 and #147. --- .github/trivyignore | 3 + .github/workflows/main-binary-scan.yaml | 75 +++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 .github/trivyignore create mode 100644 .github/workflows/main-binary-scan.yaml diff --git a/.github/trivyignore b/.github/trivyignore new file mode 100644 index 00000000..209e3327 --- /dev/null +++ b/.github/trivyignore @@ -0,0 +1,3 @@ +# Advisories triaged out of main-binary-scan.yaml, one ID per line with the reason above it. Findings with +# no fixed version are already skipped by the workflow, so this is only for fixable findings in code the +# binary never calls. Remove an entry once the dependency moves past it. diff --git a/.github/workflows/main-binary-scan.yaml b/.github/workflows/main-binary-scan.yaml new file mode 100644 index 00000000..80102b0d --- /dev/null +++ b/.github/workflows/main-binary-scan.yaml @@ -0,0 +1,75 @@ +--- +# Scans the binaries for known vulnerabilities via the Go module and stdlib versions they embed. Two jobs so +# the red X on main says what to do: `release` scans the last published release (what users are running, so +# red means cut a release) and `main` scans a fresh build of this commit (what the next release would ship, +# so red means bump the dependency). govulncheck (pr-govulncheck.yaml) is the reachability check on PRs; +# this is the version-based artifact check, the same shape as hashicorp's release security scan. +name: binary vulnerability scan +on: + push: + branches: [ 'main' ] + schedule: + - cron: '23 5 * * 1' # weekly, so new advisories against unchanged binaries still turn main red + # rescan once a release has published, so shipping the fix clears the red rather than the next commit. + # workflow_run is flagged as dangerous because it can hand secrets to a run triggered by untrusted code; + # here it only reacts to the tag-triggered release workflow, checks out main and has read-only permissions + workflow_run: # zizmor: ignore[dangerous-triggers] + workflows: ['release'] + types: ['completed'] + workflow_dispatch: + +permissions: + contents: read + +jobs: + release: + name: release binary + runs-on: ubuntu-latest + # after a release, only scan if it actually published + if: github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Download the latest release + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + gh release download --pattern '*linux_amd64.tar.gz' --dir release + tar -xzf release/*.tar.gz -C release + rm release/*.tar.gz + # rootfs, not fs: fs mode does not read go binaries. unfixed findings are skipped because nothing can + # clear them; anything else triaged out goes in .github/trivyignore with a reason + - uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 + with: + scan-type: rootfs + scan-ref: release + scanners: vuln + severity: HIGH,CRITICAL + ignore-unfixed: true + trivyignores: .github/trivyignore + exit-code: 1 + + main: + name: build of main + runs-on: ubuntu-latest + # after a release, only scan if it actually published + if: github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version-file: ./.go-version + - run: go build -o out/terrafmt . + - uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 + with: + scan-type: rootfs + scan-ref: out + scanners: vuln + severity: HIGH,CRITICAL + ignore-unfixed: true + trivyignores: .github/trivyignore + exit-code: 1 From af178f1f72191ebc47f41136de40df2290ea8ef8 Mon Sep 17 00:00:00 2001 From: kt Date: Tue, 22 Sep 2026 13:45:55 -0700 Subject: [PATCH 2/2] ci: after a release, rerun the failed binary scan instead of starting a new one A workflow_run rescan attaches as new check runs beside the original push failure, so the commit stays red (seen on katbyte/tctest after v1.3.4). Only a rerun replaces a check run in place. The release workflow now reruns the failed jobs of any binary scan on main's head; that one job gets actions: write. --- .github/workflows/main-binary-scan.yaml | 12 +----------- .github/workflows/release.yaml | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/.github/workflows/main-binary-scan.yaml b/.github/workflows/main-binary-scan.yaml index 80102b0d..40f75324 100644 --- a/.github/workflows/main-binary-scan.yaml +++ b/.github/workflows/main-binary-scan.yaml @@ -10,13 +10,7 @@ on: branches: [ 'main' ] schedule: - cron: '23 5 * * 1' # weekly, so new advisories against unchanged binaries still turn main red - # rescan once a release has published, so shipping the fix clears the red rather than the next commit. - # workflow_run is flagged as dangerous because it can hand secrets to a run triggered by untrusted code; - # here it only reacts to the tag-triggered release workflow, checks out main and has read-only permissions - workflow_run: # zizmor: ignore[dangerous-triggers] - workflows: ['release'] - types: ['completed'] - workflow_dispatch: + workflow_dispatch: # release.yaml reruns the failed jobs of the latest run on main once a release publishes permissions: contents: read @@ -25,8 +19,6 @@ jobs: release: name: release binary runs-on: ubuntu-latest - # after a release, only scan if it actually published - if: github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -54,8 +46,6 @@ jobs: main: name: build of main runs-on: ubuntu-latest - # after a release, only scan if it actually published - if: github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f4826b14..fae542e0 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -63,3 +63,24 @@ jobs: TAG: ${{ github.ref_name }} HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} run: ./scripts/update-homebrew-formula.sh + + # the binary scan (main-binary-scan.yaml) goes red on main when the published release has a known + # vulnerability, and only a release can clear it. rerun the failed jobs of the scan runs on main's head so + # the red check runs are replaced in place; a fresh run from a new event would sit beside them and the + # commit would stay red + rescan: + needs: goreleaser + runs-on: ubuntu-latest + permissions: + actions: write # rerun workflow jobs + steps: + - name: rerun the failed binary scan jobs on main + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + head=$(gh api "repos/$GH_REPO/commits/main" --jq .sha) + runs=$(gh run list -w main-binary-scan.yaml -c "$head" --json databaseId,conclusion \ + --jq '.[] | select(.conclusion == "failure") | .databaseId') + if [ -z "$runs" ]; then echo "no failed binary scan on main ($head), nothing to rerun"; exit 0; fi + for run in $runs; do echo "rerunning failed jobs of $run"; gh run rerun "$run" --failed; done