diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 1ea2f0a..21eb6ac 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -38,8 +38,7 @@ Comprehensive CI pipeline that runs on: - Builds for `linux/amd64` and `linux/arm64` - Pushes to Harbor registry - Tags with version and `latest` - - Runs Trivy security scan on image - - Uploads Trivy results to GitHub Security + - Triggers the informational Grype vulnerability scan on the pushed image (SARIF-only, does not gate the pipeline) 6. **CI Success** - Overall status check - Aggregates results from all jobs @@ -49,7 +48,7 @@ Comprehensive CI pipeline that runs on: - **Dependency Caching**: Go modules and build cache are cached automatically - **Code Coverage**: Generated and uploaded to Codecov -- **Security Scanning**: gosec (code) and Trivy (container images) +- **Security Scanning**: gosec (code) and Grype (container images, informational) - **Multi-platform Builds**: Docker images for amd64 and arm64 - **Build Metadata**: Version, commit SHA, and build time injected into binaries @@ -102,8 +101,8 @@ git push origin v1.0.0 This triggers: - Full CI pipeline - Docker multi-platform build -- Push to Harbor registry -- Trivy security scan +- Push to Docker Hub registry +- Informational Grype security scan (non-blocking) ## Local Testing diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72cd277..82189c9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -220,6 +220,8 @@ jobs: permissions: security-events: write contents: read + outputs: + tag: ${{ steps.tag.outputs.TAG }} steps: - name: Checkout code uses: actions/checkout@v5 @@ -265,17 +267,41 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max - - name: Run Trivy security scan on image - uses: aquasecurity/trivy-action@v0.35.0 + # Grype vulnerability scan - INFORMATIONAL (SARIF/report only, no build gate). + # Replaces the former Trivy scan. Uses anchore/scan-action (grype engine) IN-LINE + # rather than the org reusable grype-scan.yml for two reasons: + # 1. ci.yml is pull_request-triggered, and a private-repo reusable `uses:` fails to + # resolve at workflow-LOAD time on pull_request events, which fails the ENTIRE run + # at startup (0 jobs). A job-level `if:` does not help (resolution precedes `if`). + # 2. node-doctor publishes images to Docker Hub (docker.io/supporttools), not Harbor, + # so the reusable workflow's keyless Harbor pull is the wrong mechanism anyway. + # anchore/scan-action uses the public grype vuln DB (not the org's self-hosted air-gapped + # mirror) — acceptable here since the scanned image is public on Docker Hub. + # Runs only on tag builds, matching the `docker` job that produces the image. + grype-scan: + name: Grype Scan (informational) + runs-on: ubuntu-latest + needs: [docker] + if: startsWith(github.ref, 'refs/tags/v') + permissions: + contents: read + security-events: write + steps: + - name: Grype scan (anchore/scan-action) + id: grype + uses: anchore/scan-action@v6 with: - image-ref: ${{ env.REGISTRY }}/node-doctor:${{ steps.tag.outputs.TAG }} - format: 'sarif' - output: 'trivy-results.sarif' - - - name: Upload Trivy results to GitHub Security + image: docker.io/supporttools/node-doctor:${{ needs.docker.outputs.tag }} + fail-build: false + severity-cutoff: high + output-format: sarif + - name: Upload SARIF to GitHub Security + if: always() + continue-on-error: true uses: github/codeql-action/upload-sarif@v4 with: - sarif_file: trivy-results.sarif + sarif_file: ${{ steps.grype.outputs.sarif }} + category: grype # Summary job - provides overall status ci-success: diff --git a/.grype.yaml b/.grype.yaml new file mode 100644 index 0000000..d339d51 --- /dev/null +++ b/.grype.yaml @@ -0,0 +1,15 @@ +# .grype.yaml — Grype config + vulnerability suppressions for node-doctor. +# +# Read by the org-wide reusable Grype scan workflow +# (SupportTools/ci-runners/.github/workflows/grype-scan.yml), wired here as an +# INFORMATIONAL scan (blocking: false) that replaces the former Trivy scan. +# +# FORMAT — each suppression MUST document why it is acceptable + an expected fix: +# ignore: +# - vulnerability: CVE-2024-99999 # false positive in musl; fixed alpine 3.21; resolve 2025-04-01 +# +# REVIEW POLICY: review monthly; suppressions are temporary; remove once the base +# image carries the upstream fix. Permanent suppressions need explicit approval. +# +# Currently no vulnerabilities are suppressed. +ignore: []