Skip to content

fix(deps): clear the minor/patch CVE backlog across 7 manifests - #814

Merged
izzywdev merged 5 commits into
masterfrom
claude/deps-minors
Aug 27, 2026
Merged

fix(deps): clear the minor/patch CVE backlog across 7 manifests#814
izzywdev merged 5 commits into
masterfrom
claude/deps-minors

Conversation

@github-actions

@github-actions github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Clears the minor/patch-fixable half of the #747 CVE triage backlog. Seven manifests, no major-version bumps, no test or gate weakening.

Part of the sequencing in #747: axios (#754, merged) → tar (#813) → these minors → react-router v7 + uuid 9→11 (deferred, own PRs).

What changes

Direct bumps

Manifest Package From → To
backend/security undici ^7.28.0^7.29.0
fuzefront-website/backend morgan ^1.10.0^1.11.0
fuzefront-website/frontend react-router-dom ^6.28.0^6.30.6

Overrides added (resolved version in parens)

Manifest Override Resolves to
root ip-address >=10.2.2 10.5.0
fuzefront-website/backend lodash >=4.17.23 4.18.1
qs >=6.15.2 6.15.3
validator >=13.15.22 13.15.35
body-parser ">=1.20.6 <2" 1.20.6
sdk ws >=8.20.1 8.21.3
(transitive) socket.io-parser 4.2.7
services/billing-service body-parser ">=1.20.6 <2" / brace-expansion >=5.0.9 1.20.6 / 5.0.9
services/email-service same same
services/sms-service same same

Authoritative post-change finding counts come from gate-dependency-scan's SARIF artifact on this PR — I could not re-measure locally (Trivy's DB host is blocked from this environment), so I'm not quoting numbers I can't stand behind.

Four things worth a reviewer's attention

1. The body-parser upper bound is load-bearing, not stylistic.

The obvious override is "body-parser": ">=1.20.6". I wrote that first, and npm resolved 2.3.0 — under express@4, which requires the 1.x API. The bound is ">=1.20.6 <2" for exactly that reason. Anyone widening it later re-breaks express@4. Nothing in CI would have caught it.

2. The lockfiles were rebuilt per-package, after a first attempt that floated the whole tree.

My first pass used npm update --package-lock-only with no package arguments, which re-resolves everything. On the root lockfile that meant 207 packages changed, 687 entries removed, 174 added — including esbuild downgraded 0.27.7 → 0.24.2 and typescript floated to 6.0.3, which broke gate-frontend-build (packages/chat-client DTS build, TS5101: Option 'baseUrl' is deprecated). All seven lockfiles had the same churn.

Redone as npm update <specific packages> --package-lock-only. The root lockfile is now +1 entry, −0, ~1 version-changed vs master. Every entry that changes anywhere in this PR traces to a targeted package or its direct transitive.

Worth knowing for next time: plain npm install --package-lock-only reports "up to date" and silently does not apply a newly-added overrides entry. Only the package-scoped npm update form does both jobs correctly.

3. fuzefront-website/backend/package-lock.json is a CRLF file.

npm on Linux rewrites it as LF, which turns a 9-entry change into a 7,124-line whitespace diff — the "reordered file makes an eyeballed diff unreliable" trap CLAUDE.md warns about. Endings are preserved in this PR so the diff shows only the real changes. .gitattributes already forces LF for CSS, Helm values, and the vendored contract after this same problem bit three times; adding lockfiles to it is the right follow-up but belongs in its own PR, not a deps change.

4. A duplicate engines key was dropped from four manifests — disclosed, not intended.

