Free SKILL.md scraped from GitHub. Clone the repo or copy the file directly into your Claude Code skills directory.
npx versuz@latest install bendourthe-devai-hub-catalog-skills-security-cve-reachability-analyzergit clone https://github.com/bendourthe/DevAI-Hub.gitcp DevAI-Hub/SKILL.MD ~/.claude/skills/bendourthe-devai-hub-catalog-skills-security-cve-reachability-analyzer/SKILL.md---
name: cve-reachability-analyzer
description: Determine whether a CVE in a dependency actually affects your application by tracing call paths from your code to vulnerable functions, analyzing transitive dependencies, and reducing false positives. Use when triaging dependency vulnerabilities, prioritizing patch upgrades, or validating scanner findings.
summary_l0: "Trace call paths to determine if dependency CVEs actually affect your application"
overview_l1: "This skill determines whether a CVE in a dependency actually affects your application by tracing call paths from your code to vulnerable functions, analyzing transitive dependencies, and reducing false positives. Use it when triaging dependency vulnerabilities, prioritizing patch upgrades, validating scanner findings, or reducing alert fatigue from dependency scanning tools. Key capabilities include call path tracing from application code to vulnerable dependency functions, transitive dependency analysis, false positive identification and elimination, reachability evidence generation, upgrade priority ranking based on actual impact, and scanner finding validation with code evidence. The expected output is a reachability analysis report showing which CVEs have confirmed call paths from your code and which are unreachable false positives. Trigger phrases: CVE reachability, dependency vulnerability, call path trace, transitive dependency, false positive CVE, vulnerability triage, reachable vulnerability, scanner validation."
---
# CVE Reachability Analyzer
Trace the execution path from your application code through direct and transitive dependencies to determine whether a reported CVE in a dependency is actually reachable and exploitable in your specific context. This skill dramatically reduces the noise from dependency scanners by distinguishing between vulnerabilities that exist in your dependency tree and vulnerabilities that your application can actually trigger.
## When to Use This Skill
Use this skill when you need to:
- Determine if a CVE flagged in `npm audit`, `pip-audit`, `trivy`, or similar tools actually affects your application
- Triage a large backlog of dependency vulnerability findings
- Justify deferring a dependency upgrade that would require significant refactoring
- Build evidence for risk acceptance decisions about specific CVEs
- Reduce false-positive rates from automated security scanners
- Understand how deeply a vulnerable function is embedded in your dependency chain
- Assess the impact of a newly disclosed zero-day in a popular library
- Prioritize which dependency upgrades deliver actual security value
- Validate that a dependency upgrade actually removes the vulnerable code path
**Trigger phrases**: "is this CVE reachable", "dependency vulnerability analysis", "call graph analysis", "transitive dependency check", "false positive check", "reachability analysis", "vulnerable function tracing", "dependency call path"
## What This Skill Does
### Core Capabilities
- **Dependency Tree Construction**: Build a complete graph of direct and transitive dependencies
- **Vulnerable Function Identification**: Pinpoint the exact function, method, or class that contains the vulnerability
- **Call Graph Tracing**: Trace from application entry points through dependency layers to the vulnerable function
- **Transitive Dependency Analysis**: Follow the chain through multiple levels of indirection
- **Import Analysis**: Determine which modules and functions are actually imported and used
- **False Positive Classification**: Categorize unreachable CVEs with supporting evidence
- **Reachability Reporting**: Generate clear reports with call chains and evidence
### Reachability Classification
| Classification | Definition | Action |
|---------------|------------|--------|
| Directly Reachable | Application code calls the vulnerable function | Fix immediately |
| Indirectly Reachable | A used dependency calls the vulnerable function | Fix based on severity |
| Conditionally Reachable | Reachable only under specific configuration or input | Evaluate conditions |
| Phantom Dependency | Dependency is declared but never imported | Remove or ignore |
| Unreachable | No execution path from application to vulnerable function | Document and defer |
### Analysis Methodology
```
CVE Report Input
|
v
Step 1: Identify Vulnerable Function
|
v
Step 2: Build Dependency Graph
|
v
Step 3: Analyze Imports and Usage
|
v
Step 4: Trace Call Paths
|
v
Step 5: Evaluate Conditions
|
v
Step 6: Classify and Report
```
## Instructions
### Phase 1: Identify the Vulnerable Function
Every CVE affects a specific function, method, class, or code path within the dependency. Before analyzing reachability, pinpoint exactly what is vulnerable.
**Step 1.1: Gather CVE details**
```bash
# Fetch CVE details from the NVD API
curl -s "https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2024-29041" | \
jq '{
id: .vulnerabilities[0].cve.id,
description: .vulnerabilities[0].cve.descriptions[0].value,
cvss: .vulnerabilities[0].cve.metrics.cvssMetricV31[0].cvssData.baseScore,
references: [.vulnerabilities[0].cve.references[].url]
}'
```
**Step 1.2: Identify the affected package version range**
```bash
# For npm packages, check the advisory
npm audit --json | jq '.vulnerabilities["express"] | {
name: .name,
severity: .severity,
range: .range,
fixAvailable: .fixAvailable,
via: [.via[] | if type == "object" then {title: .title, url: .url} else . end]
}'
```
```bash
# For Python packages
pip-audit --format=json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for vuln in data['dependencies']:
if vuln.get('vulns'):
print(json.dumps(vuln, indent=2))
"
```
**Step 1.3: Locate the vulnerable code in the dependency source**
```bash
# Find the patching commit to understand exactly what was vulnerable
# Check the CVE references for a GitHub advisory or commit link
# Example: inspect the fix commit to identify the vulnerable function
git log --oneline --all --grep="CVE-2024-29041" -- .
# Or look at the diff between vulnerable and fixed versions
npm pack express@4.18.2 && tar -xzf express-4.18.2.tgz
npm pack express@4.19.2 && tar -xzf express-4.19.2.tgz
diff -ru package-old/lib/ package-new/lib/
```
Document the finding:
```yaml
cve: CVE-2024-29041
package: express
vulnerable_versions: "<4.19.2"
vulnerable_function: "res.redirect()"
vulnerable_file: "lib/response.js"
vulnerability_type: "Open Redirect"
fix_description: "Added URL validation before redirect"
```
### Phase 2: Build the Dependency Graph
**Step 2.1: Generate the full dependency tree**
Node.js:
```bash
# Full dependency tree in JSON
npm ls --all --json > dependency-tree.json
# Find the specific vulnerable package in the tree
npm ls express --all
```
Python:
```bash
# Using pipdeptree for dependency visualization
pip install pipdeptree
pipdeptree --json > dependency-tree.json
# Find reverse dependencies (who depends on the vulnerable package)
pipdeptree --reverse --packages requests
```
Java (Maven):
```bash
# Full dependency tree
mvn dependency:tree -DoutputFile=dependency-tree.txt
# Find a specific dependency
mvn dependency:tree -Dincludes=com.fasterxml.jackson.core:jackson-databind
```
Go:
```bash
# Module dependency graph
go mod graph > dependency-graph.txt
# Why is this module in the dependency tree?
go mod why -m github.com/vulnerable/package
```
**Step 2.2: Identify the dependency path**
```python
def trace_dependency_path(dep_tree: dict, target_package: str) -> list[list[str]]:
"""Find all paths from root to the target package in the dependency tree."""
paths = []
def dfs(node: dict, current_path: list[str]):
name = node.get("name", "root")
current_path = current_path + [name]
if name == target_package:
paths.append(current_path)
return
for dep_name, dep_info in node.get("dependencies", {}).items():
dep_info["name"] = dep_name
dfs(dep_info, current_path)
dfs(dep_tree, [])
return paths
```
Example output:
```
Dependency paths to express@4.18.2:
1. my-app -> express@4.18.2 (direct)
2. my-app -> some-middleware@2.0.0 -> express@4.18.2 (transitive)
```
### Phase 3: Analyze Imports and Usage
**Step 3.1: Check if the vulnerable module is imported**
```bash
# JavaScript/TypeScript: Find imports of the vulnerable package
grep -rn "require.*express\|from.*express" --include="*.js" --include="*.ts" src/
# Python: Find imports of the vulnerable package
grep -rn "import requests\|from requests" --include="*.py" src/
# Java: Find imports of the vulnerable class
grep -rn "import com.fasterxml.jackson" --include="*.java" src/
# Go: Find imports of the vulnerable module
grep -rn "github.com/vulnerable/package" --include="*.go" .
```
**Step 3.2: Determine which specific functions are used**
```python
import ast
import os
def find_function_usage(codebase_path: str, module_name: str, function_name: str) -> list[dict]:
"""Find all usages of a specific function from a module in Python code."""
usages = []
for root, dirs, files in os.walk(codebase_path):
for fname in files:
if not fname.endswith(".py"):
continue
filepath = os.path.join(root, fname)
with open(filepath) as f:
try:
tree = ast.parse(f.read())
except SyntaxError:
continue
for node in ast.walk(tree):
# Check for direct function calls
if isinstance(node, ast.Call):
if isinstance(node.func, ast.Attribute):
if node.func.attr == function_name:
usages.append({
"file": filepath,
"line": node.lineno,
"type": "method_call",
})
return usages
```
**Step 3.3: Check for phantom dependencies**
A phantom dependency is one that appears in the lockfile but is never actually imported:
```bash
# List all declared dependencies
jq -r '.dependencies | keys[]' package.json > declared-deps.txt
# Find which ones are actually imported
grep -roh "require(['\"][^'\"]*['\"])" --include="*.js" src/ | \
sed "s/require(['\"]//;s/['\"])//" | sort -u > imported-deps.txt
# Find phantom dependencies
comm -23 <(sort declared-deps.txt) <(sort imported-deps.txt)
```
### Phase 4: Trace Call Paths
**Step 4.1: Build the call graph from entry points to the vulnerable function**
```python
def trace_call_path(
entry_point: str,
vulnerable_function: str,
codebase_path: str,
) -> dict:
"""Trace the call path from an application entry point to a vulnerable function."""
result = {
"entry_point": entry_point,
"target": vulnerable_function,
"reachable": False,
"call_chain": [],
"evidence": [],
}
# Step 1: Start from the entry point
# Step 2: Follow function calls through the AST
# Step 3: Cross module boundaries via import resolution
# Step 4: Enter dependency code when the call crosses into node_modules/site-packages
# Step 5: Check if the vulnerable function is in the call chain
return result
```
**Step 4.2: Use static analysis tools for automated tracing**
JavaScript/TypeScript:
```bash
# Use madge for dependency graph visualization
npx madge --circular --extensions ts,js src/
# Use ts-prune to find unused exports (helps identify dead code paths)
npx ts-prune src/
```
Python:
```bash
# Use pyan for call graph analysis
pip install pyan3
pyan3 src/**/*.py --dot --grouped > call-graph.dot
# Use vulture to find dead code (unreachable paths)
pip install vulture
vulture src/
```
Java:
```bash
# Use jdeps for dependency analysis
jdeps --class-path 'lib/*' --multi-release 17 -verbose:class target/app.jar
# Check if a specific class is referenced
jdeps --class-path 'lib/*' -include 'com.vulnerable.ClassName' target/app.jar
```
**Step 4.3: Manual tracing for complex cases**
When automated tools cannot resolve the full path, trace manually:
```markdown
## Manual Call Path Trace: CVE-2024-29041
### Entry Point: POST /api/auth/callback
1. `src/routes/auth.ts:45` - Route handler receives request
2. `src/routes/auth.ts:52` - Calls `authService.handleCallback(req)`
3. `src/services/auth.ts:89` - Calls `res.redirect(redirectUrl)`
4. `node_modules/express/lib/response.js:912` - Express `redirect()` method (VULNERABLE)
### Reachability: DIRECTLY REACHABLE
- The vulnerable function `res.redirect()` is called with a URL derived from query parameters
- No URL validation is applied between the entry point and the redirect call
```
### Phase 5: Evaluate Conditions
**Step 5.1: Identify conditional factors**
Some vulnerabilities are reachable only under specific conditions:
```yaml
conditional_factors:
configuration:
- description: "Vulnerable path only active when FEATURE_FLAG_X is enabled"
current_state: "disabled in production"
risk: "low (would become high if enabled)"
input_constraints:
- description: "Exploitation requires input > 1MB, but API has 100KB limit"
enforced_by: "nginx request_body_size"
bypassable: false
runtime_environment:
- description: "Vulnerable to SSRF only if running with network access to metadata service"
current_state: "Pod runs with restrictive NetworkPolicy"
risk: "low"
authentication:
- description: "Vulnerable endpoint requires admin role"
current_state: "Admin accounts are MFA-protected"
risk: "medium (compromised admin = exploitable)"
```
**Step 5.2: Assess conditional reachability**
```python
def assess_conditional_reachability(conditions: list[dict]) -> dict:
"""Assess whether conditional factors make a vulnerability practically unreachable."""
blocking_conditions = []
reducing_conditions = []
for condition in conditions:
if not condition["bypassable"] and condition["current_state"] == "mitigated":
blocking_conditions.append(condition)
elif condition["risk"] in ("low", "medium"):
reducing_conditions.append(condition)
if blocking_conditions:
return {
"classification": "CONDITIONALLY_UNREACHABLE",
"blocking_factors": blocking_conditions,
"recommendation": "Monitor conditions; re-assess if configuration changes",
}
elif reducing_conditions:
return {
"classification": "CONDITIONALLY_REACHABLE",
"reducing_factors": reducing_conditions,
"recommendation": "Fix based on adjusted severity",
}
else:
return {
"classification": "REACHABLE",
"recommendation": "Fix according to severity",
}
```
### Phase 6: Classify and Report
**Step 6.1: Generate the reachability report**
```markdown
# CVE Reachability Analysis Report
**Project**: my-application
**Date**: 2025-01-15
**Scanner**: npm audit, Trivy
**Total CVEs Analyzed**: 23
## Summary
| Classification | Count | Percentage |
|---------------|-------|------------|
| Directly Reachable | 3 | 13% |
| Indirectly Reachable | 4 | 17% |
| Conditionally Reachable | 2 | 9% |
| Phantom Dependency | 5 | 22% |
| Unreachable | 9 | 39% |
## Directly Reachable (Immediate Fix Required)
### CVE-2024-29041: Express Open Redirect (CVSS 6.1)
- **Package**: express@4.18.2
- **Vulnerable Function**: `res.redirect()`
- **Call Chain**: `src/routes/auth.ts:52` -> `express/lib/response.js:912`
- **Entry Point**: `POST /api/auth/callback` (public endpoint, no authentication)
- **Recommendation**: Upgrade express to >=4.19.2
### CVE-2024-XXXXX: Prototype Pollution in lodash (CVSS 7.4)
- **Package**: lodash@4.17.20
- **Vulnerable Function**: `_.merge()`
- **Call Chain**: `src/utils/config.ts:23` -> `lodash/merge.js:45`
- **Entry Point**: `POST /api/settings` (authenticated, any user)
- **Recommendation**: Upgrade lodash to >=4.17.21
## Unreachable (No Action Required)
### CVE-2024-YYYYY: ReDoS in minimatch (CVSS 5.3)
- **Package**: minimatch@3.0.4 (transitive via glob@7.2.0)
- **Vulnerable Function**: `minimatch()` pattern matching
- **Evidence**: Application never calls glob or minimatch directly. Package is a build-time dependency only, not bundled into production code.
- **Classification**: PHANTOM_DEPENDENCY
- **Recommendation**: No action. Move glob to devDependencies if possible.
```
**Step 6.2: Generate machine-readable output for CI/CD integration**
```json
{
"analysis_date": "2025-01-15T10:30:00Z",
"project": "my-application",
"findings": [
{
"cve": "CVE-2024-29041",
"package": "express",
"version": "4.18.2",
"classification": "DIRECTLY_REACHABLE",
"cvss_base": 6.1,
"call_chain": [
"src/routes/auth.ts:52",
"node_modules/express/lib/response.js:912"
],
"fix_version": "4.19.2",
"action": "UPGRADE"
},
{
"cve": "CVE-2024-YYYYY",
"package": "minimatch",
"version": "3.0.4",
"classification": "UNREACHABLE",
"cvss_base": 5.3,
"evidence": "Build-time only dependency, not in production bundle",
"action": "NONE"
}
],
"summary": {
"total": 23,
"reachable": 3,
"indirect": 4,
"conditional": 2,
"phantom": 5,
"unreachable": 9
}
}
```
**Step 6.3: CI/CD gate configuration**
```yaml
# .github/workflows/cve-reachability.yml
name: CVE Reachability Check
on:
pull_request:
paths:
- "package.json"
- "package-lock.json"
- "requirements.txt"
- "go.sum"
jobs:
reachability:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run vulnerability scanner
run: npm audit --json > audit-results.json
- name: Analyze reachability
run: |
python scripts/cve-reachability.py \
--audit-results audit-results.json \
--codebase src/ \
--output reachability-report.json
- name: Gate on reachable vulnerabilities
run: |
python scripts/reachability-gate.py \
--report reachability-report.json \
--fail-on-reachable-critical \
--fail-on-reachable-high \
--allow-unreachable
```
### Handling Special Cases
**Transitive dependencies with multiple paths**
When a vulnerable package is reachable through multiple dependency paths, analyze each path independently:
```python
def analyze_all_paths(dep_tree: dict, target_package: str) -> list[dict]:
"""Analyze reachability through all dependency paths to the target."""
paths = trace_dependency_path(dep_tree, target_package)
results = []
for path in paths:
result = {
"path": " -> ".join(path),
"depth": len(path),
"reachable": False,
}
# Check if each intermediate package actually uses the next one
for i in range(len(path) - 1):
parent = path[i]
child = path[i + 1]
if not package_uses_module(parent, child):
result["blocked_at"] = f"{parent} does not import {child}"
break
else:
result["reachable"] = True
results.append(result)
return results
```
**Runtime-only vs build-time dependencies**
```bash
# Check if a dependency is in the production bundle (JavaScript)
npx webpack --stats-json
cat stats.json | jq '.modules[] | select(.name | contains("vulnerable-pkg"))'
# If the module does not appear in the bundle, it is build-time only
```
## Best Practices
- Always identify the exact vulnerable function before analyzing reachability; "the package has a CVE" is not specific enough to determine reachability
- Use multiple analysis techniques (import analysis, call graph tracing, bundle analysis) for higher confidence
- Document the evidence for unreachable classifications so that the decision is auditable and repeatable
- Re-run reachability analysis after code changes that add new imports or dependency usages
- Distinguish between production dependencies and development/build dependencies; a CVE in a test-only dependency has different risk than one in a production dependency
- Automate reachability checks in CI/CD to catch newly reachable vulnerabilities as code evolves
- Maintain a record of analyzed CVEs and their classifications so that you do not repeat the analysis unnecessarily
- When in doubt, classify as reachable; false negatives (missing a reachable vulnerability) are more costly than false positives
- Coordinate with the security team to establish organizational thresholds for reachability-based risk acceptance
- Update the analysis when the vulnerable package publishes new information (updated advisory, proof-of-concept exploit)
## Common Pitfalls
- **Stopping at the import statement**: A package being imported does not mean the vulnerable function is called. Trace all the way to the specific function or method.
- **Ignoring dynamic dispatch**: In languages with reflection, dependency injection, or dynamic imports, static analysis may miss reachable paths. Supplement with runtime tracing when possible.
- **Confusing dependency declaration with usage**: A package listed in `package.json` or `requirements.txt` may never be imported. Check actual import statements, not just the manifest.
- **Missing transitive paths**: The vulnerable function may not be called by your code directly, but by a library you use. Trace through the full dependency chain.
- **Assuming build-time safety**: Some build tools (Webpack, Babel plugins) execute dependency code at build time. A vulnerability in a build-time dependency could still be exploited during CI/CD.
- **Overlooking dynamic loading**: Plugins, middleware, and lazy-loaded modules may not appear in static import analysis. Check for `require()` calls with variable arguments, `importlib.import_module()`, and similar dynamic loading patterns.
- **Not accounting for version ranges**: The dependency tree may resolve to a different version than you expect. Always check the locked/resolved version, not the declared range.
- **Treating all unreachable findings as permanent**: Code changes can make previously unreachable vulnerabilities reachable. Reachability analysis must be continuous, not a one-time exercise.
- **Relying solely on automated tools**: Automated call graph tools may produce incomplete results, especially for dynamically typed languages. Combine automated analysis with manual verification for critical findings.
- **Ignoring test and CI dependencies**: While test dependencies do not affect production, they execute in your CI/CD environment. A compromised test dependency could inject malicious code into your build pipeline.