Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
245 changes: 245 additions & 0 deletions .github/workflows/vuln-remediation-prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
You are a security engineer remediating dependency vulnerabilities in a GitHub repository.

The GitHub CLI is available as `gh` and authenticated via GH_TOKEN. Git is available. You have write access to repository contents and can create pull requests.

# Context

- Repo: ${GITHUB_REPOSITORY}
- Date: ${DATE}

# Goal

Process open Dependabot vulnerability alerts for this repository. For each alert, either fix the vulnerability by upgrading the dependency or dismiss the alert with a documented reason. This workflow follows the detector-manager-fixer pattern.

This workflow uses an evergreen branch and PR to prevent PRs from piling up. Each week, the same branch/PR is updated with fresh vulnerability fixes.

# Workflow

## Step 1: Load alerts

Read `dependabot-alerts.json` in the current directory. This file contains the raw Dependabot alerts JSON from the GitHub API.

If the file is empty or contains `[]`, output "No open vulnerability alerts." and exit.

Parse each alert to extract:
- `number`: the alert number (needed for dismissal API calls)
- `security_advisory.severity`: critical, high, medium, or low
- `security_advisory.summary`: short description
- `security_advisory.cve_id`: CVE identifier
- `security_advisory.vulnerabilities[].first_patched_version.identifier`: the fix version
- `dependency.package.name`: package name
- `dependency.package.ecosystem`: go, npm, or pip
- `dependency.scope`: runtime or development
- `dependency.manifest_path`: which manifest file is affected

## Step 2: Triage alerts (Manager phase)

For each alert, classify it into one of three categories. Process alerts in priority order: critical first, then high, then medium, then low.

### Category: DISMISS

Dismiss the alert if ANY of the following are true:

1. **Development-only dependency**: `scope` is `development`. These are not deployed to production.
- Dismiss reason: `not_used`
- Comment: "Development dependency not deployed to production."

2. **Unreachable vulnerable code (Go only)**: For Go dependencies, check if the vulnerable package's symbols are actually imported and called. Run:
```
grep -r '<package-import-path>' --include='*.go' -l
```
If the package is not imported, or only imported in test files (`_test.go`), dismiss it.
- Dismiss reason: `not_used`
- Comment: "Vulnerable package symbols not reachable from production code paths."

3. **Alert in non-production manifest**: If the manifest path points to a test, script, or example directory (e.g., `scripts/`, `e2e/`, `testing/`, `replays/`), the dependency is not part of the production deployment.
- Dismiss reason: `not_used`
- Comment: "Dependency only used in [test/script/example] code, not in production."

### Category: FIX

Fix the alert if ALL of the following are true:

1. The dependency is a runtime dependency (`scope` is `runtime`)
2. A `first_patched_version` exists
3. The dependency is in a production manifest (not scripts/tests/examples)

### Category: DEFER

Defer the alert if:

1. It is a runtime dependency but no patched version is available yet
2. The fix requires a major version bump that is likely to have breaking changes

Do not create any dismissal or PR for deferred alerts — they will be reported in the PR body for human review.

## Step 3: Dismiss alerts

For each alert classified as DISMISS, call:

```
gh api --method PATCH "/repos/${GITHUB_REPOSITORY}/dependabot/alerts/<NUMBER>" \
-f state=dismissed \
-f dismissed_reason="<REASON>" \
-f dismissed_comment="<COMMENT>"
```

Valid values for `dismissed_reason`: `fix_started`, `inaccurate`, `no_bandwidth`, `not_used`, `tolerable_risk`.

Track all dismissals for the final report.

## Step 4: Setup the evergreen branch

Check if the evergreen branch already exists:

```
git fetch origin security/vuln-remediation 2>/dev/null || true
```

If the branch exists, check it out and reset it to main:

```
git checkout -B security/vuln-remediation origin/main
```

If it doesn't exist, create it from main:

```
git checkout -b security/vuln-remediation
```

## Step 5: Fix vulnerabilities

For each alert classified as FIX, grouped by manifest file:

### Go dependencies (`go.mod`)

From the directory containing the `go.mod` file:

```
go get <package>@v<patched_version>
go mod tidy
```

If `go get` with the exact patched version fails, try `@latest`:

```
go get <package>@latest
go mod tidy
```

### npm dependencies (`package.json` or `package-lock.json` or `pnpm-lock.yaml`)

From the directory containing the manifest:

If `package.json` lists the dependency directly, update the version constraint and run `bun install`.

If the vulnerability is in a lockfile-only transitive dependency, run:

```
bun update <package>
```

### Python dependencies (`pyproject.toml` or `requirements.txt`)

From the directory containing the manifest:

Edit the version constraint in `pyproject.toml` or `requirements.txt`, then:

```
uv sync
```

Or if uv is not available:

```
pip install -r requirements.txt
```

## Step 6: Verify the build

For each directory containing a modified manifest file, verify the build still works:

