name: Grype Vulnerability Scan # Second dependency/container vulnerability scanner (complements Trivy). # Grype uses Anchore's curated vulnerability DB (NVD, GitHub Advisories, # Alpine SecDB, etc.) — a different data source from Trivy's own aggregation. # Running both is standard practice: they catch different CVEs. on: workflow_call: # Called by release-gate.yml workflow_dispatch: permissions: {} # Minimal top-level for OSSF Scorecard Token-Permissions jobs: grype-filesystem: name: Grype Filesystem Scan runs-on: ubuntu-latest timeout-minutes: 30 permissions: contents: read security-events: write actions: read steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 with: egress-policy: audit - name: Checkout code uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Run Grype filesystem scan uses: anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2 # v7.4.0 id: grype-fs with: path: '.' # DO NOT change to fail-build: true — findings are enforced via SARIF alerts # in the release gate (check-code-scanning-alerts job in release-gate.yml). # Failing here would break CI without adding security value. fail-build: false output-format: sarif severity-cutoff: medium # Fail loudly if Grype produced no SARIF — never fabricate an empty one. # An empty-results SARIF uploaded under the grype-filesystem category would # make GitHub mark every previously-open Grype alert as fixed, silently # clearing real findings. Mirror the Trivy job: error + exit 1, skip upload. - name: Ensure SARIF file exists id: check-fs-sarif if: always() run: | SARIF_FILE="${{ steps.grype-fs.outputs.sarif }}" if [ -f "$SARIF_FILE" ]; then echo "exists=true" >> "$GITHUB_OUTPUT" else echo "::error::Grype filesystem scan did not produce a SARIF file — scan needs to be rerun" exit 1 fi - name: Upload Grype filesystem SARIF to GitHub Security tab uses: github/codeql-action/upload-sarif@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3 if: always() && steps.check-fs-sarif.outputs.exists == 'true' with: sarif_file: ${{ steps.grype-fs.outputs.sarif }} category: grype-filesystem grype-container: name: Grype Container Scan runs-on: ubuntu-latest timeout-minutes: 30 permissions: contents: read security-events: write actions: read steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 with: egress-policy: audit - name: Free up disk space run: | sudo rm -rf /usr/share/dotnet sudo rm -rf /usr/local/lib/android sudo rm -rf /opt/ghc sudo rm -rf /opt/hostedtoolcache/CodeQL sudo docker image prune -af df -h - name: Checkout code uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Set up Docker Buildx uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 - name: Build Docker image for scanning uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 with: context: . push: false load: true tags: local-deep-research:grype-scan cache-from: type=gha,scope=grype-scan cache-to: type=gha,mode=max,scope=grype-scan - name: Run Grype container scan uses: anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2 # v7.4.0 id: grype-container with: image: 'local-deep-research:grype-scan' # DO NOT change to fail-build: true — findings are enforced via SARIF alerts # in the release gate (check-code-scanning-alerts job in release-gate.yml). # Failing here would break CI without adding security value. fail-build: false output-format: sarif severity-cutoff: medium # Fail loudly if Grype produced no SARIF — never fabricate an empty one. # An empty-results SARIF uploaded under the grype-container category would # make GitHub mark every previously-open Grype alert as fixed, silently # clearing real findings. Mirror the Trivy job: error + exit 1, skip upload. - name: Ensure SARIF file exists id: check-container-sarif if: always() run: | SARIF_FILE="${{ steps.grype-container.outputs.sarif }}" if [ -f "$SARIF_FILE" ]; then echo "exists=true" >> "$GITHUB_OUTPUT" else echo "::error::Grype container scan did not produce a SARIF file — scan needs to be rerun" exit 1 fi - name: Upload Grype container SARIF to GitHub Security tab uses: github/codeql-action/upload-sarif@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3 if: always() && steps.check-container-sarif.outputs.exists == 'true' with: sarif_file: ${{ steps.grype-container.outputs.sarif }} category: grype-container - name: Display Grype summary if: always() run: | { echo "## Grype Container Vulnerability Scan" echo "" if [ "${{ steps.check-container-sarif.outputs.exists }}" = "true" ]; then echo "✅ Grype scan completed - Results available in Security tab" else echo "❌ Grype scan failed - no SARIF produced (see error above); rerun required" fi } >> "$GITHUB_STEP_SUMMARY"