From cc1b5905bee8809a8c2e370effa920f66a427380 Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 29 Jul 2026 11:04:24 -0500 Subject: [PATCH] feat(ci): add container security scanning and harden workflow permissions Add a Trivy job to the Docker build workflow that scans the built image for CRITICAL/HIGH CVEs, uploads SARIF to GitHub code scanning, and fails only on CRITICAL findings so a solo maintainer isn't blocked by HIGH-only noise. Split top-level workflow permissions down to contents:read and elevate packages:write / security-events:write per job instead of at the workflow level. Strip npm/npx/yarn/corepack from the Docker runtime stage since they are never invoked at runtime (node server.js only) and were the source of the only CRITICAL/HIGH findings in the current image. --- .github/workflows/app-ci.yml | 32 ++++++++++++++++++++++++ .github/workflows/ci.yml | 48 +++++++++++++++++++++++++++++++++++- Dockerfile | 8 ++++++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/app-ci.yml diff --git a/.github/workflows/app-ci.yml b/.github/workflows/app-ci.yml new file mode 100644 index 0000000..6ecc418 --- /dev/null +++ b/.github/workflows/app-ci.yml @@ -0,0 +1,32 @@ +name: App CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: read + +jobs: + lint-and-build: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v7 + + - name: Set up Node.js + uses: actions/setup-node@v7 + with: + node-version: '22' + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Lint + run: npm run lint + + - name: Build + run: npm run build diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e680cd..201ce49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,11 +8,13 @@ on: permissions: contents: read - packages: write jobs: build: runs-on: ubuntu-latest + permissions: + contents: read + packages: write steps: - name: Checkout Code uses: actions/checkout@v7 @@ -38,3 +40,47 @@ jobs: ghcr.io/koftwentytwo/nuncio-website:${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max + + trivy-scan: + needs: build + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - name: Checkout Code + uses: actions/checkout@v7 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Build image for scanning + uses: docker/build-push-action@v7 + with: + context: . + push: false + load: true + tags: nuncio-website:scan + cache-from: type=gha + + - name: Run Trivy vulnerability scan (report-only, SARIF) + uses: aquasecurity/trivy-action@v0.36.0 + with: + image-ref: nuncio-website:scan + format: sarif + output: trivy-results.sarif + severity: CRITICAL,HIGH + exit-code: '0' + + - name: Upload Trivy results to GitHub code scanning + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: trivy-results.sarif + + - name: Run Trivy vulnerability scan (blocking on CRITICAL) + uses: aquasecurity/trivy-action@v0.36.0 + with: + image-ref: nuncio-website:scan + format: table + severity: CRITICAL + exit-code: '1' diff --git a/Dockerfile b/Dockerfile index 7d80e0c..955511e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,6 +29,14 @@ ENV HOSTNAME="0.0.0.0" RUN addgroup --system --gid 1001 nodejs && \ adduser --system --uid 1001 nextjs +# Strip build-time package-manager tooling inherited from the base image. +# The runtime entrypoint is `node server.js` only; npm/npx/yarn/corepack are +# never invoked here, and removing them drops unused CVE surface (e.g. the +# tar version bundled with npm) from the shipped image. +RUN rm -rf /usr/local/lib/node_modules/npm /usr/local/lib/node_modules/corepack \ + /usr/local/bin/npm /usr/local/bin/npx /usr/local/bin/corepack \ + /usr/local/bin/yarn /usr/local/bin/yarnpkg + COPY --from=builder /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static -- 2.54.0