feat: add Trivy security scanning and SBOM generation #16
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 Publish Containers | |
| on: | |
| push: | |
| branches: | |
| - main | |
| pull_request: | |
| branches: | |
| - main | |
| schedule: | |
| # Run weekly on Sundays at 00:00 UTC to catch new vulnerabilities | |
| - cron: "0 0 * * 0" | |
| workflow_dispatch: | |
| # Allow manual triggering | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: ${{ github.repository }} | |
| jobs: | |
| build-and-publish: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| attestations: write | |
| id-token: write | |
| 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: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | |
| tags: | | |
| type=raw,value=latest | |
| labels: | | |
| org.opencontainers.image.authors=${{ github.actor }} | |
| - name: Process metadata | |
| run: | | |
| # Convert newline-separated labels to multiple --label arguments | |
| LABEL_ARGS=$(echo "${{ steps.meta.outputs.labels }}" | while IFS= read -r line; do | |
| [ -n "$line" ] && echo -n "--label \"$line\" " | |
| done) | |
| echo "LABEL_ARGS=$LABEL_ARGS" >> $GITHUB_ENV | |
| - name: Install Devcontainer CLI | |
| run: npm install -g @devcontainers/cli | |
| - name: Build and publish base-ubuntu container | |
| run: | | |
| cd src/base-ubuntu | |
| devcontainer build --workspace-folder . \ | |
| --image-name ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:base-ubuntu \ | |
| --platform linux/arm64,linux/amd64 \ | |
| --output type=registry \ | |
| ${{ env.LABEL_ARGS }} | |
| - name: Build and publish node container | |
| run: | | |
| cd src/node | |
| devcontainer build --workspace-folder . \ | |
| --image-name ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:node \ | |
| --platform linux/arm64,linux/amd64 \ | |
| --output type=registry \ | |
| ${{ env.LABEL_ARGS }} | |
| - name: Build and publish python container | |
| run: | | |
| cd src/python | |
| devcontainer build --workspace-folder . \ | |
| --image-name ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:python \ | |
| --platform linux/arm64,linux/amd64 \ | |
| --output type=registry \ | |
| ${{ env.LABEL_ARGS }} | |
| # Security scanning job - runs after successful build | |
| security-scan: | |
| needs: build-and-publish | |
| runs-on: ubuntu-latest | |
| # Only run on push to main or scheduled runs (not on PRs to avoid duplicate scans) | |
| if: github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' | |
| permissions: | |
| contents: read | |
| packages: read | |
| security-events: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| image: [base-ubuntu, node, python] | |
| steps: | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Pull container image | |
| run: docker pull ghcr.io/${{ github.repository }}:${{ matrix.image }} | |
| - name: Generate SBOM with Trivy | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: "ghcr.io/${{ github.repository }}:${{ matrix.image }}" | |
| format: "cyclonedx" | |
| output: "sbom-${{ matrix.image }}.json" | |
| - name: Upload SBOM as artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sbom-${{ matrix.image }} | |
| path: sbom-${{ matrix.image }}.json | |
| retention-days: 90 | |
| - name: Scan for vulnerabilities with Trivy | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: "ghcr.io/${{ github.repository }}:${{ matrix.image }}" | |
| format: "sarif" | |
| output: "trivy-${{ matrix.image }}.sarif" | |
| severity: "CRITICAL,HIGH,MEDIUM" | |
| - name: Upload Trivy scan results to GitHub Security tab | |
| uses: github/codeql-action/upload-sarif@v3 | |
| with: | |
| sarif_file: "trivy-${{ matrix.image }}.sarif" | |
| category: "container-${{ matrix.image }}" |