Add Trivy container vulnerability scanning #6
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and Push Docker Image | |
| on: | |
| push: | |
| branches: | |
| - '**' | |
| tags: | |
| - '**' | |
| pull_request: | |
| branches: | |
| - main | |
| schedule: | |
| - cron: '0 6 * * 1' # Weekly Monday scan for new CVEs | |
| workflow_dispatch: | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: ${{ github.repository }} | |
| jobs: | |
| build: | |
| # Skip build on schedule and manual dispatch — only scan existing images | |
| if: github.event_name != 'schedule' && github.event_name != 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| outputs: | |
| image-tag: ${{ steps.first-tag.outputs.tag }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to Container Registry | |
| if: github.event_name != 'pull_request' | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata (tags, labels) | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | |
| tags: | | |
| # For branches: use branch name | |
| type=ref,event=branch | |
| # For tags: use tag name | |
| type=ref,event=tag | |
| # For PRs: use pr-<number> | |
| type=ref,event=pr | |
| # Add 'latest' tag for default branch | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| - name: Extract first tag for scan job | |
| id: first-tag | |
| run: | | |
| # Get the first tag from metadata output (most specific tag) | |
| FIRST_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) | |
| echo "tag=$FIRST_TAG" >> "$GITHUB_OUTPUT" | |
| - name: Build and push Docker image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| platforms: linux/amd64,linux/arm64 | |
| push: ${{ github.event_name != 'pull_request' }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # Scan the pushed image for vulnerabilities | |
| scan: | |
| needs: build | |
| if: always() && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: read | |
| security-events: write | |
| steps: | |
| - name: Login to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Determine image ref | |
| id: image-ref | |
| run: | | |
| if [ "${{ github.event_name }}" = "push" ]; then | |
| # Use the tag from the build job | |
| echo "ref=${{ needs.build.outputs.image-tag }}" >> "$GITHUB_OUTPUT" | |
| else | |
| # Schedule or manual dispatch — scan the latest (main) image | |
| echo "ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Pull image | |
| run: docker pull ${{ steps.image-ref.outputs.ref }} | |
| - name: Checkout (for ignore policy) | |
| uses: actions/checkout@v4 | |
| - name: Run Trivy vulnerability scanner (table) | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: ${{ steps.image-ref.outputs.ref }} | |
| format: table | |
| severity: CRITICAL,HIGH | |
| exit-code: '1' | |
| ignore-unfixed: true | |
| ignore-policy: .trivy-ignore-policy.rego | |
| trivyignores: .trivyignore | |
| - name: Run Trivy vulnerability scanner (SARIF) | |
| uses: aquasecurity/trivy-action@master | |
| if: always() | |
| with: | |
| image-ref: ${{ steps.image-ref.outputs.ref }} | |
| format: sarif | |
| output: trivy-results.sarif | |
| severity: CRITICAL,HIGH | |
| ignore-unfixed: true | |
| ignore-policy: .trivy-ignore-policy.rego | |
| trivyignores: .trivyignore | |
| - name: Log scan result count | |
| if: always() | |
| run: | | |
| if [ -f trivy-results.sarif ]; then | |
| COUNT=$(jq '[.runs[].results[]] | length' trivy-results.sarif) | |
| echo "::notice::Trivy found $COUNT findings (after policy filtering)" | |
| fi | |
| - name: Upload results to GitHub Security tab | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| with: | |
| sarif_file: trivy-results.sarif |