Vulnetix Fix Intelligence Skill
SkillSecurityGet fix intelligence for a vulnerability and propose concrete remediation for the current repository
Instructions available. Your AI can read the instructions. Execution depends on the setup they require.
Account requirements not reviewed. Check the skill instructions before use; ahel provides instructions and does not run this skill.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the Vulnetix Fix Intelligence Skill skill
What this skill tells your AI
The instructions your AI receives, as published by davepoon/buildwithclaude in plugins/vulnetix/skills/fix/SKILL.md and read by ahel’s review.
This skill fetches fix intelligence for a vulnerability and proposes concrete, actionable remediation steps for the current repository.
Output & Analysis Guidelines
Primary output format: Markdown. All reports, tables, fix options, version diffs, and verification summaries MUST be presented as formatted markdown text directly — never generate scripts or programs to produce output that can be expressed as markdown.
Visual data — use Mermaid diagrams to display data visually when it aids comprehension. Mermaid renders natively in markdown and requires no external tools. Use it for:
- Dependency upgrade paths →
graph LRshowing current → target version with breaking change annotations - Fix option comparison →
quadrantChartplotting Safe Harbour confidence vs. version change magnitude - Dependency tree showing vulnerable path →
graph TD(root → parent → vulnerable dep) - Post-fix verification status →
flowchart(scan → tests → result)
Example — upgrade path:
```mermaid
graph LR
A[log4j-core 2.14.1] -->|patch| B[2.14.2]
A -->|minor| C[2.17.1 ✓ fix]
A -->|major| D[3.0.0]
style A fill:#f66,stroke:#333
style C fill:#6f6,stroke:#333
```
If uv is available, richer visualizations can be generated with Python (matplotlib, plotly) and saved to .vulnetix/:
command -v uv &>/dev/null && uv run --with matplotlib python3 -c '
import matplotlib.pyplot as plt
# ... generate chart ...
plt.savefig(".vulnetix/chart.png", dpi=150, bbox_inches="tight")
'
When Python charts are generated, display them inline and keep the Mermaid version as a text fallback.
Data processing — tooling cascade (strict order):
- jq / yq + bash builtins (preferred) —
jqfor JSON (API responses, CycloneDX SBOMs, package manager output),yqfor YAML (memory file). Pipe tohead,tail,cut,sed,grep,sort,uniq,wcfor shaping. - uv (for complex analysis or charts) — If dependency graph analysis, version comparison logic, or visualization beyond Mermaid are needed, check
uvfirst:command -v uv &>/dev/null && uv run --with pandas,matplotlib python3 -c '...' - python3 stdlib (last resort) — Only if
uvis unavailable. Usejson,csv,collections,statisticsmodules — no pip dependencies:command -v python3 &>/dev/null && python3 -c 'import json, sys; ...'
Never assume any runtime is available — always check with command -v before use. If all programmatic tools are unavailable, analyze manually with the Read tool and present results as markdown with Mermaid diagrams.
Package manager commands (npm install --dry-run, pip show, go mod tidy, cargo check, etc.) are exempt — they are executed directly as part of the fix workflow, not for data analysis.
Mandatory Reporting Requirements
Every output and report from this skill MUST include the following version and provenance information for each affected package:
Package Version Reporting
All reports MUST display:
| Field | Description | Required |
|---|---|---|
| Current Version | The version currently installed/resolved | Always |
| Version Source | How the version was determined (see below) | Always |
| Fix Target Version | The patched version to upgrade to | When available |
| Fix Source | Registry, distro patch, or source commit hash | Always |
| Safe Harbour Confidence | Confidence score 0.00–1.00 (see below) | Always |
Version Source Transparency
You MUST be transparent about how the current version was determined. Report one of:
- User-supplied — the user provided the version directly
- Manifest — read from a package manager manifest file (state which file)
- Lockfile — read from a lockfile (state which file)
- Installed — derived from the installed package on the filesystem:
- npm/node: read
node_modules/<pkg>/package.json(search parent directories too) - Python: run
pip show <pkg>or readsite-packages/<pkg>/METADATA - Go: read
go.sumor rungo list -m <pkg> - Rust: read
Cargo.lockor runcargo metadata - System binaries: run
<binary> --versionor checkPATHresolution - Ruby: run
gem list <pkg>or readGemfile.lock - Maven: read effective POM or local
.m2cache
- npm/node: read
- Context — the version was already present in conversation context
- Unknown — version could not be determined (explain why)
If the user does not supply the version and it is not in conversation context, you MUST attempt to derive it from the filesystem before reporting "Unknown". Search outside the current working directory if needed — check parent directories, global package manager directories, and gitignored directories (e.g., node_modules/, vendor/, .venv/, target/, __pycache__/).
Safe Harbour Confidence Score
Express the Safe Harbour score as a decimal between 0.00 and 1.00 where 1.00 = 100% confidence the fix resolves the vulnerability without introducing regressions or breaking changes.
Confidence tiers:
- High confidence (> 0.90): Patch-level bump in the same minor version, official registry release, well-tested fix, minimal API surface change
- Reasonable confidence (0.35–0.90): Minor version bump, distro-repackaged patch, source fix from upstream with commit hash, some API changes but backward-compatible
- Low confidence (< 0.35): Major version bump, unofficial patch, cherry-picked commit from development branch, significant API changes, no upstream release yet
What factors adjust confidence:
- Registry-published release with changelog: +0.15
- Distro-maintained patch (e.g., Debian, Ubuntu, RHEL): +0.10
- Upstream commit hash verified in release tag: +0.10
- CISA KEV listed (validated exploitation): +0.05 (urgency signal, not fix quality)
- Major version jump: −0.25
- No test suite in project to validate: −0.15
- Transitive dependency (indirect control): −0.10
- Built from source with untagged commit: −0.20
Report format for each affected package:
Package: <name>
Current Version: <version> (source: <version-source>)
Fix Target: <version> (source: <registry|distro <name> <version>|commit <hash>>)
Safe Harbour: <score> (<High|Reasonable|Low> confidence)
Vulnerability Memory File (.vulnetix/memory.yaml)
This skill maintains a .vulnetix/memory.yaml file in the repository root that tracks all vulnerability encounters, decisions, and fix outcomes across sessions. You MUST read this file at the start of every invocation and update it after every action.
Schema
# .vulnetix/memory.yaml
# Auto-maintained by Vulnetix Claude Code Plugin
# Do not remove — tracks vulnerability decisions, manifest scans, and fix history
schema_version: 1
manifests: # Tracked manifest files and SBOM scan history
package.json:
path: "package.json" # Relative path from repo root
ecosystem: npm
last_scanned: "2024-01-15T10:30:00Z" # ISO 8601 UTC
sbom_generated: true
sbom_path: ".vulnetix/scans/package.json.20240115T103000Z.cdx.json"
vuln_count: 3 # Vulnerabilities found in last scan
scan_source: hook # hook | fix | exploits | package-search
services--api--go.mod:
path: "services/api/go.mod" # Supports monorepo paths (key uses -- separator)
ecosystem: go
last_scanned: "2024-01-15T10:31:00Z"
sbom_generated: true
sbom_path: ".vulnetix/scans/services--api--go.mod.20240115T103100Z.cdx.json"
vuln_count: 0
scan_source: hook
vulnerabilities:
CVE-2021-44228: # Primary vuln ID (key)
aliases: # Other IDs for the same vuln
- GHSA-jfh8-c2jp-5v3q
package: log4j-core
ecosystem: maven
discovery:
date: "2024-01-15T10:30:00Z" # ISO 8601 UTC
source: manifest # manifest | lockfile | sbom | scan | user | hook
file: pom.xml # The manifest where it was found
sbom: .vulnetix/scans/pom.xml.cdx.json # CycloneDX v1.7 SBOM (when produced by scan/hook)
versions:
current: "2.14.1"
current_source: "lockfile: pom.xml"
fixed_in: "2.17.1"
fix_source: "registry: Maven Central"
severity: critical # critical | high | medium | low | unknown
safe_harbour: 0.82 # 0.00–1.00 confidence score
status: fixed # See VEX Status Mapping below
justification: null # See VEX Justification Mapping below
action_response: null # See VEX Action Response Mapping below
threat_model: # Populated by /vulnetix:exploits
techniques: [T1190, T1059] # MITRE ATT&CK IDs (internal only)
tactics: # Developer-friendly descriptions (shown to user)
- "Attackable from the internet"
- "Can run arbitrary commands"
attack_vector: network # network | local | adjacent | physical
attack_complexity: low # low | high
privileges_required: none # none | low | high
user_interaction: none # none | required
reachability: direct # direct | transitive | not-found | unknown
exposure: public-facing # public-facing | internal | local-only | unknown
cwss: # CWSS-derived priority (populated by /vulnetix:exploits)
score: 87.5 # 0-100 composite priority score
priority: P1 # P1 | P2 | P3 | P4
factors:
technical_impact: 100 # 0-100
exploitability: 95 # 0-100
exposure: 100 # 0-100
complexity: 90 # 0-100
repo_relevance: 70 # 0-100
pocs: # PoC sources (from /vulnetix:exploits, never executed)
- url: "https://exploit-db.com/exploits/12345"
source: exploitdb
type: poc
local_path: ".vulnetix/pocs/CVE-2021-44228/exploit_12345.py"
fetched_date: "2024-01-15T10:35:00Z"
verified: true
analysis: "RCE via JNDI lookup, network vector, no auth"
dependabot: # Populated from GitHub Dependabot via gh CLI
alert_number: 42 # Dependabot alert number on this repo
alert_state: fixed # open | dismissed | fixed | auto_dismissed
alert_url: "https://github.com/owner/repo/security/dependabot/42"
dismiss_reason: null # fix_started | inaccurate | no_bandwidth | not_used | tolerable_risk | null
dismiss_comment: null # Dismisser's comment, if any
pr_number: 187 # Associated Dependabot PR number, or null
pr_state: merged # open | closed | merged | null
pr_url: "https://github.com/owner/repo/pull/187"
pr_latest_comment: "LGTM, merging" # Last comment on the PR (for context)
last_checked: "2024-01-15T10:30:00Z"
code_scanning: # Populated from GitHub CodeQL / code scanning via gh CLI
alerts: # CodeQL alerts correlated to this vuln (matched by CWE)
- alert_number: 15
state: dismissed # open | dismissed | fixed
rule_id: "java/log4j-injection"
rule_name: "Log4j injection"
severity: critical # critical | high | medium | low | warning | note | error
dismissed_reason: null # "false positive" | "won't fix" | "used in tests" | null
dismissed_comment: null # Free-text justification (max 280 chars)
dismissed_by: "octocat" # GitHub username
file_path: "src/main/java/App.java"
start_line: 42
url: "https://github.com/owner/repo/security/code-scanning/15"
tool: CodeQL # CodeQL | semgrep | etc.
tool_version: "2.15.0"
last_checked: "2024-01-15T10:30:00Z"
secret_scanning: # Populated from GitHub secret scanning via gh CLI
alerts: # Secret scanning alerts correlated to this vuln's package/context
- alert_number: 7
state: resolved # open | resolved
secret_type: "github_personal_access_token"
secret_type_display: "GitHub Personal Access Token"
resolution: revoked # false_positive | wont_fix | revoked | used_in_tests | null
resolution_comment: "Token rotated and old one revoked"
resolved_by: "octocat"
validity: inactive # active | inactive | unknown
file_path: "config/settings.py"
url: "https://github.com/owner/repo/security/secret-scanning/7"
push_protection_bypassed: false
last_checked: "2024-01-15T10:30:00Z"
decision:
choice: fix-applied # See User Decision Values below
reason: "Upgraded to 2.17.1 via version bump"
date: "2024-01-15T11:00:00Z"
history: # Append-only event log
- date: "2024-01-15T10:30:00Z"
event: discovered
detail: "Found via /vulnetix:fix CVE-2021-44228"
- date: "2024-01-15T11:00:00Z"
event: fix-applied
detail: "Version bumped log4j-core 2.14.1 → 2.17.1 in pom.xml"
VEX Status Mapping (Internal → Developer Language)
Use VEX semantics internally but always communicate to the user in developer-friendly language. Never use raw VEX terminology with the user.
| VEX Status | Developer Language | When to use |
|---|---|---|
not_affected | Not affected — this vuln doesn't apply to your project | Package not present, code path unreachable, or already mitigated |
affected | Vulnerable — your project is exposed, action needed | Package is present at a vulnerable version |
fixed | Fixed — a fix has been applied | Version bumped, patch applied, or dependency removed |
under_investigation | Investigating — still evaluating the impact | User hasn't decided yet, or analysis is ongoing |
VEX Justification Mapping (for not_affected status)
| VEX Justification | Developer Language | Example |
|---|---|---|
component_not_present | Package not in this project | Manifest search found no match |
vulnerable_code_not_reachable | Vulnerable code path not used | App imports only safe submodules |
vulnerable_code_cannot_be_controlled_by_adversary | Not exploitable in this deployment | Internal-only service, no untrusted input |
inline_mitigations_already_exist | Already mitigated | WAF rule, input validation, or config hardening in place |
VEX Action Response Mapping (for affected status)
| VEX Action | Developer Language | When to use |
|---|---|---|
will_not_fix | Risk accepted — won't fix, documented reason | User explicitly accepts the risk |
will_fix | Fix planned — scheduled for later | User wants to fix but not right now |
update | Updating — fix in progress | Actively applying a version bump or patch |
User Decision Values
These are the decision.choice values recorded in the memory file, mapped from user feedback:
| Decision | Maps to VEX | Triggered by user saying |
|---|---|---|
fix-applied | status: fixed | "Yes, apply the fix" / fix was successfully applied |
risk-accepted | status: affected, action: will_not_fix | "We'll accept this risk" / "Won't fix" |
not-affected | status: not_affected | "This doesn't affect us" / "Not relevant" |
investigating | status: under_investigation | "Let me look into this" / "Need more info" |
deferred | status: affected, action: will_fix | "We'll fix this later" / "Not now" |
mitigated | status: not_affected, justification: inline_mitigations_already_exist | "We have a workaround" / "Already handled" |
inlined | status: fixed | Dependency was replaced with first-party code |
risk-avoided | status: not_affected, justification: component_not_present | "We removed the dependency" / "Feature disabled" / "Not deploying this" |
risk-transferred | status: not_affected, justification: vulnerable_code_cannot_be_controlled_by_adversary | "Our WAF handles it" / "Platform mitigates this" / "Handled by infrastructure" |
Dependabot Integration
When gh CLI is available, check GitHub Dependabot alerts and PRs for additional context. Dependabot state is a supplementary signal — it does not override user decisions recorded in the memory file, but it enriches context.
Checking gh CLI Availability
gh auth status 2>/dev/null
If this succeeds, the user has gh authenticated and you can query Dependabot. If it fails, skip Dependabot checks silently — do not prompt the user to authenticate.
Querying Dependabot Alerts
# Get the repo owner/name from git remote
gh api repos/{owner}/{repo}/dependabot/alerts --jq '[.[] | select(.security_advisory.cve_id == "'"$ARGUMENTS"'" or (.security_advisory.ghsa_id == "'"$ARGUMENTS"'") or (.security_advisory.identifiers[]? | select(.type == "CVE" and .value == "'"$ARGUMENTS"'") ) )] | first'
If the vuln ID is a GHSA, also match on .security_advisory.ghsa_id. If the vuln ID is a CVE, match on .security_advisory.cve_id and the identifiers array.
If no alert matches the exact vuln ID, also try aliases from the memory file entry.
Querying Dependabot PRs
# Find Dependabot PRs referencing this vulnerability or the affected package
gh pr list --author "app/dependabot" --state all --json number,title,state,url,comments --limit 50 | jq '[.[] | select(.title | test("'"$PACKAGE_NAME"'"; "i"))]'
For each matching PR, extract:
- PR number, state (open/closed/merged), URL
- The latest comment (last item in
.comments[]):gh pr view <number> --json comments --jq '.comments[-1].body'
Dependabot Alert State → VEX Mapping
Map Dependabot states to VEX status and user decision values. Always communicate to the user in developer-friendly language.
| Dependabot Alert State | Dismiss Reason | VEX Status | Decision Choice | Developer Language |
|---|---|---|---|---|
open | — | under_investigation | investigating | "Dependabot flagged this — still open, no action taken yet" |
dismissed | fix_started | affected | deferred | "Dependabot dismissed — team started a fix" |
dismissed | inaccurate | not_affected | not-affected | "Dependabot dismissed — team determined this is inaccurate" |
dismissed | no_bandwidth | affected | deferred | "Dependabot dismissed — deferred, no bandwidth" |
dismissed | not_used | not_affected | not-affected | "Dependabot dismissed — vulnerable code not used" |
dismissed | tolerable_risk | affected | risk-accepted | "Dependabot dismissed — risk accepted as tolerable" |
fixed | — | fixed | fix-applied | "Dependabot reports this as fixed" |
auto_dismissed | — | not_affected | not-affected | "Dependabot auto-dismissed — no longer applicable" |
When a Dependabot PR exists:
| PR State | VEX Interpretation | Developer Language |
|---|---|---|
open | affected + will_fix | "Dependabot PR #N is open — the team is working on this upgrade" |
merged | fixed | "Dependabot PR #N was merged — fix applied via Dependabot" |
closed (not merged) | Check latest PR comment for reason | "Dependabot PR #N was closed without merging — " |
For closed (not merged) PRs: Read the latest comment on the PR to derive the justification. Common patterns:
- "superseded by ..." / "replaced by ..." → decision:
deferred, reason: paraphrase the comment - "not needed" / "false positive" → decision:
not-affected, reason: paraphrase the comment - "will handle manually" → decision:
deferred, reason: "Team will handle manually" - "breaking changes" / "can't upgrade" → decision:
deferred, reason: paraphrase the comment - No comments or unclear → decision:
investigating, reason: "Dependabot PR closed without explanation"
When Dependabot and Memory File Disagree
If the memory file has a user decision but Dependabot shows a different state:
- User decision takes precedence — it represents a deliberate human choice
- Flag the discrepancy to the user: "Note: Dependabot shows this as , but you previously marked it as . The Dependabot state may be out of sync."
- Update the
dependabotsection in the memory file to reflect current Dependabot state regardless — it's a factual record of what GitHub shows
Code Scanning (CodeQL) Integration
When gh CLI is available, query GitHub code scanning alerts for findings that correlate with the current vulnerability. CodeQL alerts are correlated by CWE match — if the vulnerability's CWE (from VDB data) matches a CodeQL rule's tags or the rule directly references the vulnerability.
Querying Code Scanning Alerts
# List all open code scanning alerts
gh api repos/{owner}/{repo}/code-scanning/alerts?state=open --jq '[.[] | select(.rule.tags[]? | test("cwe-"; "i"))]'
# Check for alerts matching a specific CWE (extracted from vuln context in Step 2)
gh api repos/{owner}/{repo}/code-scanning/alerts --jq '[.[] | select(.rule.tags[]? | test("CWE-<NUMBER>"; "i"))]'
# Also check dismissed and fixed alerts for prior decisions
gh api repos/{owner}/{repo}/code-scanning/alerts?state=dismissed --jq '[.[] | select(.rule.tags[]? | test("CWE-<NUMBER>"; "i"))]'
gh api repos/{owner}/{repo}/code-scanning/alerts?state=fixed --jq '[.[] | select(.rule.tags[]? | test("CWE-<NUMBER>"; "i"))]'
If the repository does not have code scanning enabled, these calls return 403 or 404 — skip silently.
Checking Default Setup Status
gh api repos/{owner}/{repo}/code-scanning/default-setup --jq '{state, languages, query_suite}'
If state is not-configured, note this to the user: "CodeQL is not enabled on this repository. Consider enabling it to catch similar issues in code."
Code Scanning Alert State → VEX Mapping
| Code Scanning State | Dismissed Reason | VEX Status | Decision Choice | Developer Language |
|---|---|---|---|---|
open | — | under_investigation | investigating | "CodeQL flagged this pattern — still open" |
dismissed | false positive | not_affected | not-affected | "CodeQL alert dismissed — false positive" |
dismissed | won't fix | affected | risk-accepted | "CodeQL alert dismissed — risk accepted" |
dismissed | used in tests | not_affected | not-affected | "CodeQL alert dismissed — only in test code" |
fixed | — | fixed | fix-applied | "CodeQL reports the code pattern is fixed" |
Enriching vulnerability context with CodeQL findings:
When a CodeQL alert matches the vulnerability's CWE:
- The
most_recent_instance.locationtells you exactly which file and line the vulnerable pattern appears — include this in the fix report - The
rule.full_descriptionprovides CodeQL's analysis of the weakness — quote relevant parts - If multiple instances exist, list the affected files so the user knows everywhere the pattern occurs
- If the alert is
fixed, this is strong evidence the code-level vulnerability has been addressed (complements a dependency version bump) - Use
dismissed_commentas the justification text when surfacing prior decisions
Autofix Integration (CodeQL AI Suggestions)
If a CodeQL alert has an autofix available, check its status:
gh api repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/autofix --jq '{status}'
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 4k
- Forks
- 543
- Last commit
- Sep 2026
ahel review
K1binfo
installs-packages
Automated review, not a security audit. Ruleset v1+k2.
Advanced
- Item type
- skill
- Key
fix-davepoon- Source
- github.com/davepoon/buildwithclaude
github.com/davepoon/buildwithclaude
Related picks
Skill · thedaviddias
The pick for JavaScriptmodern-javascript-patterns
Skill · wshobson
The pick for JavaScriptsetup-ts-deep-modules
Skill · mattpocock
The pick for TypeScripttypescript-pro
Skill · jeffallan
The pick for TypeScriptpython-performance-optimization
Skill · wshobson
The pick for Pythonpython-pro
Skill · jeffallan
The pick for Python