From 78eb898a50a094442339924b53ccf2dd09b3ff92 Mon Sep 17 00:00:00 2001 From: Matthew Mattox Date: Thu, 9 Jul 2026 20:49:23 -0500 Subject: [PATCH 1/6] chore(ci): migrate Trivy image scan to org-wide reusable Grype workflow Trivy's image scan in the Docker build/push job was informational only (SARIF upload, no build gate) and is replaced by the org-standard SupportTools/ci-runners grype-scan reusable workflow, wired the same way: blocking: false, fail-on: high. Adds .grype.yaml (empty ignore list) for the workflow's optional suppressions file. Note: node-doctor publishes images to Docker Hub (docker.io/supporttools), not Harbor, so the image ref passed to grype-scan is a Docker Hub ref rather than the harbor.support.tools ref used by other repos on this workflow. --- .github/workflows/README.md | 9 ++++----- .github/workflows/ci.yml | 32 +++++++++++++++++++++----------- .grype.yaml | 15 +++++++++++++++ 3 files changed, 40 insertions(+), 16 deletions(-) create mode 100644 .grype.yaml 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..c2b56d6 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,25 @@ 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 - with: - image-ref: ${{ env.REGISTRY }}/node-doctor:${{ steps.tag.outputs.TAG }} - format: 'sarif' - output: 'trivy-results.sarif' - - - name: Upload Trivy results to GitHub Security - uses: github/codeql-action/upload-sarif@v4 - with: - sarif_file: trivy-results.sarif + # Grype vulnerability scan - INFORMATIONAL (SARIF/report only, no build gate). + # Replaces the former Trivy scan. NOTE: node-doctor publishes images to Docker Hub + # (docker.io/supporttools), not Harbor, so unlike other repos wired to this reusable + # workflow the `image` input below is a Docker Hub ref rather than a + # harbor.support.tools ref. The reusable workflow's Harbor login step only affects + # pulls from harbor.support.tools and does not block grype from pulling this + # public Docker Hub image directly. + grype-scan: + name: Grype Scan (informational) + needs: [docker] + uses: SupportTools/ci-runners/.github/workflows/grype-scan.yml@main + with: + image: docker.io/supporttools/node-doctor:${{ needs.docker.outputs.tag }} + fail-on: high + blocking: false + permissions: + contents: read + id-token: write + security-events: write # 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: [] From 7396e255a9832def20a10f3e0c95e8340fdff68c Mon Sep 17 00:00:00 2001 From: Matthew Mattox Date: Thu, 9 Jul 2026 20:53:58 -0500 Subject: [PATCH 2/6] chore: retrigger CI From 76bcf5d72a8f42b231cd5fee10cde1022c1c2117 Mon Sep 17 00:00:00 2001 From: Matthew Mattox Date: Thu, 9 Jul 2026 20:55:45 -0500 Subject: [PATCH 3/6] diagnostic: temp workflow_dispatch trigger to test grype-scan resolution --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2b56d6..c28d192 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,7 @@ on: pull_request: branches: - main + workflow_dispatch: env: GO_VERSION: '1.25' From 71603eba64c80664638f20f828a2b671eacc7a19 Mon Sep 17 00:00:00 2001 From: Matthew Mattox Date: Thu, 9 Jul 2026 20:57:07 -0500 Subject: [PATCH 4/6] revert: drop temporary workflow_dispatch trigger used for diagnostics The grype-scan reusable-workflow reference currently fails to resolve for this repo regardless of trigger event (confirmed via both pull_request and a temporary workflow_dispatch test): error parsing called workflow ".github/workflows/ci.yml" -> "SupportTools/ci-runners/.github/workflows/grype-scan.yml@main": workflow was not found. SupportTools/ci-runners reports access_level=organization (should allow any SupportTools repo), and org-level Actions policy allows all actions and repositories, so this looks like an infra-side access gap for node-doctor specifically (working fine for memory-bank/SupportTools/cd.yaml). Needs ci-runners maintainer attention; see PR description. --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c28d192..c2b56d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,6 @@ on: pull_request: branches: - main - workflow_dispatch: env: GO_VERSION: '1.25' From 78bdba82798af8acceb3dadf78c2b7f316bf0331 Mon Sep 17 00:00:00 2001 From: Matthew Mattox Date: Thu, 9 Jul 2026 21:05:39 -0500 Subject: [PATCH 5/6] ci: retrigger grype-scan reusable workflow resolution From abba31a1fbebbde58c909d5242ead4f8b477c76d Mon Sep 17 00:00:00 2001 From: Matthew Mattox Date: Thu, 9 Jul 2026 21:18:22 -0500 Subject: [PATCH 6/6] ci(grype): use anchore/scan-action in-line instead of reusable workflow The private-repo reusable grype-scan.yml fails to resolve at workflow-load time on pull_request events, failing the entire ci.yml run at startup (0 jobs) even with a job-level if:. node-doctor also publishes to Docker Hub, not Harbor, so the reusable workflow's keyless Harbor pull was the wrong mechanism anyway. Swap to anchore/scan-action@v6 (grype engine, informational, fail-build: false), scanning the public docker.io image on tag builds. --- .github/workflows/ci.yml | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2b56d6..82189c9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -268,24 +268,40 @@ jobs: cache-to: type=gha,mode=max # Grype vulnerability scan - INFORMATIONAL (SARIF/report only, no build gate). - # Replaces the former Trivy scan. NOTE: node-doctor publishes images to Docker Hub - # (docker.io/supporttools), not Harbor, so unlike other repos wired to this reusable - # workflow the `image` input below is a Docker Hub ref rather than a - # harbor.support.tools ref. The reusable workflow's Harbor login step only affects - # pulls from harbor.support.tools and does not block grype from pulling this - # public Docker Hub image directly. + # 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] - uses: SupportTools/ci-runners/.github/workflows/grype-scan.yml@main - with: - image: docker.io/supporttools/node-doctor:${{ needs.docker.outputs.tag }} - fail-on: high - blocking: false + if: startsWith(github.ref, 'refs/tags/v') permissions: contents: read - id-token: write security-events: write + steps: + - name: Grype scan (anchore/scan-action) + id: grype + uses: anchore/scan-action@v6 + with: + 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: ${{ steps.grype.outputs.sarif }} + category: grype # Summary job - provides overall status ci-success: