chore(deps): full dependency audit — 14 advisories to 0, adopt Vitest 5 stack - #25
Merged
Merged
Conversation
Runs `npm update --save`, which resolves every open npm audit finding (1 critical, 9 high, 4 moderate) without any major version bump: - next 16.2.10 -> 16.3.5 (CRITICAL: unauthenticated RCE on Windows hosts GHSA-p293-qw3h-jr36, plus proxy bypass, SSRF, cache confusion, and Image Optimization RCE/DoS) - postcss 8.5.16 -> 8.5.28 (XSS + sourceMappingURL arbitrary file read) - @xmldom/xmldom 0.9.10 -> 0.9.12 via docxtemplater (name injection, ReDoS) - undici, sharp, svgo, js-yaml, nanoid, browserslist (transitive) - vitest/@vitest/mocker 4.1.10 -> 4.1.11 (mocker path traversal) - react/react-dom 19.2.7 -> 19.3.0, lucide-react 1.24 -> 1.45, Radix primitives, and the remaining in-range minors npm audit: 14 vulnerabilities -> 0. Tests: 805 passing, unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Each of these has zero references outside package.json (verified by
repo-wide grep excluding node_modules/.next):
- openai no import anywhere; also removes the pending 6 -> 7 major
- @types/js-cookie js-cookie itself is not a dependency (and was declared
under "dependencies", not "devDependencies")
- @types/react-syntax-highlighter the runtime package is not installed
- autoprefixer not referenced by postcss.config.mjs, which loads only
@tailwindcss/postcss; Tailwind v4 prefixes via Lightning CSS
- @tailwindcss/typography never registered with @plugin in globals.css and
no `prose` class is used anywhere
Verified: `next build` compiles successfully, 805 tests pass, eslint clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…alk 6 All four majors were viable; each was applied and verified separately. - vitest + @vitest/coverage-v8 4.1.11 -> 5.0.0. Vite is already 8.3.0, so the vite ^6.4||^7||^8 peer is satisfied. Vitest 5 clears mocks before each test by default, which this suite already did explicitly, so all 805 tests pass unchanged. Verified `test:coverage` still writes coverage/coverage-final.json — the exact path .github/workflows/test.yml uploads to Codecov — since Vitest 5 relocated several other reporter outputs to .vitest/. - jsdom 29 -> 30, chalk 5 -> 6 (scripts/setup.ts only; `setup:check` verified), @testing-library/jest-dom 6 -> 7. - jest-dom 7 moved its Vitest `expect` type augmentation to the '@testing-library/jest-dom/vitest' entry point. The bare import still registers matchers at runtime, so tests passed while `next build` reported 60 spurious "Property 'toBeInTheDocument' does not exist" errors. src/test-setup.ts now imports the /vitest entry. engines.node: >=20 -> ^22.22.2 || ^24.15.0 || >=26.0.0. Every package above requires Node >=22, and jsdom 30's range is the strictest; the old >=20 would have let an install succeed and then fail at runtime. Node 20 reached end-of-life in April 2026. CI already runs Node 24. Type-check output is unchanged at the same 12 pre-existing errors. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds a repeatable process so the analysis behind this audit is not re-derived every time: - .claude/commands/update-deps.md — the /update-deps command. Applies in-range updates, evaluates majors one at a time, sweeps OSV.dev (which carries advisories npm audit's GitHub-reviewed-only feed does not), runs supply-chain checks, and writes a record. - .claude/packages/ — one record per audit, plus README conventions. The "held back" section is the point: each hold carries the upstream condition that clears it and the command that re-checks it. - CLAUDE.md — Dependencies section with the audit history table, the current holds, and two facts worth not rediscovering: CI never runs `next build`, and CI's Codecov upload depends on the exact path coverage/coverage-final.json. Also documents the jest-dom v7 `/vitest` entry-point requirement under Testing, since that break is invisible to the test suite. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The audit records were silently untracked: .gitignore carries a NuGet rule from the Visual Studio template, `**/[Pp]ackages/*` (line 208), which also matches .claude/packages/. Negated with `!.claude/packages/**` — the directory-only form does not work here, since the NuGet rule excludes the contents rather than the directory. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Full dependency audit: in-range updates, per-major evaluation, an advisory sweep beyond
npm audit, and a repeatable process so this does not have to be re-derived next time.Security: 14 → 0
Every advisory was cleared by in-range updates; no major bump was required for security.
The headline is
next16.2.10 → 16.3.5, which carried a critical advisory plus ten others. Three matter structurally for this app:src/proxy.ts, so a bypass here is an authentication bypass.Also closed:
postcss(XSS +sourceMappingURLarbitrary.mapread),@xmldom/xmldomvia docxtemplater,undici,sharp,svgo,js-yaml,nanoid,@vitest/mocker.Sweep beyond
npm auditnpm auditreports only GitHub-reviewed advisories, so all 472 packages in the resolved tree were additionally queried against OSV.dev.One hit, confirmed false positive:
grapesjs@0.22.16→ GHSA-589f-c66p-hxr4. The advisory text says fixed in 0.19.5; the machine-readable record declares{"introduced": "0"}with nofixedevent, so it matches every version ever published. Documented so it is not re-triaged.Supply chain: no deprecated packages in the tree.
zod@4.6.4was published the same day it was installed — verified before accepting (sole maintainercolinhacks, published by GitHub Actions with npm provenance attestations, zero runtime deps, no install scripts).Majors adopted
Vitest 5 +
@vitest/coverage-v85, jsdom 30, jest-dom 7, chalk 6 — each applied and verified individually.Two notes worth reading:
expecttype augmentation to the@testing-library/jest-dom/vitestentry point. The bare import still registers matchers at runtime, so 805 tests passed whilenext buildgained 60 type errors.src/test-setup.tsnow imports the/vitestentry..vitest/. Coverage was not moved, but it was verified explicitly thattest:coveragestill writescoverage/coverage-final.json— the exact pathtest.ymluploads to Codecov. Had it moved, CI would have gone green while uploading nothing.engines.node:>=20→^22.22.2 || ^24.15.0 || >=26.0.0, mirroring the strictest dev dependency (jsdom 30). Node 20 reached EOL in April 2026; CI already runs Node 24.Majors held back
Each recorded with the upstream condition that clears it and a one-line re-check command:
typescript7typescript-eslintpeerstypescript: >=4.8.4 <6.1.0eslint10eslint-plugin-reactcalls a removed rule-context method.7.37.5is the latest release and peerseslint ^9.7, so no npmoverridefixes itgrapesjs0.23@grapesjs/react@2.0.0(latest) peersgrapesjs ^0.22.5Removed — 5 unreferenced packages
openai(zero imports — also makes the pending 6 → 7 major moot),@types/js-cookie(runtime package not installed, and misfiled underdependencies),@types/react-syntax-highlighter(runtime package not installed),autoprefixer(not inpostcss.config.mjs; Tailwind v4 prefixes via Lightning CSS),@tailwindcss/typography(never registered with@plugin, noproseusage).Process
/update-deps(.claude/commands/update-deps.md) makes this repeatable..claude/packages/holds one record per audit. The "held back" section is the point — it keeps the next audit from re-deriving the same blockers.CLAUDE.mdgains a Dependencies section with the audit history and current holds..gitignorewas silently swallowing.claude/packages/via a NuGet rule inherited from the Visual Studio template (**/[Pp]ackages/*); negated.Pre-existing, NOT fixed here
npm run buildalready fails ondevwith 12 type errors in three test files (MP fixtures using snake_case wire names against camelCase types). Confirmed pre-existing — reproduced when onlypackage.json/package-lock.jsondiffered. CI does not catch this:test.ymlruns onlynpm run test:coverage, nevernpm run build.Being handled separately. Adding
npm run build(ortsc --noEmit) to CI afterwards would stop it regressing.Verification
npm auditeslint .next buildtype errors🤖 Generated with Claude Code