.cursor/skills/security-vuln-remediation/SKILL.md
Remediate security vulnerabilities found by Grype or pnpm audit. Use when a security scan fails, a CVE needs fixing, or you need to analyze, upgrade, override, or ignore a vulnerable dependency.
npx skillsauth add stacklok/toolhive-studio security-vuln-remediationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Playbook for analyzing and remediating security vulnerabilities in this Electron/Node.js project. Applies to both CI (Cursor agent in GitHub Actions) and local development.
pnpm-lock.yaml).grype.yaml (only reports vulnerabilities with a fix available)pnpm audit --prod --audit-level=moderatepnpm.overrides in package.json — used to pin transitive dependencies to patched versionsdependencies in package.jsondevDependencies in package.json — not shipped to usersFollow these steps in order. Stop at the first step that resolves the vulnerability.
Run both scanners and capture the output:
grype . --config .grype.yaml
pnpm audit --prod --audit-level=moderate
For each finding, record:
CVE-2024-12345)For each vulnerable package, understand who pulls it in:
pnpm why <package-name>
Record:
dependencies (production) or devDependencies (dev-only)Check if a patch/minor upgrade of the direct dependency resolves the CVE:
pnpm update <direct-dependency>
Or, if the vulnerable package is a direct dependency itself, update its version range in package.json.
After the change:
pnpm install to regenerate the lockfilegrype . --config .grype.yaml to verify the vulnerability is goneIf the upgrade resolves it, this step is complete. Move to the next vulnerability.
If no non-breaking upgrade is available, or the fix requires a major version bump of a transitive dependency that is safe to force:
Add or update an entry in pnpm.overrides inside package.json. You MUST follow the existing override style used in the project. Current overrides look like this:
{
"pnpm": {
"overrides": {
"fast-xml-parser": ">=5.5.9",
"lodash-es": ">=4.17.23",
"tar": ">=7.5.7"
}
}
}
Rules for overrides (follow strictly):
Always use the >= prefix for values — this allows future patches. Example: ">=1.2.3".
BEFORE writing any override, check pnpm why <package> for multiple major versions. This is mandatory. If the tree contains more than one major line, an unscoped override would force ALL consumers onto the overridden version, breaking packages that expect a different major.
Use a simple top-level override ONLY when one major line exists — if every installation of the package is on the same major, a single "package": ">=fixed" entry is safe:
{ "some-package": ">=1.2.3" }
Use parent-scoped overrides when multiple majors coexist — scope the override to the dependency path that pulls in the vulnerable version using "parent>package" syntax:
{ "parent-pkg>vulnerable-pkg": ">=2.0.1" }
This only overrides vulnerable-pkg when required by parent-pkg, leaving other consumers on their compatible major. Pick the nearest direct parent that exclusively uses the vulnerable major line.
WRONG — never use an unscoped override when multiple majors exist:
{ "vulnerable-pkg": ">=2.0.1" }
This would force every consumer onto v2, breaking those that depend on v1.
Only override actually vulnerable versions — if pnpm why shows multiple major lines but only one is in the advisory's vulnerable range, override only that version's path. Do not add overrides for versions that are not vulnerable.
Use only "package" or "parent>package" as override keys — do not use version-range selectors like @>=1.0.0 <2.0.0 in keys. Scope via parent instead.
After the change:
pnpm install to regenerate the lockfilegrype . --config .grype.yaml to verify the vulnerability is goneOnly if ALL of the following are true:
devDependencies tree)Add an ignore entry to .grype.yaml:
ignore:
- vulnerability: CVE-XXXX-XXXXX
package:
name: <package-name>
type: npm
reason: >-
<package-name> is a dev-only dependency (used by <parent-package> for <purpose>).
<CVE-ID> requires <attack-vector> which does not apply to this Electron desktop app.
Fix requires a breaking major upgrade of <parent> that cannot be safely applied.
Every ignore MUST include a reason explaining why it is safe to suppress.
For each vulnerability processed, write a summary containing:
| Field | Description |
| ----------------- | --------------------------------------------- |
| CVE ID | The vulnerability identifier |
| Package | Affected package name and version |
| Severity | Critical / High / Medium |
| CVSS Score | Numeric score if available |
| Attack Vector | Network / Local / Adjacent / Physical |
| Production Impact | Yes (in dependencies tree) or No (dev-only) |
| Action Taken | Upgraded / Overridden / Ignored |
| Verification | Whether grype/audit passes after the fix |
When running in plan mode (Phase 1): write all findings and proposed actions to remediation-plan.md in the repository root. Do not modify project files.
When running in implementation mode (Phase 2): read remediation-plan.md, apply the changes to package.json, pnpm-lock.yaml, and .grype.yaml as needed, then verify with grype. Update remediation-plan.md with verification results.
Also write a concise pr-body.md for the pull request description using this exact structure:
## Summary
<1-2 sentence description of what was fixed and why>
## Changes
| CVE | Package | Severity | Production | Action | Verified |
| --- | ------- | -------- | ---------- | ------ | --------- |
| ... | ... | ... | Yes/No | ... | Pass/Fail |
## Files Modified
- `package.json`: <what changed>
- `.grype.yaml`: <what changed, if applicable>
## Verification
- `pnpm audit --prod`: <Pass/Fail>
- `grype . --config .grype.yaml`: <Pass/Fail>
Keep the PR body short and scannable. The full analysis stays in remediation-plan.md for reference but is not used in the PR.
Also write a single-line conventional-commit-style title to remediation-title.txt summarizing the specific changes, for example:
fix(security): upgrade tar to 7.5.7, override lodash (CVE-2025-1234, CVE-2025-5678)fix(security): add grype ignore for tmp (dev-only, CVE-2025-9999)fix(security): override fast-xml-parser >=5.5.9 (CVE-2025-4321)Keep it under 72 characters. Mention the key packages and CVEs, not a generic description.
git commands — git operations are handled by the CI workflowgh commands — PR creation is handled by the CI workflow.env filespnpm install after modifying package.json to regenerate the lockfilegrype . --config .grype.yaml after each remediation to verifydevelopment
Start here for all API mocking in tests. Covers auto-generation, fixtures, and when to use other skills. Required reading before creating, refactoring, or modifying any test involving API calls.
development
Test that components send correct query parameters or request arguments. Use when testing filtering, sorting, pagination, or any read operation where request parameters matter. Use for test-scoped mock customization.
development
Verify API requests in tests. Use when testing that correct API calls are made for create, update, or delete operations. Use when testing mutations, form submissions, or actions with backend side effects.
development
REQUIRED for editing any skill file. Ensures changes sync to Claude, Codex, and Cursor. Never edit .claude/skills/ files directly - always use this skill.