|
| 1 | +--- |
| 2 | +name: fix-security-pr |
| 3 | +description: "Fix a PR that is failing due to security or vulnerability issues — npm/pnpm/yarn/bun audit failures, CVE alerts, Dependabot merge conflicts, Snyk failures, or GitHub security advisory blocks. Use when asked to 'fix the security PR', 'resolve the vulnerability failure', or 'unblock the Dependabot PR'." |
| 4 | +--- |
| 5 | + |
| 6 | +# Fix Security PR |
| 7 | + |
| 8 | +Diagnose and remediate security/vulnerability failures in a pull request so CI passes. |
| 9 | + |
| 10 | +## Step 1 — Identify the PR and failure |
| 11 | + |
| 12 | +**Detect the PR:** |
| 13 | +- If a PR URL or number is provided, use it directly |
| 14 | +- If on a branch: `gh pr view --json number,url,headRefName,baseRefName` |
| 15 | +- If unspecified: list recent failing PRs: `gh pr list --state open --json number,title,url | grep -i -E "security|vuln|cve|dependabot|snyk|audit"` |
| 16 | + |
| 17 | +**Read the failure:** |
| 18 | + |
| 19 | +```bash |
| 20 | +gh run list --repo <owner>/<repo> --branch <branch> --limit 5 --json databaseId,conclusion,name |
| 21 | +gh run view <run-id> --log-failed 2>&1 | head -100 |
| 22 | +``` |
| 23 | + |
| 24 | +Look for these patterns in the logs: |
| 25 | + |
| 26 | +| Pattern | Source | Meaning | |
| 27 | +|---|---|---| |
| 28 | +| `npm audit` / `pnpm audit` / `yarn audit` exit non-zero | Audit step | Vulnerable dep in tree | |
| 29 | +| `High` / `Critical` severity advisory | Audit output | Specific CVE needs fixing | |
| 30 | +| `merge conflict` / `conflict` in PR | Git | Dependabot PR is stale; needs rebase | |
| 31 | +| `Snyk found` / `snyk test` failure | Snyk | Vulnerable dep detected by Snyk | |
| 32 | +| `GHSA-*` advisory ID | GitHub Advisory | Specific advisory blocking | |
| 33 | + |
| 34 | +## Step 2 — Understand the vulnerability |
| 35 | + |
| 36 | +Extract from the failure log: |
| 37 | +- **Package name** (e.g. `lodash`) |
| 38 | +- **Vulnerable version range** (e.g. `<4.17.21`) |
| 39 | +- **Safe version** (e.g. `>=4.17.21`) |
| 40 | +- **Severity** (critical / high / moderate / low) |
| 41 | +- **Advisory ID** (CVE or GHSA number) |
| 42 | +- **Whether it's a direct or transitive dependency** |
| 43 | + |
| 44 | +For Dependabot PRs, also check: |
| 45 | +```bash |
| 46 | +gh pr view <number> --json body,title,commits |
| 47 | +``` |
| 48 | + |
| 49 | +## Step 3 — Detect package manager and repo type |
| 50 | + |
| 51 | +| File | Package manager | |
| 52 | +|---|---| |
| 53 | +| `pnpm-lock.yaml` | pnpm | |
| 54 | +| `bun.lock` / `bun.lockb` | bun | |
| 55 | +| `yarn.lock` | yarn | |
| 56 | +| `package-lock.json` | npm | |
| 57 | + |
| 58 | +Check for monorepo: `pnpm-workspace.yaml`, `workspaces` in root `package.json`, or `bun.workspace.ts`. |
| 59 | + |
| 60 | +## Step 4 — Apply the fix |
| 61 | + |
| 62 | +Choose the approach based on whether the dependency is direct or transitive: |
| 63 | + |
| 64 | +### Direct dependency |
| 65 | + |
| 66 | +Update the version in `package.json` to the safe version, then reinstall: |
| 67 | + |
| 68 | +```bash |
| 69 | +# pnpm |
| 70 | +pnpm update <package>@<safe-version> |
| 71 | + |
| 72 | +# npm |
| 73 | +npm install <package>@<safe-version> |
| 74 | + |
| 75 | +# yarn |
| 76 | +yarn upgrade <package>@<safe-version> |
| 77 | + |
| 78 | +# bun |
| 79 | +bun update <package> |
| 80 | +``` |
| 81 | + |
| 82 | +### Transitive dependency (you don't control the version directly) |
| 83 | + |
| 84 | +Add an override to force the safe version across the entire tree: |
| 85 | + |
| 86 | +**pnpm** (`package.json`): |
| 87 | +```json |
| 88 | +{ |
| 89 | + "pnpm": { |
| 90 | + "overrides": { |
| 91 | + "<package>": ">=<safe-version>" |
| 92 | + } |
| 93 | + } |
| 94 | +} |
| 95 | +``` |
| 96 | + |
| 97 | +**npm** (`package.json`): |
| 98 | +```json |
| 99 | +{ |
| 100 | + "overrides": { |
| 101 | + "<package>": ">=<safe-version>" |
| 102 | + } |
| 103 | +} |
| 104 | +``` |
| 105 | + |
| 106 | +**yarn** (`package.json`): |
| 107 | +```json |
| 108 | +{ |
| 109 | + "resolutions": { |
| 110 | + "<package>": ">=<safe-version>" |
| 111 | + } |
| 112 | +} |
| 113 | +``` |
| 114 | + |
| 115 | +After adding the override, reinstall to regenerate the lockfile: |
| 116 | +```bash |
| 117 | +<pm> install |
| 118 | +``` |
| 119 | + |
| 120 | +### Dependabot PR with merge conflicts |
| 121 | + |
| 122 | +The PR branch is stale. Rebase it onto the base branch: |
| 123 | + |
| 124 | +```bash |
| 125 | +git fetch origin |
| 126 | +git checkout <dependabot-branch> |
| 127 | +git rebase origin/<base-branch> |
| 128 | +# resolve any conflicts |
| 129 | +git push --force-with-lease origin <dependabot-branch> |
| 130 | +``` |
| 131 | + |
| 132 | +If the conflict is in the lockfile, delete it and reinstall after resolving `package.json` conflicts: |
| 133 | +```bash |
| 134 | +rm <lockfile> |
| 135 | +<pm> install |
| 136 | +git add <lockfile> |
| 137 | +git rebase --continue |
| 138 | +``` |
| 139 | + |
| 140 | +### Monorepo: vulnerability in a workspace package |
| 141 | + |
| 142 | +Check which workspace contains the vulnerable dep: |
| 143 | +```bash |
| 144 | +<pm> audit --json 2>/dev/null | jq '.vulnerabilities | to_entries[] | {pkg: .key, via: .value.via}' |
| 145 | +``` |
| 146 | + |
| 147 | +If the vulnerable dep is a transitive dep of a workspace, add the override to the **root** `package.json` (not the workspace's). |
| 148 | + |
| 149 | +## Step 5 — Verify the fix locally |
| 150 | + |
| 151 | +```bash |
| 152 | +# Confirm no remaining vulnerabilities at the severity level that was failing |
| 153 | +<pm> audit --audit-level=high # or: critical / moderate |
| 154 | + |
| 155 | +# If Snyk is used |
| 156 | +npx snyk test |
| 157 | +``` |
| 158 | + |
| 159 | +If the audit still fails after fixing one package, check for additional advisories in the output and repeat Step 4 for each. |
| 160 | + |
| 161 | +## Step 6 — Commit and push |
| 162 | + |
| 163 | +```bash |
| 164 | +git add package.json <lockfile> |
| 165 | +git commit -m "fix: patch <package> vulnerability (<CVE-or-GHSA>)" |
| 166 | +git push origin <branch> |
| 167 | +``` |
| 168 | + |
| 169 | +For Dependabot PRs where you rebased with `--force-with-lease`, the push is already done in Step 4. |
| 170 | + |
| 171 | +## Step 7 — Re-trigger CI and verify |
| 172 | + |
| 173 | +```bash |
| 174 | +# Watch the new run |
| 175 | +gh run list --branch <branch> --limit 3 |
| 176 | +gh run watch <new-run-id> |
| 177 | +``` |
| 178 | + |
| 179 | +If CI passes, the PR is unblocked. If another security failure appears, return to Step 2 for the next advisory. |
| 180 | + |
| 181 | +## Edge cases |
| 182 | + |
| 183 | +**Audit level mismatch:** CI may fail on `moderate` while you're checking `high`. Check the CI command's `--audit-level` flag and match it when verifying locally. |
| 184 | + |
| 185 | +**No safe version exists yet:** If the advisory has no fix available, options are: |
| 186 | +1. Remove the package entirely if it's not truly needed |
| 187 | +2. Add the package to an audit ignore list (`.nsprc`, `auditignore`, or `--ignore` flag) and leave a comment explaining why — inform the user before doing this |
| 188 | +3. Wait for upstream to release a fix; inform the user |
| 189 | + |
| 190 | +**Private registry:** If `npm audit` / `pnpm audit` fails to reach the registry, check for `.npmrc` or `.pnpmrc` with a private registry URL. The fix process is the same; just ensure the registry is reachable in CI. |
| 191 | + |
| 192 | +**Dependabot already auto-merged:** Check if the PR is still open before starting. If it merged and CI still fails on `main`, the vulnerability is in the base branch — treat it as a direct fix to `main`, not a PR fix. |
0 commit comments