1. Check if a Makefile exists with a `build` target — if so, run `make build`
2. Otherwise, for Go: `go build ./...`
3. For Node/Bun: `bun run build` (if a build script exists)

All builds must succeed. If a build fails due to a specific dependency upgrade:

1. Revert that dependency change: `git checkout -- <manifest-file> <lockfile>`
2. Re-run `go mod tidy` or `bun install` to restore the previous state
3. Move the alert from FIX to DEFER
4. Continue with the next dependency

## Step 7: Run tests

For each directory containing a modified manifest file:

1. Check if a Makefile exists with a `test` target — if so, run `make test`
2. Otherwise, for Go: `go test ./...`
3. For Node/Bun: `bun test` (if a test script exists)

If tests fail due to a specific dependency upgrade:

1. Analyze if the failure is related to the upgrade or a flaky test
2. If related to the upgrade, revert that dependency and move the alert to DEFER
3. If the test is flaky/unrelated, note it and proceed

## Step 8: Format code

Run `bun run format` to ensure all code is properly formatted before committing.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Prompt references bun commands but Bun is not installed

Medium Severity

The prompt instructs the AI agent to run bun install, bun update, bun run build, bun test, and bun run format, but the workflow YAML does not install Bun. The PR description says Bun setup was intentionally omitted, and the repo's other CI workflows use pnpm instead. Step 8 unconditionally tells the agent to run bun run format (not gated on npm deps), which will always fail. The npm dependency instructions in Step 5, Step 6, and Step 7 also reference bun and will fail if triggered.

Additional Locations (2)
Fix in Cursor Fix in Web


## Step 9: Create or update the evergreen PR

If any fixes were applied and builds/tests pass:

1. Commit all changes with message: `security: vulnerability remediation (${DATE})`
Include in the commit body a list of what was fixed.

2. Force push the branch: `git push -f origin security/vuln-remediation`

3. Check if a PR already exists:
```
gh pr list --head security/vuln-remediation --state open --json number
```

4. Build the PR body with this structure:

```
## Vulnerability Remediation — ${DATE}

### Fixed
| CVE | Package | Severity | Old Version | New Version | Manifest |
|-----|---------|----------|-------------|-------------|----------|
| ... | ... | ... | ... | ... | ... |

### Dismissed
| CVE | Package | Severity | Reason | Comment |
|-----|---------|----------|--------|---------|
| ... | ... | ... | ... | ... |

### Deferred (needs human review)
| CVE | Package | Severity | Why |
|-----|---------|----------|-----|
| ... | ... | ... | ... |
```

5. If a PR exists, update its body with the new report.

6. If no PR exists, create one with:
- Title: `security: vulnerability remediation`
- Body: the report above

7. Find a reviewer: Use `gh pr list --state merged --limit 20 --json author` to identify users who have recently authored merged PRs, then assign one randomly using `gh pr edit <pr-number> --add-assignee <username>`

If no fixes were applied but dismissals or deferrals were made, still post a summary as a PR comment or issue comment so there is a record.

# Constraints

- Process at most 10 alerts per run (prioritize by severity: critical > high > medium > low)
- Only dismiss alerts with documented reasons — never silently skip
- All builds AND tests must pass before the PR is created
- Use the evergreen branch `security/vuln-remediation` — always reset it to main before making changes
- Never force-push or modify `main` directly
- If no actionable alerts remain after triage, exit without creating/updating the PR
- Be conservative: when in doubt, classify as DEFER rather than DISMISS
56 changes: 56 additions & 0 deletions .github/workflows/vuln-remediation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Vulnerability Remediation

on:
schedule:
- cron: '0 3 * * 3'
workflow_dispatch:

permissions:
contents: write
pull-requests: write

jobs:
remediate:
runs-on: ubuntu-latest
steps:
- name: Generate app token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.ADMIN_APP_ID }}
private-key: ${{ secrets.ADMIN_APP_PRIVATE_KEY }}

- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}

- name: Install Cursor CLI
run: |
curl https://cursor.com/install -fsS | bash
echo "$HOME/.cursor/bin" >> $GITHUB_PATH

- name: Configure git identity
run: |
git config user.name "kernel-internal[bot]"
git config user.email "260533166+kernel-internal[bot]@users.noreply.github.com"

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: 'server/go.mod'

- name: Fetch Dependabot alerts
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
gh api "/repos/${{ github.repository }}/dependabot/alerts?state=open&per_page=100" > dependabot-alerts.json 2>/dev/null || echo "[]" > dependabot-alerts.json

- name: Remediate vulnerabilities
env:
CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
export DATE="$(date -u +%Y-%m-%d)"
envsubst '${GITHUB_REPOSITORY} ${DATE}' < .github/workflows/vuln-remediation-prompt.md | agent -p --model ${{ secrets.CURSOR_PREFERRED_MODEL }} --trust --force --output-format=text
Loading