| name | fix-vulnerabilities |
|---|---|
| description | Scan GitHub repos for Dependabot security alerts and auto-fix vulnerable Python dependencies. Use this skill whenever the user mentions vulnerability scanning, Dependabot alerts, dependency security, fixing CVEs, patching packages, keeping repos healthy, or cleaning up security warnings on GitHub. Also trigger when the user asks to check security across their repos, update insecure packages, or fix GitHub security alerts. Works on the current repo or all repos under a GitHub user/org. |
Scan GitHub repositories for open Dependabot security alerts, update the vulnerable Python packages in dependency files, commit, and push — all automatically.
-
Download this file and place it at:
~/.claude/skills/fix-vulnerabilities/SKILL.mdQuick setup:
mkdir -p ~/.claude/skills/fix-vulnerabilities curl -o ~/.claude/skills/fix-vulnerabilities/SKILL.md \ https://gist.githubusercontent.com/alexlib/dc8bafa8224887633c300e282491eb61/raw/SKILL.md
-
Restart Claude Code. The skill will appear in the available skills list automatically.
Single repo — cd into any git repo and type:
/fix-vulnerabilities
Or just ask naturally: "check this repo for vulnerabilities and fix them"
Multi-repo — scan all repos under a GitHub user or org:
scan all repos under myusername for vulnerabilities and fix them
Or casually: "my github repos have dependabot warnings, clean them up"
ghCLI authenticated (gh auth statusshould succeed)- Git configured with push access to target repos
- The repo must have Dependabot alerts enabled on GitHub
When invoked inside a git repo with no arguments, fix vulnerabilities in the current repo.
When the user provides a GitHub username or org name, scan all their public repos (or repos accessible to the authenticated user) and fix each one.
Single-repo: Use gh repo view --json nameWithOwner to get the current repo.
Multi-repo: Use gh repo list <owner> --limit 200 --json nameWithOwner,name --no-archived to get all non-archived repos. For each repo, check if it has open Dependabot alerts before cloning.
Fetch ALL alerts (not just open ones) because GitHub sometimes marks alerts as "fixed" even when the dependency file hasn't actually been updated. This catches stale fixes.
gh api repos/{owner}/{repo}/dependabot/alerts \
--jq '.[] | {
number: .number,
state: .state,
package: .security_vulnerability.package.name,
ecosystem: .security_vulnerability.package.ecosystem,
severity: .security_advisory.severity,
summary: .security_advisory.summary,
vulnerable_range: .security_vulnerability.vulnerable_version_range,
patched: .security_vulnerability.first_patched_version.identifier
}'If the API returns a 403 or empty result, skip the repo — Dependabot may not be enabled.
Filter to ecosystem == "pip" since we handle Python dependencies only. Count alerts for other ecosystems (npm, rubygems, actions, etc.) and report them in the summary so the user knows what needs manual attention, but don't attempt those fixes.
Look for these files in the repo root (and common subdirectories):
requirements.txt(and variants likerequirements-dev.txt,requirements/*.txt)pyproject.toml(look in[project.dependencies]and[project.optional-dependencies])setup.cfg(look in[options] install_requires)setup.py(look ininstall_requireslist)
This is the critical step. For EVERY alert (open AND "fixed"), cross-check the version currently pinned in the dependency file against the patched version. GitHub's "fixed" state can be wrong — the alert may show as fixed while the dependency file still pins a vulnerable version.
For each alert that has a patched version:
- Find the package in the dependency file(s)
- Parse the currently pinned version
- Compare it to the
patchedversion — if the pinned version is LOWER than the patched version, it needs updating regardless of the alert's state - Update the pinned version:
package==1.2.3→package==<patched>package>=1.2.3→package>=<patched>package~=1.2.3→package~=<patched>- For unpinned deps (
packagewith no version), add>=<patched>
Handle case-insensitive package name matching — pip treats _ and - as equivalent, so jupyter-server matches jupyter_server.
If a package appears in the alerts but NOT in any dependency file, it's likely a transitive dependency. Note it for the user but don't create new entries — the fix needs to come from updating the direct dependency that pulls it in.
Classify each alert into one of these categories for the summary:
- Auto-fixable: package is in a dep file and has a patched version
- No patch available: Dependabot hasn't identified a safe version yet
- Transitive: vulnerable package isn't a direct dependency
- Non-Python: npm, actions, rubygems, etc. — reported but not fixed
Before committing, show the user what changed:
| Package | Old Version | New Version | Alerts Fixed | Severity |
|----------------|-------------|-------------|--------------|-------------|
| urllib3 | 2.6.3 | 2.7.0 | 2 | 2 High |
| jupyter-server | 2.15.0 | 2.18.0 | 4 | 3 High, 1 Med |
Also list any alerts that couldn't be auto-fixed (no patched version, transitive dep, non-Python ecosystem) so the user knows what needs manual attention.
Create a single commit with a descriptive message:
Bump <packages> to fix security vulnerabilities
- package1 X.Y.Z → A.B.C (summary of vuln)
- package2 X.Y.Z → A.B.C (summary of vuln)
Fixes N open Dependabot alerts.
Push to the current branch (typically main). Include a Co-Authored-By trailer.
For multi-repo mode, for each repo with alerts:
- Clone into a temp directory:
gh repo clone {owner}/{repo} /tmp/fix-vuln-{repo} - Run the single-repo workflow inside that clone
- Push changes
- Remove the temp clone
- Move to the next repo
At the end, show a summary of all repos processed with ecosystem breakdown:
| Repo | Total | pip | npm | actions | Auto-Fixed | Remaining | Status |
|----------------|-------|-----|-----|---------|-----------|------------------|----------|
| owner/repo-a | 5 | 5 | 0 | 0 | 5 | 0 | Clean |
| owner/repo-b | 15 | 10 | 5 | 0 | 8 | 7 (2 no patch, 5 npm) | Partial |
| owner/repo-c | 0 | 0 | 0 | 0 | 0 | 0 | Already clean |
Also include a priority ordering of repos by risk (critical alerts first, then high count).
- If
gh auth statusfails, tell the user to rungh auth loginfirst - If a repo has no dependency files, skip it and note it in the summary
- If git push fails (permissions, branch protection), report the error and continue to the next repo
- If the Dependabot API returns rate-limit errors, wait and retry with exponential backoff
- Fix non-Python vulnerabilities (it reports them but doesn't patch JS/Ruby/Go/etc.)
- Run tests after updating — the user should verify compatibility
- Create pull requests — it pushes directly (if the user wants PRs instead, they should say so)
- Handle major version bumps that might break APIs — it patches to the minimum safe version reported by Dependabot