Fix Audit Vulnerabilities
Workflow for identifying, fixing, and verifying high-severity security vulnerabilities in the saved-views pnpm monorepo.
When to Use This Skill
- User asks to "fix audit vulnerabilities" or "run pnpm audit"
- User mentions "security issues", "CVEs", or "dependency vulnerabilities"
- CI audit check is failing
- User wants to "update audit" or "resolve security advisories"
Prerequisites
- pnpm >= 10 installed
- Node >= 20
- Run commands from the repository root (
e:\saved-views_1\saved-views\) - On Windows, prefix commands with
cmd /c "cd /d <repo-root> && <command> 2>&1"
Step-by-Step Workflow
Step 1: Run the Audit
Run run-audit.ps1 or execute directly:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process
cd e:\saved-views_1\saved-views
pnpm audit 2>&1
Note: Run without
--audit-level highso that moderate vulnerabilities are also visible — they may be fixed just as easily.
Read the output carefully. Note:
- Package name with the vulnerability
- Severity (high or critical)
- Via chain (which of your direct deps pulled it in)
- Fix available — whether a non-breaking fix exists
Step 2: Diagnose the Affected Packages
Run pnpm why for each vulnerable package — this is mandatory, not optional. You need to know whether each package is a direct dependency or purely transitive before you can choose the right fix strategy. Guessing leads to incomplete fixes.
pnpm why <vulnerable-package>
Caveat:
pnpm whyonly searches the current workspace root, not sub-packages. If it returns nothing, the package is still likely installed as a transitive dep — confirm by checkingpnpm-lock.yamldirectly:Select-String -Path pnpm-lock.yaml -Pattern "^ <vulnerable-package>@"
For each vulnerable package, record:
- Is it a direct dep? Check each
package.jsonthat appears inpnpm why's output - Full dependency path(s): e.g.
eslint@8.57.1 > cross-spawn@7.0.3— these tell you which packages could be bumped to eliminate the transitive pull
Check the sub-package package.json files in:
packages/saved-views-client/package.jsonpackages/saved-views-react/package.jsonpackages/test-app-frontend/package.jsonpackages/test-app-backend/package.json- Root
package.json
Step 3: Choose a Fix Strategy
Consult audit-fix-guide.md for strategy details.
| Situation | Strategy |
|---|---|
| Direct dependency, non-breaking update available | Bump version in the relevant package.json |
| Transitive dependency, fix available | Add pnpm.overrides in root package.json |
| No fix available yet | Add pnpm.overrides to pin to the least-vulnerable version |
| Breaking major version bump needed | Update code for compatibility before bumping |
Step 4: Apply the Fix
Option A — Bump a direct dependency:
Edit the relevant package.json and update the version range, then:
cmd /c "cd /d e:\saved-views_1\saved-views && pnpm install 2>&1"
Use a range, not a pin: Write
"^7.0.5"or">=7.0.5"rather than"7.0.5"so future patch releases are automatically resolved. Exact pins require manual bumps every time a new patch drops.
Option B — Add/update a pnpm override (root package.json):
{
"pnpm": {
"overrides": {
"vulnerable-package@<fixed-version": ">=fixed-version"
}
}
}
Then run pnpm install.
Override key syntax: Always use comparison-operator ranges on the key (e.g.
"pkg@<1.2.3","pkg@>=4.0.0 <5.0.5") rather than caret/tilde ranges (e.g."pkg@^4"). Caret/tilde ranges in keys may not match correctly.Override value syntax: Use
">=fixed-version"(not a pinned exact version) so future patches are still resolved. For the value,>=works correctly even though it looks unbounded — pnpm resolves the minimum satisfying version.Scope overrides precisely: If a vulnerability only affects one major version range, restrict the key accordingly (e.g.
"picomatch@>=4.0.0 <4.0.4": "4.0.4") rather than a bare"picomatch"which would force all consumers, including those on a safe^2.xrange, to use the overridden version.
Step 5: Consider Deleting the Lockfile for a Fresh Resolve
If some overrides don't seem to take effect, delete pnpm-lock.yaml and re-install to force a full dependency re-resolution:
Remove-Item pnpm-lock.yaml
pnpm install
This ensures all overrides are applied from scratch rather than reusing cached resolutions.
Step 6: Verify the Fix
Re-run the audit to confirm vulnerabilities are resolved:
pnpm audit 2>&1
Step 7: Minimize the Fix Footprint
Before finishing, review every change you made and ask whether it can be simplified:
Can an override be replaced by bumping a direct dep?
For each override, trace the chain with pnpm why and check if the direct dep that pulls in the vulnerable package has a newer version where the vulnerability is already fixed:
pnpm view <direct-dep>@latest dependencies --json
If yes, bump that direct dep and remove the override — a self-maintaining dep bump is always preferable to a long-lived override.
Did you add a direct dep that shouldn't exist?
If pnpm why showed the vulnerable package was only transitive, but you added a direct devDependency entry just as a fix mechanism, reconsider: adding a dep purely to control its version creates unnecessary maintenance burden. A scoped pnpm.overrides entry achieves the same result with less footprint. Remove the spurious direct dep and use an override instead.
Only keep changes that are truly necessary.
Step 8: Confirm Nothing is Broken
Run build, tests, and lint to make sure the fix didn't break anything:
pnpm run build 2>&1
pnpm test 2>&1
pnpm run lint -- --max-warnings 0 2>&1
Warning — ESLint plugin upgrades: If you upgraded
@typescript-eslint/eslint-pluginor@typescript-eslint/parseras part of the fix, major version bumps (e.g. v7 → v8) can remove or rename rules, causing lint to break even if audit passes. Common v8 breakages:
- Formatting rules removed:
@typescript-eslint/comma-dangle,@typescript-eslint/quotes,@typescript-eslint/member-delimiter-style→ replace with base eslint equivalents (comma-dangle,quotes) and dropmember-delimiter-style@typescript-eslint/ban-typesremoved → use@typescript-eslint/no-empty-object-typeor disable itno-unused-expressionsdefault changed → add["error", { "allowShortCircuit": true, "allowTernary": true }]to restore previous behavior- New rules added to
recommended-type-checkedmay flag existing code (e.g.no-duplicate-type-constituents,only-throw-error)Always run lint immediately after any
@typescript-eslintupgrade and before considering the fix complete.
If type errors appear, run typecheck for details:
pnpm run typecheck 2>&1
Important Notes
- Never use
npm— this project blocks npm ("npm": "<0"in engines). Always usepnpm. - pnpm overrides live under the
"pnpm"key in the rootpackage.json, not"resolutions". - After editing any
package.json, always runpnpm installto regeneratepnpm-lock.yaml. - If a fix requires a major version bump of a published package (
saved-views-clientorsaved-views-react), check for API breaking changes before applying.