From 658e406d1f74dcf8a9b391f80ae53e5994ebb0bd Mon Sep 17 00:00:00 2001 From: Christophe COLLET Date: Wed, 13 May 2026 10:32:04 +0200 Subject: [PATCH] ci(docker): gate image publish on quality and security checks Add test, security and CodeQL jobs that must pass before the Docker image is built and pushed. Quality covers gofmt, go vet, golangci-lint, race-enabled tests with coverage, and a build sanity check. Security runs govulncheck, gosec, gitleaks, Trivy filesystem scan and Hadolint on the Dockerfile, uploading SARIF to the GitHub Security tab. The publish job now builds a single-arch local image for a Trivy image scan that fails on CRITICAL/HIGH before doing the multi-arch push, which now also emits provenance and SBOM attestations. PRs trigger the checks without pushing. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/docker-publish.yml | 186 +++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 4d97958..6a0f573 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -5,16 +5,172 @@ on: push: branches: [master] tags: ['v*'] + pull_request: + branches: [master] workflow_dispatch: permissions: contents: read + security-events: write env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true' jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Download modules + run: go mod download + + - name: Verify go.mod tidy + run: | + go mod tidy + git diff --exit-code -- go.mod go.sum + + - name: gofmt + run: | + fmt_out=$(gofmt -l .) + if [ -n "$fmt_out" ]; then + echo "gofmt issues:" + echo "$fmt_out" + exit 1 + fi + + - name: go vet + run: go vet ./... + + - name: golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + version: latest + + - name: go test + run: go test -race -cover -coverprofile=coverage.out -v ./... + + - name: Upload coverage + uses: actions/upload-artifact@v4 + with: + name: coverage + path: coverage.out + if-no-files-found: ignore + + - name: go build + run: go build ./... + + security: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: govulncheck + uses: golang/govulncheck-action@v1 + with: + go-version-file: go.mod + + - name: gosec + uses: securego/gosec@master + with: + args: '-no-fail -fmt sarif -out gosec.sarif ./...' + + - name: Upload gosec SARIF + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: gosec.sarif + category: gosec + + - name: gitleaks + uses: gitleaks/gitleaks-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + config-path: '' + + - name: Trivy filesystem scan + uses: aquasecurity/trivy-action@0.24.0 + with: + scan-type: fs + scan-ref: . + format: sarif + output: trivy-fs.sarif + severity: CRITICAL,HIGH + exit-code: '1' + ignore-unfixed: true + + - name: Upload Trivy FS SARIF + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: trivy-fs.sarif + category: trivy-fs + + - name: Hadolint Dockerfile + uses: hadolint/hadolint-action@v3.1.0 + with: + dockerfile: Dockerfile + format: sarif + output-file: hadolint.sarif + no-fail: true + + - name: Upload Hadolint SARIF + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: hadolint.sarif + category: hadolint + + codeql: + runs-on: ubuntu-latest + permissions: + security-events: write + actions: read + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Init CodeQL + uses: github/codeql-action/init@v3 + with: + languages: go + + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + - name: CodeQL analyze + uses: github/codeql-action/analyze@v3 + with: + category: codeql-go + publish: + needs: [test, security, codeql] + if: github.event_name != 'pull_request' runs-on: ubuntu-latest steps: @@ -47,6 +203,34 @@ jobs: type=semver,pattern={{major}} type=raw,value=latest,enable={{is_default_branch}} + - name: Build local image for scan + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64 + load: true + tags: staticmap:scan + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Trivy image scan + uses: aquasecurity/trivy-action@0.24.0 + with: + image-ref: staticmap:scan + format: sarif + output: trivy-image.sarif + severity: CRITICAL,HIGH + exit-code: '1' + ignore-unfixed: true + vuln-type: 'os,library' + + - name: Upload Trivy image SARIF + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: trivy-image.sarif + category: trivy-image + - name: Build and push uses: docker/build-push-action@v6 with: @@ -57,5 +241,7 @@ jobs: labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max + provenance: true + sbom: true ...