-
Notifications
You must be signed in to change notification settings - Fork 5
ci: add automated vulnerability remediation workflow #182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ulziibay-kernel
wants to merge
2
commits into
main
Choose a base branch
from
security/vuln-remediation-workflow
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+285
−0
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,225 @@ | ||
| 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. The Socket CLI is available as `socket` and authenticated via SOCKET_SECURITY_API_KEY. | ||
|
|
||
| # Context | ||
|
|
||
| - Repo: ${GITHUB_REPOSITORY} | ||
| - Date: ${DATE} | ||
|
|
||
| # Goal | ||
|
|
||
| Process vulnerability alerts from a Socket.dev scan report for this repository. For each alert, either fix the vulnerability by upgrading the dependency or log the decision with a 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 `socket-report.json` in the current directory. This file contains the Socket scan report JSON. | ||
|
|
||
| If the report shows `"healthy": true` and the `alerts` map is empty, output "No vulnerability alerts. Scan is healthy." and exit. | ||
|
|
||
| The Socket report nests alerts by ecosystem, package, version, file, and location. Flatten these into a list of unique alerts. For each alert, extract: | ||
| - Alert type (e.g., `cve`, `installScripts`, `networkAccess`, `envVars`, etc.) | ||
| - Severity: `error`, `warn`, `monitor`, `ignore` | ||
| - Package name and version | ||
| - Ecosystem (npm, go, pypi, etc.) | ||
| - Description / summary if available | ||
| - CVE ID if the alert type is `cve` | ||
|
|
||
| If `socket-report.json` is missing or unparseable, fall back to running a fresh scan: | ||
|
|
||
| ``` | ||
| socket scan create --repo=<repo-name> --branch=main --report --json --fold=version > socket-report.json | ||
| ``` | ||
|
|
||
| ## Step 2: Triage alerts (Manager phase) | ||
|
|
||
| Focus only on alerts with severity `error` or `warn`. Ignore `monitor` and `ignore` level alerts. | ||
|
|
||
| For each alert, classify it into one of three categories. Process alerts in priority order: `error` first, then `warn`. | ||
|
|
||
| ### Category: DISMISS | ||
|
|
||
| Skip the alert if ANY of the following are true: | ||
|
|
||
| 1. **Non-CVE behavioral alert**: Alert types like `installScripts`, `networkAccess`, `envVars`, `shellAccess`, `filesystemAccess` are informational supply chain signals. Log them but do not attempt to fix. These are useful for awareness but not actionable via dependency bumps. | ||
|
|
||
| 2. **Development-only dependency**: If the package appears only in dev dependency sections (devDependencies in package.json, test files in go.mod). These are not deployed to production. | ||
|
|
||
| 3. **Unreachable vulnerable code (Go only)**: For Go dependencies, check if the vulnerable package's symbols are actually imported and called: | ||
| ``` | ||
| grep -r '<package-import-path>' --include='*.go' -l | ||
| ``` | ||
| If the package is not imported, or only imported in test files (`_test.go`), skip it. | ||
|
|
||
| 4. **Alert in non-production manifest**: If the manifest path points to a test, script, or example directory (e.g., `scripts/`, `e2e/`, `testing/`, `replays/`, `shared/cdp-test/`), the dependency is not part of the production deployment. | ||
|
|
||
| ### Category: FIX | ||
|
|
||
| Fix the alert if ALL of the following are true: | ||
|
|
||
| 1. The alert type is `cve` (a known vulnerability with a CVE ID) | ||
| 2. The dependency is a runtime/production dependency | ||
| 3. A newer version of the package exists that resolves the CVE | ||
| 4. The dependency is in a production manifest | ||
|
|
||
| ### Category: DEFER | ||
|
|
||
| Defer the alert if: | ||
|
|
||
| 1. It is a CVE in 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 attempt fixes for deferred alerts — they will be reported in the PR body for human review. | ||
|
|
||
| ## Step 3: 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 4: 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>@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 5: 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 6: 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 7: Format code | ||
|
|
||
| Run `bun run format` to ensure all code is properly formatted before committing. | ||
|
|
||
| ## Step 8: 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 | | ||
| |-----|---------|----------|-------------|-------------|----------| | ||
| | ... | ... | ... | ... | ... | ... | | ||
|
|
||
| ### Skipped (non-actionable) | ||
| | Alert Type | Package | Severity | Reason | | ||
| |------------|---------|----------|--------| | ||
| | ... | ... | ... | ... | | ||
|
|
||
| ### 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 there are deferred items, still post a summary as a PR comment or issue so there is a record. | ||
|
|
||
| # Constraints | ||
|
|
||
| - Process at most 10 CVE alerts per run (prioritize by severity: error > warn) | ||
| - Only act on `cve` type alerts for fixes — behavioral alerts are informational only | ||
| - 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| 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: 'go.mod' | ||
|
|
||
| - name: Install Socket CLI | ||
| run: npm install -g @socketsecurity/cli | ||
|
|
||
| - name: Run Socket scan | ||
| env: | ||
| SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_API_TOKEN }} | ||
| run: | | ||
| socket scan create --repo="${{ github.event.repository.name }}" --branch=main --default-branch --report --json > socket-report.json 2>/dev/null || echo '{"healthy":true,"alerts":{}}' > socket-report.json | ||
|
|
||
| - name: Remediate vulnerabilities | ||
| env: | ||
| CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }} | ||
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | ||
| SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_API_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 | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scan failure silently produces false healthy report
High Severity
When
socket scan createfails (bad credentials, network error, service outage, etc.), the||fallback writes{"healthy":true,"alerts":{}}tosocket-report.json, making the workflow believe no vulnerabilities exist. Combined with2>/dev/nullsuppressing all error output, scan failures are silently treated as a clean bill of health. This defeats the entire purpose of the vulnerability remediation workflow — real vulnerabilities go undetected and unremediated with no indication anything went wrong.