sdk, services/billing-service, services/email-service, and services/sms-service each declared "engines" twice (issue #755). The JSON round-trip that added overrides collapsed the duplicate. Both copies were byte-identical (node >=24.0.0, npm >=10.0.0) and the first is retained, so the Node 24 floor is unchanged — verified on all four. It's strictly correct and re-adding a duplicate key would not be, so I kept it rather than reverting; flagging it because it wasn't part of the stated scope.

Correcting my own triage in #747 on react-router

I previously wrote that all four react-router findings clear on 6.30.6. That was wrong. It came from taking max() over Trivy's FixedVersion list, which enumerates fixes across multiple release branches — so a 6.x backport and a 7.x fix appear side by side and max() reads as "you must go to 7".

Actual state: 6.30.6 clears 2 of the 4; CVE-2026-53666 and CVE-2026-53669 list 7.18.0 only, with no 6.x backport. So 2 genuinely require v7 and remain deferred to their own PR — which needs a real-Chromium check under the ui-runtime-validation mandate, since FuzeFront is the Module-Federation host.

Not in scope

@github-actions github-actions Bot added the auto-merge Enable squash auto-merge once CI passes label Aug 26, 2026
@github-actions
github-actions Bot requested a review from izzywdev as a code owner August 26, 2026 18:04
@github-actions github-actions Bot added the auto-merge Enable squash auto-merge once CI passes label Aug 26, 2026
@izzywdev
izzywdev force-pushed the claude/deps-minors branch from f76de66 to 3f27f8f Compare August 26, 2026 18:04
@github-actions
github-actions Bot enabled auto-merge (squash) August 26, 2026 18:04
Bumps the minor/patch-fixable half of the #747 triage backlog. No major
version bumps; no test or gate weakening.

Direct bumps:
  backend/security          undici       ^7.28.0 -> ^7.29.0
  fuzefront-website/backend morgan       ^1.10.0 -> ^1.11.0
  fuzefront-website/frontend react-router-dom ^6.28.0 -> ^6.30.6

Overrides added:
  root                      ip-address    >=10.2.2   (-> 10.5.0)
  fuzefront-website/backend lodash        >=4.17.23  (-> 4.18.1)
                            qs            >=6.15.2   (-> 6.15.3)
                            validator     >=13.15.22 (-> 13.15.35)
                            body-parser   >=1.20.6 <2
  sdk                       ws            >=8.20.1   (-> 8.21.3)
  services/{billing,email,sms}-service
                            body-parser   >=1.20.6 <2
                            brace-expansion >=5.0.9

The body-parser upper bound is load-bearing. An open ">=1.20.6" resolves
2.3.0, which breaks express@4's 1.x API. Nothing in CI catches that.

Lockfiles were regenerated per-package rather than with a blanket
"npm update --package-lock-only", which floats the entire tree: the first
attempt at this change churned 207 root packages, downgraded esbuild
0.27.7 -> 0.24.2, and floated typescript to 6.0.3, taking the
chat-client DTS build down with TS5101. Every lockfile entry that
changes here now traces to a targeted package or its direct transitive.

fuzefront-website/backend/package-lock.json is committed with CRLF
endings; npm on Linux rewrites it as LF, which turns a 9-entry change
into a 7k-line whitespace diff. Endings are preserved here. Normalizing
it via .gitattributes belongs in its own change.

Also drops a duplicate second "engines" key from sdk and the three
services (issue #755). Both copies were identical (node >=24, npm >=10)
and the first is retained, so the Node 24 floor is unchanged. This fell
out of the JSON round-trip rather than being an intended edit, but it is
strictly correct and re-adding the duplicate would not be.

Refs #747, #755

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013tMciHkPE8To7V67CsgKKc
gate-line-endings rejects CRLF in any file a PR touches, and this lockfile
is the repo's only CRLF one. It was left as CRLF here to keep the
dependency diff readable; the gate is right and that was the wrong call.

#816 pins package-lock.json to LF in .gitattributes so this cannot recur.
Once it lands, rebasing this branch collapses this file's diff back to the
nine dependency entries that actually changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013tMciHkPE8To7V67CsgKKc

Copy link
Copy Markdown
Owner

Both failing checks are pre-existing on master and not caused by this PR. Fixes are open for each.

Dockerfiles install from the lockfile — the FFRNT-254 violation in services/selection-list-service/Dockerfile, a file this PR does not touch. Fixed by #817 (verified: that gate reports success on #817's head).

Trivy — reports "1 configuration present on refs/heads/master was not found: .github/workflows/ci.yml:security-scan", then lists pre-existing findings as new because there is no baseline to diff against. Fixed by #818 (verified: Trivy reports success on #818's head, and does so while Container Security Scan is still running — which is the decoupling that fix is for).

Not porting either fix here — both are green on their own gates with auto-merge armed, and porting would put four concurrently-open PRs on the same Dockerfile and workflow files. Once they land I'll update this branch onto the fixed base.


Separately, and more relevant to reviewing this PR: the lockfiles were rebuilt since the description was first written. The original pass used npm update --package-lock-only with no package arguments, which re-resolves the entire tree rather than the targeted packages. On the root lockfile that meant 207 packages changed, 687 entries removed, 174 added — including esbuild downgraded 0.27.7 → 0.24.2 and typescript floated to 6.0.3, which is what broke gate-frontend-build (TS5101 in the chat-client DTS build). All seven lockfiles had the same churn.

Redone with the package-scoped form (npm update <pkgs> --package-lock-only). The root lockfile is now +1 entry, −0, ~1 version-changed against master, and every changed entry anywhere in this PR traces to a targeted package or its direct transitive. gate-frontend-build passes.

Worth knowing for the next dependency PR: plain npm install --package-lock-only reports "up to date" and silently does not apply a newly-added overrides entry. Only the package-scoped npm update form does both jobs.


Generated by Claude Code

izzywdev and others added 2 commits August 27, 2026 07:26
The Trivy check flagged this as a genuinely new HIGH on this PR, and it
is right:

  fuzefront-website/backend/package-lock.json:6541
  tar-fs 2.1.3 — symlink validation bypass — CVE-2025-59343

It surfaces as "new" because regenerating this lockfile put the line in
the diff. The exposure is real either way.

Corrects my own #747 triage, which listed tar-fs as a deferred 2 -> 3
MAJOR. It is not: the fix is backported to the 2.x line and 2.1.5 exists.
The same max()-over-FixedVersion mistake that mis-triaged react-router —
Trivy enumerates fixes across multiple release branches, so a 3.x entry
next to a 2.x one reads as "you must go to 3".

Bounded ">=2.1.5 <3" rather than left open, for the same reason as
body-parser in this PR: an unbounded floor crosses the major, and
prebuild-install requires tar-fs ^2.0.0.

Scoped by measurement, not assumption — every lockfile in the repo was
checked for tar-fs and only this one was behind:

  package-lock.json                             2.1.5   (already fixed)
  fuzefront-website/backend/package-lock.json   2.1.3 -> 2.1.5

The root already resolving 2.1.5 also confirms 2.1.5 is valid for this
dependency shape.

Diff is three lines, tar-fs only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013tMciHkPE8To7V67CsgKKc

Copy link
Copy Markdown
Owner

The Trivy failure on this PR was real, not the phantom-baseline one — and it's now fixed in 904911a7.

Worth noting the difference from the earlier failures, because it shows the gate started working:

earlier runs this run
Title 1 configuration not found 1 new alert including 1 high severity security vulnerability
Baseline warning present gone
Verdict phantom — pre-existing findings with no baseline to diff against a genuine new finding

With #818 on master the baseline is complete, so the check could finally do a real diff. What it found:

fuzefront-website/backend/package-lock.json:6541
tar-fs 2.1.3 — tar-fs symlink validation bypass — CVE-2025-59343  (HIGH)

It surfaces as "new" because regenerating this lockfile put the line into the diff. The exposure is real either way, so it's fixed rather than annotated.

Correcting my own #747 triage again

I listed tar-fs there as a deferred 2 → 3 major. That was wrong — the fix is backported to the 2.x line and 2.1.5 exists. Same mistake as the react-router one I corrected earlier in this PR: taking max() over Trivy's FixedVersion, which enumerates fixes across multiple release branches, so a 3.x entry sitting beside a 2.x one reads as "you must go to 3".

Fix is "tar-fs": ">=2.1.5 <3" — bounded for the same reason as body-parser in this PR, since prebuild-install requires tar-fs ^2.0.0 and an unbounded floor would cross the major.

Scoped by measurement

Every lockfile in the repo was checked rather than assuming this was the only one:

package-lock.json                             2.1.5   (already fixed)
fuzefront-website/backend/package-lock.json   2.1.3 → 2.1.5

The root already resolving 2.1.5 independently confirms it's valid for this dependency shape. Diff is three lines, tar-fs only.


Separately: the remaining Trivy red on #816 is still the baseline/race issue, which #818 did not fully fix. Measured on #816 afterwards the check ran 04:27:07 → 04:27:09 — two seconds — and still reported a missing configuration. Two configurations under one SARIF tool race regardless of how far apart they finish. #820 fixes that properly with one configuration per tool.


Generated by Claude Code

@izzywdev
izzywdev merged commit 2038552 into master Aug 27, 2026
74 checks passed
@izzywdev
izzywdev deleted the claude/deps-minors branch August 27, 2026 06:35
izzywdev added a commit that referenced this pull request Aug 27, 2026
The existing globs pin package.json only — neither `package.json` nor
`**/package.json` matches `package-lock.json`, so lockfiles were never
pinned.

This matters more for a lockfile than for ordinary source: npm rewrites
the whole file on every resolution, so a CRLF-stored lockfile regenerated
on Linux comes back entirely LF and a 9-entry dependency change renders as
a 7,124-line diff — hiding the real change from review and tripping
gate-line-endings at the same time. That happened on #814.

The EOL normalisation this PR originally also carried is already on master
via #814, so this is now just the durable rule: 8 lines, one file.
claude Bot added a commit that referenced this pull request Aug 27, 2026
…etect them (#822)

Root cause: the #646/#647 engines sweep appended a second "engines" block to
32 package.json manifests that already had one (likely a re-run of the sweep
over an already-touched worklist). JSON.parse silently keeps the LAST
occurrence of a duplicate key, so every one of these manifests parsed to a
single well-formed engines value and nothing complained -- gate-toolchain's
entire job is policing engines, and it was structurally blind to engines
being declared twice, because it reads manifests via JSON.parse.

PR #814 already fixed 4 of the original 36 (sdk, services/billing-service,
services/email-service, services/sms-service) as a side effect of an axios
bump. Re-measured from latest origin/master: 32 manifests were still
affected. All 32 duplicate pairs were verified byte-identical
(node ">=24.0.0", npm ">=10.0.0") before collapsing -- no divergent copies
were found, so this is a mechanical de-dup, not a behavior change.

Part B: gate-toolchain.mjs now tokenizes each package.json's raw text and
walks brace/bracket nesting to find top-level keys declared more than once,
BEFORE any JSON.parse-based check runs -- so a duplicate is caught even
though JSON.parse can't see it. Generalized beyond "engines" per the issue's
suggestion: any duplicated top-level key in a manifest is a latent version of
this same bug (JSON.parse takes last, other parsers take first or reject,
npm normalises and silently discards one). Verified the new check both
detects the regression (re-ran the gate against the pre-fix duplicates via
git stash -- 12/12 correctly flagged and failed) and produces zero false
positives on legitimately repeated key names nested under different parents.


Claude-Session: https://claude.ai/code/session_013tMciHkPE8To7V67CsgKKc

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
claude Bot added a commit that referenced this pull request Aug 27, 2026
…xpress@4/body-parser@2 regression (#838)

Re-measured #747 from latest master (axios #754, tar+dead-lockfile #813, and the
7-manifest minor/patch backlog #814 were already merged). Re-verified every
"needs a major" claim against the currently-installed major line per the
methodology correction in the issue (Trivy's FixedVersion spans release
branches; max() over it overstates the required bump) — using `npm audit`
against isolated pinned-version fixtures as a stand-in for Trivy, which this
sandbox cannot reach (ghcr.io/aquasec denied, same as the issue's own note).

Root cause found while re-measuring: root package.json's `body-parser` override
(from #814) was `">=1.20.6"` with NO upper bound — the exact express@4 +
body-parser incident this repo's CLAUDE.md already documents as having
happened once before. It had silently jumped `backend/applications`'s
express@4.22.2 to body-parser@2.3.0 (a major, breaking API line) instead of
the intended 1.20.6 patch. Bounded it to `<2`, matching every other manifest's
copy of this same override, and it correctly resolved back down to 1.20.6.

Also found and fixed: package-lock.json's `packages[""]` never mirrors
package.json's `overrides` field in this repo's lockfiles (confirmed on 5
manifests) — this is *why* a plain `npm install --package-lock-only` silently
no-ops on a new override (per the issue's warning): npm's fast-path "up to
date" check trusts a lockfile that already looks internally consistent, and
without the mirror it never rechecks deeply-nested exact-pinned subtrees.
Re-synced the mirror after every scoped `npm update <pkg> --package-lock-only`
in this PR; a plain (non-scoped) install strips it again, so do not run one
after this without re-syncing.

Genuinely fixed (same-major, `npm update <pkg> --package-lock-only`,
package-scoped, no bare tree-wide update):
- root: undici 7.28.0 -> 7.29.0 (dev-only, via wrangler/miniflare)
- root + services/{sms,email,billing}-service: qs 6.11.0 -> 6.15.3 (new
  bounded override; was transitive via express@4.19.2's exact "6.11.0" pin,
  npm's override engine wasn't cascading into it — 4 nested trees inside
  root's own workspace lockfile needed a direct hand-patch to real,
  registry-verified 1.20.6/6.15.3 metadata after `npm update`/`dedupe`
  repeatedly no-opped on them; a real peer conflict on express-rate-limit
  across backend/backend-applications/security-service blocks a full
  `npm dedupe` from reconciling this on its own)
- root + services/{chat,email,notification,selection-list}-service (nested in
  root's own lockfile): body-parser 1.20.2 -> 1.20.6 (same patch, alongside
  the major-regression fix above)
- fuzefront-website/frontend: ws 8.18.3 -> 8.21.3 (new bounded override; dev
  dependency via jsdom, clears a HIGH DoS CVE plus the MEDIUM from #747)

Confirmed already fixed by #813/#814, no action: axios, tar, tar-fs,
ip-address, lodash, validator, morgan, follow-redirects, @remix-run/router,
@tootallnate/once (2.0.1 has no CVE — the 3.0.1 major floated in #747 was
never actually required).

Confirmed genuinely blocked on a major (left as-is, evidence below):
- uuid 9.0.1 (root, services/email-service, services/billing-service): the
  9.x line has no further release (9.0.0-beta.0, 9.0.0, 9.0.1 — nothing
  since); the fix requires >=11.1.1.
- react-router(-dom) 6.30.6 in fuzefront-website/frontend: already the
  maximum safe 6.x fix (clears GHSA-9jcx-v3wj-wh4m and GHSA-2j2x-hqr9-3h42);
  2 of the original 4 CVEs (GHSA-wrjc-x8rr-h8h6, GHSA-337j-9hxr-rhxg) require
  >=7.18.0 and were correctly never claimed fixable in #747's corrected
  telling. root/ and frontend/'s own react-router are unaffected — both
  already on v7 (7.18.2 / 7.18.0) as a pre-existing, unrelated dependency
  choice, not part of this CVE backlog.

Verified: `npm install --package-lock-only` (root and all 4 touched
sub-manifests) is idempotent post-fix (stable content hash across repeat
runs); `npm ls <pkg> --package-lock-only --workspaces` confirms every target
package now resolves to the intended version tree-wide; `node --eval
JSON.parse(...)` on all 5 touched package-lock.json files. No `npm ci` /
build run in this sandbox (disk-constrained, no network to a full registry
mirror) — CI's own `npm ci` + build + gate-toolchain matrix is the remaining
verification surface once this PR is up.


Claude-Session: https://claude.ai/code/session_013tMciHkPE8To7V67CsgKKc

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auto-merge Enable squash auto-merge once CI passes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants