Add a fast App CI gate (app-ci.yml): npm ci / npm run lint / npm run build on Node 22, running on every PR and push to main, separate from the slower Docker build.
Add a Trivy container scan job (ci.yml, trivy-scan, runs after build) that scans the built image for CRITICAL/HIGH CVEs, uploads SARIF to GitHub code scanning (Security tab), and fails the job only on CRITICAL findings (HIGH is reported, not blocking) so a solo maintainer isn't stalled by noise.
Workflow hygiene: every workflow now has an explicit least-privilege top-level permissions: contents: read, with packages: write (GHCR push) and security-events: write (SARIF upload) elevated only on the jobs that need them. All third-party actions are pinned to version tags.
Dockerfile: strip npm/npx/yarn/corepack from the final runtime stage. The container's entrypoint is node server.js only — none of that tooling is ever invoked at runtime — and it was the source of the only CRITICAL (CVE-2026-59873, tar) and all 5 HIGH findings Trivy found in the current image. Removing it resolves those findings without touching the node:22-alpine base image tag or the app's runtime behavior.
dependabot.yml already covered both npm and github-actions ecosystems — no change needed there.
codeql.yml already has least-privilege permissions (actions: read, contents: read, security-events: write) — left as-is per the brief.
Verified locally before opening this PR
npm ci && npm run lint && npm run build — all pass (lint: 0 errors, 3 pre-existing warnings; build: succeeds).
docker build — image builds and the container serves GET / with 200 OK.
Built the image and ran aquasecurity/trivy against it directly: before the Dockerfile change — 1 CRITICAL + 5 HIGH (all in npm's bundled tar/brace-expansion/picomatch/sigstore, base-image tooling not used at runtime); after — 0 CRITICAL, 0 HIGH.
Existing GHCR build/push behavior is unchanged: push still gated to github.event_name != 'pull_request', same tags (latest, ${{ github.sha }}).
Test plan
build check passes (required by branch protection, unchanged behavior)
App CI / lint-and-build passes
Build and Push Nuncio Website Container / trivy-scan passes and shows 0 CRITICAL
SARIF results appear under the repo's Security > Code scanning tab after merge
## Summary
- Add a fast **App CI gate** (`app-ci.yml`): `npm ci` / `npm run lint` / `npm run build` on Node 22, running on every PR and push to `main`, separate from the slower Docker build.
- Add a **Trivy container scan** job (`ci.yml`, `trivy-scan`, runs after `build`) that scans the built image for CRITICAL/HIGH CVEs, uploads SARIF to GitHub code scanning (Security tab), and fails the job only on CRITICAL findings (HIGH is reported, not blocking) so a solo maintainer isn't stalled by noise.
- **Workflow hygiene**: every workflow now has an explicit least-privilege top-level `permissions: contents: read`, with `packages: write` (GHCR push) and `security-events: write` (SARIF upload) elevated only on the jobs that need them. All third-party actions are pinned to version tags.
- **Dockerfile**: strip `npm`/`npx`/`yarn`/`corepack` from the final runtime stage. The container's entrypoint is `node server.js` only — none of that tooling is ever invoked at runtime — and it was the source of the only CRITICAL (`CVE-2026-59873`, `tar`) and all 5 HIGH findings Trivy found in the current image. Removing it resolves those findings without touching the `node:22-alpine` base image tag or the app's runtime behavior.
- `dependabot.yml` already covered both `npm` and `github-actions` ecosystems — no change needed there.
- `codeql.yml` already has least-privilege permissions (`actions: read`, `contents: read`, `security-events: write`) — left as-is per the brief.
## Verified locally before opening this PR
- `npm ci && npm run lint && npm run build` — all pass (lint: 0 errors, 3 pre-existing warnings; build: succeeds).
- `docker build` — image builds and the container serves `GET /` with `200 OK`.
- Built the image and ran `aquasecurity/trivy` against it directly: **before** the Dockerfile change — 1 CRITICAL + 5 HIGH (all in npm's bundled `tar`/`brace-expansion`/`picomatch`/`sigstore`, base-image tooling not used at runtime); **after** — 0 CRITICAL, 0 HIGH.
- Existing GHCR build/push behavior is unchanged: push still gated to `github.event_name != 'pull_request'`, same tags (`latest`, `${{ github.sha }}`).
## Test plan
- [ ] `build` check passes (required by branch protection, unchanged behavior)
- [ ] `App CI / lint-and-build` passes
- [ ] `Build and Push Nuncio Website Container / trivy-scan` passes and shows 0 CRITICAL
- [ ] SARIF results appear under the repo's Security > Code scanning tab after merge
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.
What Enabling Code Scanning Means:
The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.
For more information about GitHub Code Scanning, check out the documentation.
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.
### What Enabling Code Scanning Means:
- The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
- Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
- You will be able to see the analysis results for the pull request's branch on this [overview](/KofTwentyTwo/nuncio-website/security/code-scanning?query=pr%3A31+is%3Aopen) once the scans have completed and the checks have passed.
For more information about GitHub Code Scanning, check out [the documentation](https://docs.github.com/code-security/code-scanning/introduction-to-code-scanning/about-code-scanning).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
app-ci.yml):npm ci/npm run lint/npm run buildon Node 22, running on every PR and push tomain, separate from the slower Docker build.ci.yml,trivy-scan, runs afterbuild) that scans the built image for CRITICAL/HIGH CVEs, uploads SARIF to GitHub code scanning (Security tab), and fails the job only on CRITICAL findings (HIGH is reported, not blocking) so a solo maintainer isn't stalled by noise.permissions: contents: read, withpackages: write(GHCR push) andsecurity-events: write(SARIF upload) elevated only on the jobs that need them. All third-party actions are pinned to version tags.npm/npx/yarn/corepackfrom the final runtime stage. The container's entrypoint isnode server.jsonly — none of that tooling is ever invoked at runtime — and it was the source of the only CRITICAL (CVE-2026-59873,tar) and all 5 HIGH findings Trivy found in the current image. Removing it resolves those findings without touching thenode:22-alpinebase image tag or the app's runtime behavior.dependabot.ymlalready covered bothnpmandgithub-actionsecosystems — no change needed there.codeql.ymlalready has least-privilege permissions (actions: read,contents: read,security-events: write) — left as-is per the brief.Verified locally before opening this PR
npm ci && npm run lint && npm run build— all pass (lint: 0 errors, 3 pre-existing warnings; build: succeeds).docker build— image builds and the container servesGET /with200 OK.aquasecurity/trivyagainst it directly: before the Dockerfile change — 1 CRITICAL + 5 HIGH (all in npm's bundledtar/brace-expansion/picomatch/sigstore, base-image tooling not used at runtime); after — 0 CRITICAL, 0 HIGH.github.event_name != 'pull_request', same tags (latest,${{ github.sha }}).Test plan
buildcheck passes (required by branch protection, unchanged behavior)App CI / lint-and-buildpassesBuild and Push Nuncio Website Container / trivy-scanpasses and shows 0 CRITICALYou are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.
What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation.