Skip to content
Open
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
3 changes: 2 additions & 1 deletion .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
"Skill(sentry-skills:claude-settings-audit)",
"Skill(sentry-skills:agents-md)",
"Skill(sentry-skills:brand-guidelines)",
"Skill(sentry-skills:doc-coauthoring)"
"Skill(sentry-skills:doc-coauthoring)",
"Skill(sentry-skills:react-next-cve)"
],
"deny": []
},
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Copy the `skills/` directory to your agent's skills location, or reference the S
| [agents-md](plugins/sentry-skills/skills/agents-md/SKILL.md) | Maintain AGENTS.md with concise agent instructions |
| [brand-guidelines](plugins/sentry-skills/skills/brand-guidelines/SKILL.md) | Write copy following Sentry brand guidelines |
| [doc-coauthoring](plugins/sentry-skills/skills/doc-coauthoring/SKILL.md) | Structured workflow for co-authoring documentation, proposals, and specs |
| [react-next-cve](plugins/sentry-skills/skills/react-next-cve/SKILL.md) | Scan GitHub org for React/Next.js CVE-affected repos |

## Available Subagents

Expand Down
3 changes: 2 additions & 1 deletion plugins/sentry-skills/skills/claude-settings-audit/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ If this is a Sentry project (or sentry-skills plugin is installed), include:
"Skill(sentry-skills:iterate-pr)",
"Skill(sentry-skills:claude-settings-audit)",
"Skill(sentry-skills:agents-md)",
"Skill(sentry-skills:brand-guidelines)"
"Skill(sentry-skills:brand-guidelines)",
"Skill(sentry-skills:react-next-cve)"
]
```

Expand Down
275 changes: 275 additions & 0 deletions plugins/sentry-skills/skills/react-next-cve/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
---
name: react-next-cve
description: Scan a GitHub org for repos affected by a React/Next.js CVE. Use when given a CVE blog post URL (Vercel changelog, GitHub advisory) to identify impacted repositories. Extracts affected versions, searches org repos, generates remediation report, and optionally creates PRs.
allowed-tools: WebFetch Bash Grep Read AskUserQuestion
---

# React/Next.js CVE Scanner

Scan a GitHub organization for repositories affected by React or Next.js CVEs.

## Usage

Provide a CVE blog post URL and optionally a GitHub organization:
- URL: Vercel changelog, GitHub advisory, or security blog post
- Org: GitHub organization to scan (defaults to getsentry)

## Process

### Step 1: Extract CVE Details

Fetch the CVE blog post and extract:
- CVE identifier
- Affected packages (next, react, react-dom, react-server-dom-*)
- Affected version ranges (could be any major version, not just React 19)
- Fixed versions for each version line

### Step 2: Search Organization

Search for affected packages in package.json files (includes monorepos with nested packages):

```bash
# Search for Next.js in dependencies - returns ALL package.json files including nested ones
gh search code '"next":' --owner {org} --filename package.json --limit 200

# Search for React (any version that might be affected)
gh search code '"react":' --owner {org} --filename package.json --limit 200

# Search for react-server-dom packages if RSC is affected
gh search code 'react-server-dom' --owner {org} --filename package.json --limit 100
```

**Important**: The search returns the full path including nested directories (e.g., `apps/web/package.json`, `packages/ui/package.json`). Track both the repo AND the path for monorepos.

### Step 3: Filter Results

For each repo/path found:

1. **Check if production dependency** (not devDependencies):
```bash
# For root package.json
gh api repos/{org}/{repo}/contents/package.json --jq '.content' | base64 -d | jq '{
next: .dependencies.next,
react: .dependencies.react,
reactDom: .dependencies["react-dom"]
}'

# For nested package.json (monorepos)
gh api repos/{org}/{repo}/contents/{path}/package.json --jq '.content' | base64 -d | jq '{
next: .dependencies.next,
react: .dependencies.react,
reactDom: .dependencies["react-dom"]
}'
```

2. **Check visibility and activity**:
```bash
gh api repos/{org}/{repo} --jq '{visibility, pushed_at, archived, homepage}'
```

3. **Detect package manager** (check at both root and package path for monorepos):
```bash
# Root level
gh api repos/{org}/{repo}/contents --jq '.[].name' | grep -E "pnpm-lock|yarn.lock|package-lock|bun.lockb"

# For monorepos, also check the package directory
gh api repos/{org}/{repo}/contents/{path} --jq '.[].name' | grep -E "pnpm-lock|yarn.lock|package-lock|bun.lockb"
```

Package manager priority: pnpm-lock.yaml → yarn.lock → package-lock.json → bun.lockb

**Monorepo handling**: If a repo has multiple vulnerable package.json files (e.g., `apps/web/package.json` and `packages/ui/package.json`), list them separately in the report and handle each path during the update process.

### Step 4: Compare Versions

Compare each repo's version against affected/fixed versions:
- Parse semver from package.json (strip ^ or ~ prefixes)
- Determine which version line the repo is on (e.g., 15.4.x, 16.0.x, 19.1.x)
- Check if version is below the fix for that version line
- Mark as VULNERABLE or SAFE

For RSC-specific CVEs, also check if the repo actually uses RSC:
- Has Next.js App Router, OR
- Has react-server-dom-* packages in dependencies

### Step 5: Generate Report

Output a markdown report with:

1. **CVE Summary** - What the vulnerability is
2. **Fixed Versions Table** - All version lines and their minimum fixed versions
3. **Vulnerable Public Repos** - Table with repo, current version, fix needed, package manager, deployment URL if any
4. **Vulnerable Internal Repos** - Separate section
5. **Safe Repos** - Repos already on fixed versions or not affected
6. **Not Affected** - Repos with package in devDependencies only

### Step 6: Create PRs (Optional)

After generating the report, ask the user:

> "Would you like me to create PRs for the vulnerable repos?"

If yes, ask which repos to update (can select all or specific ones).

For each selected repo, follow this workflow:

#### 6a. Clone and Setup

```bash
cd /tmp && rm -rf {repo} && gh repo clone {org}/{repo}
cd /tmp/{repo}
```

Note: For monorepos, stay at the repo root for package manager and git commands.

#### 6b. Check Push Access

```bash
git remote -v
```

If origin points to the org repo (not a fork), test push access:
```bash
git push --dry-run 2>&1
```

If permission denied and repo is public, create/use a fork:
```bash
gh repo fork --remote=true
# This sets origin to fork, upstream to original
```

#### 6c. Update Dependencies

Based on detected package manager and repo structure:

**Single-package repos (package.json at root):**
```bash
# pnpm
pnpm add next@{fixed} react@{fixed} react-dom@{fixed}

