Warn on inherited npm advisories when the lockfile is unchanged

GHSA-vfj7-8cjw-p6xm (braces <= 3.0.3, no patched release) reached the
frontend graph only through dev-only tailwindcss 3 and jscpd 4, and from
3 October it failed the required Frontend check on every open pull
request, Go-only ones included. The production audit is clean, and a
change that leaves package.json and package-lock.json untouched has the
base commit's graph, so it can neither add a finding nor remove one.
Failing it blocked every fix and made nothing safer.

The runner now applies the split it already used for an unreachable
endpoint to findings too. A change that moves the graph, or edits the
runner, still fails on any finding at any severity. A change whose graph
is the base commit's warns, naming every finding. Only an explicit
NPM_AUDIT_REQUIRE_RESULT=false selects that mode; an empty or
unrecognised value, as a missing workflow output would give, stays
strict, which also tightens the outage path.

The scheduled npm-audit scan and the daily dependency advisory watch
keep the strict default, so an inherited advisory still fails somewhere
every day until the dependency graph is fixed. No severity threshold,
allowlist or suppression is added.
This commit is contained in:
rcourtman
2026-10-03 12:08:37 +01:00
parent 74fd189fd0
commit 237130a2cb
9 changed files with 215 additions and 82 deletions
+8 -6
View File
@@ -120,10 +120,12 @@ exposes independent marker damage in the same evidence packet; it never admits
the release or enables later delivery checks unless both trust checks pass.
Scheduled and push-time npm audits classify JSON results, use one-minute
registry attempts, and retry an unavailable audit endpoint. Advisory findings
still fail immediately, even if the same response contains a transport error. The
scheduled security scan and dependency-changing builds fail if three attempts
produce no result; a build with an unchanged dependency graph warns and uses
the base commit's passing answer. Audit steps defer their aggregate verdict so
are conclusive immediately, even if the same response contains a transport error. The
scheduled security scan, the dependency advisory watch and dependency-changing
builds fail on any finding and when three attempts produce no result. A build
with an unchanged dependency graph warns on both instead and names every
finding, because its graph is the base commit's, so any advisory it sees is one
the base already has and that only a dependency change can remove. Audit steps defer their aggregate verdict so
an unavailable endpoint cannot suppress independent frontend checks or the
production bundle build; the preceding clean install disables npm's duplicate
best-effort audit request.
@@ -170,8 +172,8 @@ one that passed. It uploads nothing and gates nothing.
`dependency-advisory-watch.yml` runs the required build-and-test frontend audit
daily against `main` and every active `release/v<major>.<minor>` line (the
latest stable's line and newer), because a new npm advisory against an
unchanged lockfile otherwise surfaces only when it fails every pull request on
that branch. Each line is a separate matrix job that reads only that line's
unchanged lockfile otherwise surfaces only as a warning on that branch's pull
requests; this watch is where it fails. Each line is a separate matrix job that reads only that line's
`frontend-modern/package.json` and `package-lock.json` (it never checks out,
installs or runs another branch's code in the default branch's scope) and audits
them with the same Node.js pin and `scripts/npm-audit-retry.sh all`. A failing job names
+13 -10
View File
@@ -105,10 +105,12 @@ jobs:
fi
echo "code=$code" >> "$GITHUB_OUTPUT"
# Whether this change moves the frontend dependency graph. It decides
# what an unreachable npm advisory endpoint means: unknown answer
# (must block) versus the base commit's already-passing answer. A
# change to the audit runner itself also demands a real result, so it
# can never be relaxed under cover of its own tolerant mode.
# whether the audit blocks it: a change that moves the graph answers
# for any finding and for an unreachable endpoint, while one that
# leaves the graph alone only warns, because its findings are the
# base commit's. A change to the audit runner itself also demands a
# clean result, so it can never be relaxed under cover of its own
# tolerant mode.
if printf '%s\n' "$changed" | grep -qE '^frontend-modern/package(-lock)?\.json$|^scripts/npm-audit-retry\.sh$'; then
frontend_deps=true
else
@@ -193,14 +195,15 @@ jobs:
run: npm ci --no-audit
# npm audit exits 1 both for a real advisory and for an unreachable
# advisory endpoint. The runner keeps the advisory verdict exactly as
# strict and only retries the endpoint being down; see the script header.
# advisory endpoint. The runner retries the endpoint being down, fails a
# change that moves the dependency graph on any finding, and only warns,
# naming every finding, for a change whose graph is the base commit's;
# see the script header.
#
# Only the complete graph is audited here. `--omit=dev` audits a subset
# of these packages, so it can only report a subset of these advisories;
# and because the complete audit fails the job on any finding, the
# production step could only ever run in the cases where it was already
# guaranteed clean. The dev-versus-production split is still reported
# of these packages, so it can only report a subset of these advisories,
# under the same blocking rule; a separate production step could never
# block a change the complete audit lets through. The dev-versus-production split is still reported
# for every workspace by the scheduled npm-audit job in
# security-scan.yml, where it informs rather than blocks delivery.
- name: Audit complete frontend dependency graph
@@ -1,12 +1,14 @@
name: Dependency Advisory Watch
# A new npm advisory against an unchanged lockfile fails the required
# A new npm advisory against an unchanged lockfile used to fail the required
# "Audit complete frontend dependency graph" step in build-and-test.yml on
# every pull request to that branch. On 30 Sep-1 Oct 2026 two advisories did
# exactly that to main, release/v6.4 and release/v6.5 at once, and nothing
# owned the fix. This watch runs the same audit daily against main and every
# active release line, fails when any of them would block, and names the
# branch, the advisories and the fix in the step summary. Scheduled workflows
# owned the fix. Since 3 Oct 2026 that step only warns for a pull request that
# leaves the lockfile alone, so this watch is where such an advisory fails: it
# runs the same audit daily against main and every active release line with
# NPM_AUDIT_REQUIRE_RESULT=true, fails on any finding, and names the branch,
# the advisories and the fix in the step summary. Scheduled workflows
# only run on the default branch. The audit needs only each line's
# frontend-modern/package.json and package-lock.json, so this job reads those
# two files with git and never checks out, installs or executes another