Container Security Scan #200
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
| # Copyright AGNTCY Contributors (https://github.com/agntcy) | |
| # SPDX-License-Identifier: Apache-2.0 | |
| name: Container Security Scan | |
| on: | |
| # Nightly scan on main branch only; manual dispatch allowed. | |
| schedule: | |
| - cron: "0 3 * * *" # Daily at 03:00 UTC (runs on default branch context: main) | |
| workflow_dispatch: | |
| inputs: | |
| image-tag: | |
| required: false | |
| type: string | |
| description: "Override tag for repo images (latest for latest release version)" | |
| permissions: | |
| contents: read | |
| security-events: write # for uploading SARIF | |
| actions: read | |
| issues: write # create issues for critical CVEs | |
| jobs: | |
| resolve-latest-tag: | |
| if: ${{ github.event.inputs.image-tag == 'latest' }} | |
| name: Resolve latest release version tag | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.resolve.outputs.version }} | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| steps: | |
| - name: Resolve image tag | |
| id: resolve | |
| run: | | |
| set -euo pipefail | |
| if [ -n "${{ inputs.image-tag }}" ]; then | |
| echo "version=${{ inputs.image-tag }}" >> $GITHUB_OUTPUT | |
| else | |
| OWNER=${GITHUB_REPOSITORY%%/*} | |
| REPO_NAME=${GITHUB_REPOSITORY#*/} | |
| TAG_VERSION=$(gh api graphql \ | |
| -f query=' | |
| query($owner: String!, $repo: String!, $tagCount: Int!) { | |
| repository(owner: $owner, name: $repo) { | |
| refs(refPrefix: "refs/tags/", first: $tagCount, orderBy: {field: TAG_COMMIT_DATE, direction: DESC}) { | |
| nodes { name } | |
| } | |
| } | |
| } | |
| ' \ | |
| -F owner=$OWNER -F repo=$REPO_NAME -F tagCount=100 \ | |
| --jq 'first(.data.repository.refs.nodes[] | select(.name | contains("/") | not).name)' | |
| ) | |
| echo "version=${TAG_VERSION}" >> $GITHUB_OUTPUT | |
| fi | |
| build-from-main: | |
| if: ${{ github.event.inputs.image-tag == 'latest' && github.event.inputs.image-tag == '' }} | |
| name: Build main branch | |
| runs-on: ubuntu-latest | |
| outputs: | |
| main_sha: ${{ steps.get-commit-sha.outputs.main_sha }} | |
| steps: | |
| - name: Get main branch SHA | |
| id: get-commit-sha | |
| run: | | |
| MAIN_SHA=gh api repos/${{ github.repository }}/git/refs/heads/${{ github.event.repository.default_branch }} | jq -r .object.sha | |
| echo "main_sha=${MAIN_SHA}" >> $GITHUB_OUTPUT | |
| - uses: ./.github/workflows/reusable-build.yaml | |
| with: | |
| image_repo: ghcr.io/agntcy | |
| image_tag: ${{ steps.get-commit-sha.outputs.main_sha }} | |
| image-list: | |
| name: Resolve image list | |
| runs-on: ubuntu-latest | |
| needs: [resolve-latest-tag, build-from-main] | |
| outputs: | |
| matrix: ${{ steps.matrix.outputs.matrix }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Taskfile | |
| shell: bash | |
| run: sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin | |
| - name: Get image list from task | |
| id: matrix | |
| env: | |
| IMAGE_TAG: ${{ needs.resolve-latest-tag.outputs.version || needs.build-from-main.outputs.main_sha || github.event.inputs.image-tag }} | |
| IMAGE_REPO: ghcr.io/${{ github.repository_owner }} | |
| run: | | |
| matrix=$(task --silent deps:vuln:images:list | jq -R -s -c 'split("\n") | map(select(length > 0)) | {image: .}') | |
| echo "matrix<<EOF" >> $GITHUB_OUTPUT | |
| echo "$matrix" >> $GITHUB_OUTPUT | |
| echo "EOF" >> $GITHUB_OUTPUT | |
| trivy-scan: | |
| name: Trivy Scan | |
| runs-on: ubuntu-latest | |
| needs: [image-list] | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJson(needs.image-list.outputs.matrix) }} | |
| steps: | |
| - name: Set image name | |
| id: image-name | |
| run: | | |
| # Extract image name from full reference (e.g., ghcr.io/owner/image:tag -> image) | |
| IMAGE_NAME=$(echo "${{ matrix.image }}" | sed -E 's|.*/||; s|[:@].*||') | |
| echo "name=$IMAGE_NAME" >> $GITHUB_OUTPUT | |
| - name: Scan image | |
| uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0 | |
| with: | |
| image-ref: ${{ matrix.image }} | |
| github-pat: ${{ secrets.GITHUB_TOKEN }} | |
| format: sarif | |
| output: trivy-${{ steps.image-name.outputs.name }}.sarif | |
| vuln-type: "os,library" | |
| severity: "CRITICAL,HIGH,MEDIUM" | |
| ignore-unfixed: true | |
| - name: Export image metadata | |
| run: echo "${{ matrix.image }}" > trivy-${{ steps.image-name.outputs.name }}.meta | |
| - name: Upload SARIF | |
| uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 | |
| with: | |
| sarif_file: trivy-${{ steps.image-name.outputs.name }}.sarif | |
| category: trivy-${{ steps.image-name.outputs.name }} | |
| - name: Upload report artifacts | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 | |
| with: | |
| name: trivy-report-${{ steps.image-name.outputs.name }} | |
| path: | | |
| trivy-${{ steps.image-name.outputs.name }}.sarif | |
| trivy-${{ steps.image-name.outputs.name }}.meta | |
| retention-days: 7 | |
| summarize: | |
| name: Summarize Results | |
| needs: [trivy-scan] | |
| runs-on: ubuntu-latest | |
| if: always() | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Download artifacts | |
| uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 | |
| with: | |
| path: trivy-artifacts | |
| - name: Generate summary | |
| run: | | |
| chmod +x .github/workflows/scripts/security/generate_trivy_summary.sh | |
| .github/workflows/scripts/security/generate_trivy_summary.sh | |
| - name: Fail if critical vulns found (optional gate) | |
| if: ${{ github.event_name != 'pull_request' }} | |
| run: | | |
| set -e | |
| found=$(grep -R "CRITICAL" -c trivy-artifacts || true) | |
| if [ "${found}" != "0" ]; then | |
| echo "Critical vulnerabilities detected. (Gate currently informational.)" >&2 | |
| fi | |
| - name: Create GitHub issues for critical CVEs | |
| if: ${{ github.event_name != 'pull_request' }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| run: | | |
| set -euo pipefail | |
| echo "Installing dependencies for issue creation script"; | |
| npm init -y >/dev/null 2>&1 || true | |
| npm install @octokit/rest@21 glob >/dev/null 2>&1 | |
| node .github/workflows/scripts/security/create_critical_cve_issues.js |