# yarn
yarn add next@{fixed} react@{fixed} react-dom@{fixed}

# npm
npm install next@{fixed} react@{fixed} react-dom@{fixed}

# bun
bun add next@{fixed} react@{fixed} react-dom@{fixed}
```

**Monorepos (package.json in subdirectory like apps/web):**
```bash
# pnpm workspaces - run from repo root
pnpm add next@{fixed} react@{fixed} react-dom@{fixed} --filter {package-name}
# Or: pnpm --dir {path} add next@{fixed} react@{fixed} react-dom@{fixed}

# yarn workspaces
yarn workspace {package-name} add next@{fixed} react@{fixed} react-dom@{fixed}

# npm workspaces
npm install next@{fixed} react@{fixed} react-dom@{fixed} -w {path}
```

Always update react and react-dom together to matching versions.

#### 6d. Verify Build

```bash
# Check for build script and run it
grep -q '"build"' package.json && {package_manager} build
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The verification step incorrectly checks for build and test scripts in the root package.json for monorepos, instead of the specific package's package.json.
Severity: HIGH

Suggested Fix

Modify the verification logic to check for build and test scripts in the correct package.json file. For monorepos, the check should target the package-specific package.json located at the path where dependencies were updated (e.g., {path}/package.json), not the repository root.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: plugins/sentry-skills/skills/react-next-cve/SKILL.md#L180

Potential issue: In a monorepo, the skill's verification step checks for `build` and
`test` scripts in the root `package.json`. However, dependency updates are scoped to
specific packages within subdirectories (e.g., using `pnpm --filter {package-name}`).
The verification logic at lines 178-189 fails to check the package-specific
`package.json` where the changes were made. This causes the build and test verification
steps to be silently skipped if the scripts are defined in the package but not at the
root, potentially missing build errors or test failures for the updated package.

```

If build fails, report the error and skip this repo.

#### 6e. Run Tests (if available)

```bash
# Check for test script and run it
grep -q '"test"' package.json && {package_manager} test
```

Report test results but continue even if no tests exist.

#### 6f. Create Branch and Commit

```bash
git checkout -b fix/cve-{cve-id}

# Single-package repos
git add package.json {lockfile}

# Monorepos - add both the subdirectory package.json and root lockfile
git add {path}/package.json {lockfile}
# e.g., git add apps/web/package.json pnpm-lock.yaml

git commit -m "$(cat <<'EOF'
fix: update {packages} for CVE-{cve-id}

Updates:
- {package}: {old} → {new}

Co-Authored-By: Claude <noreply@anthropic.com>
EOF
)"
```

#### 6g. Push and Create PR

```bash
git push -u origin fix/cve-{cve-id}

# If using fork, create PR to upstream
gh pr create --repo {org}/{repo} --title "fix: update packages for CVE-{cve-id}" --body "$(cat <<'EOF'
## Summary
Updates packages to address CVE-{cve-id}.

### Changes
- `{package}`: {old} → {new}

### Vulnerability Details
{brief description from CVE}

### Verification
- ✅ Build passes
- ✅ Tests pass (or N/A)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
```

#### 6h. Report Results

After processing all repos, provide a summary:

| Repository | PR | Status |
|------------|-----|--------|
| repo-1 | https://github.com/org/repo-1/pull/123 | ✅ Created |
| repo-2 | - | ❌ Build failed |
| repo-3 | https://github.com/org/repo-3/pull/456 | ✅ Created |

## Example Report Output

### CVE-2026-23864 Impact Assessment

**Vulnerability**: DoS vulnerabilities in React Server Components

#### Fixed Versions

| Package | Line | Fixed Version |
|---------|------|---------------|
| next | 15.4.x | 15.4.11+ |
| next | 15.5.x | 15.5.10+ |
| next | 16.0.x | 16.0.11+ |
| react | 19.1.x | 19.1.5+ |
| react | 19.2.x | 19.2.4+ |

#### Vulnerable Repos

| Repository | Path | Next.js | React | PM | Deployment |
|------------|------|---------|-------|-----|------------|
| sentry-docs | / | 15.1.11 → 15.1.12 | ^19.2.3 → 19.2.4 | yarn | docs.sentry.io |
| abacus | / | 16.1.1 → 16.1.5 | 19.2.3 → 19.2.4 | pnpm | - |
| super-duper-status | apps/web | ^15.4.8 → ^15.4.11 | ^19.1.0 → 19.1.5 | pnpm | - |
| ai-linear-tools | ui | 15.5.7 → 15.5.10 | 19.1.0 → 19.1.5 | pnpm | - |