Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
248 changes: 248 additions & 0 deletions .agent/skills/fix-npm-vulnerability/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
---
name: fix-npm-vulnerability
description: Fix a pnpm/npm security advisory in a pnpm monorepo. First attempts to update the head dependency; falls back to a pnpm override with a tracking GitHub issue. Use when pnpm audit or Dependabot surfaces a vulnerability.
allowed-tools: Bash(*) Read(*) Edit(*) WebSearch(*)
---

# Fix npm Vulnerability

You are resolving a security advisory in a pnpm monorepo.

## Step 0 — Gather the advisory details

Ask the user for one or both of:

> **What vulnerability do you want to fix?**
> Provide a CVE ID (e.g. `CVE-2026-44705`), GHSA slug (e.g. `GHSA-w7jw-789q-3m8p`), or vulnerable package name.
> Optionally paste the `pnpm audit` output line for it.

Wait for the answer before proceeding.

---

## Step 1 — Locate the workspace root

Find the directory containing `pnpm-workspace.yaml`, starting from the current directory and walking up:

```bash
dir=$(pwd); while [ "$dir" != "/" ]; do [ -f "$dir/pnpm-workspace.yaml" ] && echo "$dir" && break; dir=$(dirname "$dir"); done
```

If nothing is found, ask the user to navigate to the monorepo root first.

All subsequent commands run from this root.

---

## Step 2 — Get the full audit picture

```bash
pnpm audit --json 2>/dev/null | jq '.advisories // .vulnerabilities // .' 2>/dev/null | head -400
```

Identify for each advisory matching the user's input:
- **`vulnerablePackage`** — the transitive package that contains the flaw
- **`patchedVersions`** — the version range that fixes it (e.g. `>=4.0.6`)
- **`via`** — the dependency chain showing which head (direct workspace) dep pulls it in
- **`advisoryId`** — CVE or GHSA identifier
- **`severity`** — high / critical

---

## Step 3 — Traverse the dependency chain

Use `pnpm why` to confirm the full chain from the workspace to the vulnerable package:

```bash
pnpm why <vulnerable-package> 2>&1 | head -60
```

This shows every head dep that transitively depends on it. Record:
- The **head package(s)** (direct workspace deps) pulling in the vulnerable version
- The **current version** of those head packages

---

## Step 4 — Check if updating the head dep resolves the issue

For each head package, inspect what version of the vulnerable package its latest release ships:

```bash
# Latest available version of the head dep
npm info <head-package> version

# Its full dependency tree for the vulnerable package
npm info <head-package>@latest dependencies --json 2>/dev/null | jq '.<vulnerable-package> // "not direct"'

# If it's deeper, check peer dependencies too
npm info <head-package>@latest peerDependencies --json
```

For transitive chains deeper than one level, check the registry manifest:

```bash
npm pack <head-package>@latest --dry-run --json 2>/dev/null | head -50
```

**Decision:**

**A. If the latest `<head-package>` ships `<vulnerable-package>` at a version satisfying `<patchedVersions>`:**
→ The update resolves the issue. Continue to Step 5a.

**B. If the latest `<head-package>` still ships the vulnerable version** (fix not yet released upstream):
→ An override is required. Continue to Step 5b.

---

## Step 5a — Update the head dependency

Check whether this is a **major version bump**:

```bash
current=$(node -p "require('./node_modules/<head-package>/package.json').version" 2>/dev/null)
latest=$(npm info <head-package> version)
echo "current=$current latest=$latest"
```

**If it IS a major bump**, before touching any files, generate testing guidance (Step 6) and show it to the user. Ask:

> This is a major version upgrade for `<head-package>` (`<current>` → `<latest>`).
> I've listed the affected workspaces and test commands above.
> Shall I proceed with the upgrade?

Wait for confirmation.

Update the dep:

```bash
pnpm update <head-package>@latest
```

Verify the vulnerable package is now at the patched version:

```bash
pnpm why <vulnerable-package> 2>&1 | grep -E "version|<vulnerable-package>"
pnpm audit --audit-level=high 2>&1 | grep -c "high\|critical" && echo "ISSUES REMAIN" || echo "CLEAN"
```

