diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml new file mode 100644 index 00000000..06e58458 --- /dev/null +++ b/.github/workflows/trivy.yml @@ -0,0 +1,95 @@ +name: Trivy Security Scan + +on: + pull_request: + push: + branches: + - main + schedule: + # Weekly full scan, Mondays 06:00 UTC. + - cron: "0 6 * * 1" + workflow_dispatch: + +permissions: + contents: read + +# Cancel superseded PR runs; non-PR runs get a unique group so they are never +# interrupted (mirrors ci.yml). +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + +jobs: + # Blocking gate on PRs and pushes to main: fail only on fixable CRITICAL/HIGH + # vulnerabilities in Go dependencies, so a red check always has an actionable + # fix (bump the dependency). Vulns with no upstream fix don't wedge every PR. + scan-pr: + name: Dependency Scan + if: github.event_name == 'pull_request' || github.event_name == 'push' + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + security-events: write # upload SARIF to code scanning + steps: + - name: Checkout code + uses: actions/checkout@v7 + + # SARIF report for the Security tab covers all severities and never fails + # the job (the gate below owns pass/fail). trivy-action ignores severity + # filters when format is sarif, so this cannot double as the gate. + - name: Run Trivy (SARIF report) + uses: aquasecurity/trivy-action@v0.36.0 + with: + scan-type: fs + scanners: vuln + format: sarif + output: trivy-results.sarif + + # Fork-PR tokens have no security-events:write, so skip the upload there + # (the scan + gate still run). + - name: Upload SARIF to code scanning + if: always() && github.event.pull_request.head.repo.fork != true + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: trivy-results.sarif + + # The blocking gate: fail only on fixable CRITICAL/HIGH vulnerabilities. + - name: Run Trivy (fixable CRITICAL/HIGH gate) + uses: aquasecurity/trivy-action@v0.36.0 + with: + scan-type: fs + scanners: vuln + severity: CRITICAL,HIGH + ignore-unfixed: true + exit-code: "1" + format: table + + # Weekly full scan: every severity, including vulns with no fix yet. New CVEs + # land against unchanged code, so this catches what the PR gate never sees. + # Results go to the Security tab (code scanning) — watchers subscribed to + # security alerts get notified there, no Slack/webhook plumbing. + scan-weekly: + name: Full Scan + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + security-events: write # upload SARIF to code scanning + steps: + - name: Checkout code + uses: actions/checkout@v7 + + - name: Run Trivy (all severities) + uses: aquasecurity/trivy-action@v0.36.0 + with: + scan-type: fs + scanners: vuln + format: sarif + output: trivy-results.sarif + + - name: Upload SARIF to code scanning + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: trivy-results.sarif