If clean, skip Steps 5b, 7, and 8. Report the upgrade (and testing checklist if it was a major bump).

---

## Step 5b — Add the pnpm override

**First create the GitHub issue (Step 7)** so you have the URL to embed in the comment.

Then open `pnpm-workspace.yaml` and add under `overrides:`:

```yaml
# JUSTIFICATION: Fixes <CVE/GHSA-slug> — <one-line description of the flaw>.
# Added <YYYY-MM-DD>. Remove once <head-dep> ships a release containing <vulnerable-package>@<patchedVersions>.
# Tracking: <github-issue-url>
<vulnerable-package>: '<patchedVersion>'
```

Use scoped syntax when the vulnerability is only reachable through one specific head dep:

```yaml
<head-package>><vulnerable-package>: '<patchedVersion>'
```

Match the comment and formatting style of existing entries in the file.

The override needs `pnpm install` to take effect in the lockfile — that regenerates resolutions
for the whole tree, not just the pinned package, so it can pull in unrelated drift (a transitive
dep bumping a patch version elsewhere). Ask before running it:

> I've added the override to `pnpm-workspace.yaml`. Running `pnpm install` now will regenerate
> `pnpm-lock.yaml` to match — that can also shift unrelated transitive versions. Shall I proceed?

Wait for confirmation, then run:

```bash
pnpm install
pnpm audit --audit-level=high 2>&1 | grep -c "high\|critical" && echo "ISSUES REMAIN" || echo "CLEAN"
```

Make sure `pnpm-lock.yaml` ends up staged/committed together with the `pnpm-workspace.yaml` edit —
don't leave the lockfile change uncommitted for a later, unrelated commit to pick up.

---

## Step 6 — Testing guidance (major version bumps only)

When the head dep crosses a major version, output a checklist **before** applying the change.

**Affected workspaces** — every workspace that depends on `<head-package>`:

```bash
grep -rl '"<head-package>"' --include='package.json' . | grep -v node_modules
```

**Changelog / breaking changes** — fetch release notes:

```bash
npm info <head-package>@latest 2>/dev/null | grep -A5 "homepage\|repository"
```

Then `WebSearch` for `"<head-package> v<major> migration guide"` or its changelog.

**Test commands to run after the upgrade:**
- For each affected workspace: `pnpm --filter <workspace> test`
- If the workspace has a dev server: `pnpm --filter <workspace> dev` — note which URLs or UI flows exercise this package
- Run the full audit: `pnpm audit --audit-level=high`

Present this as a numbered checklist the developer can follow manually after the PR lands.

---

## Step 7 — Create the tracking GitHub issue (override path only)

Detect the remote:

```bash
git remote get-url origin 2>/dev/null
```

```bash
gh issue create \
--title "[Dependency Management 🔒] Remove Pnpm Override for <vulnerable-package> Once <head-dep> Ships the Fix" \
--label "security,maintenance" \
--body "$(cat <<'EOF'
## Tracking Override Added on <YYYY-MM-DD>

| Field | Value |
|---|---|
| Advisory | <CVE/GHSA-slug> |
| Vulnerable package | `<vulnerable-package>` |
| Fix requires | `<vulnerable-package>@<patchedVersions>` |
| Blocked by | `<head-dep>` — fix not yet released upstream |
| Override location | `pnpm-workspace.yaml` |

## How to Remove

Once `<head-dep>` publishes a release that includes `<vulnerable-package>@<patchedVersions>`:

1. Delete the override line from `pnpm-workspace.yaml`
2. Run `pnpm install`
3. Run `pnpm audit --audit-level=high` to confirm it is clean
EOF
)"
```

Capture the returned issue URL. Use it in the `# Tracking:` comment in Step 5b.

> If `security` or `maintenance` labels don't exist in the repo, omit `--label` rather than failing.

---

## Step 8 — Final report

**If upgraded:**
- Package upgraded and the fix confirmed clean
- Testing checklist (if major bump)

**If overridden:**
- Exact lines added to `pnpm-workspace.yaml`
- GitHub issue URL
- When to revisit: "Once `<head-dep>` releases with `<vulnerable-package>@<patchedVersions>`, delete the override line and run `pnpm install && pnpm audit --audit-level=high`"
Loading
Loading