vulntriage 0.12.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vulntriage-0.12.0/.env.example +21 -0
- vulntriage-0.12.0/.gitignore +53 -0
- vulntriage-0.12.0/.python-version +1 -0
- vulntriage-0.12.0/LICENSE +21 -0
- vulntriage-0.12.0/PKG-INFO +292 -0
- vulntriage-0.12.0/README.md +261 -0
- vulntriage-0.12.0/pyproject.toml +56 -0
- vulntriage-0.12.0/src/vulntriage/__init__.py +1 -0
- vulntriage-0.12.0/src/vulntriage/audit.py +85 -0
- vulntriage-0.12.0/src/vulntriage/cache.py +39 -0
- vulntriage-0.12.0/src/vulntriage/cli.py +447 -0
- vulntriage-0.12.0/src/vulntriage/context.py +134 -0
- vulntriage-0.12.0/src/vulntriage/epss.py +59 -0
- vulntriage-0.12.0/src/vulntriage/exceptions.py +18 -0
- vulntriage-0.12.0/src/vulntriage/ignore.py +17 -0
- vulntriage-0.12.0/src/vulntriage/importscan.py +83 -0
- vulntriage-0.12.0/src/vulntriage/kev.py +27 -0
- vulntriage-0.12.0/src/vulntriage/models.py +55 -0
- vulntriage-0.12.0/src/vulntriage/nvd.py +90 -0
- vulntriage-0.12.0/src/vulntriage/output.py +150 -0
- vulntriage-0.12.0/src/vulntriage/pypi.py +91 -0
- vulntriage-0.12.0/src/vulntriage/ranker.py +495 -0
- vulntriage-0.12.0/src/vulntriage/sarif.py +148 -0
- vulntriage-0.12.0/tests/__init__.py +0 -0
- vulntriage-0.12.0/tests/conftest.py +20 -0
- vulntriage-0.12.0/tests/fixtures/pip_audit_output.json +29 -0
- vulntriage-0.12.0/tests/fixtures/pyproject_sample.toml +8 -0
- vulntriage-0.12.0/tests/fixtures/requirements.txt +5 -0
- vulntriage-0.12.0/tests/test_audit.py +319 -0
- vulntriage-0.12.0/tests/test_cache.py +82 -0
- vulntriage-0.12.0/tests/test_cli.py +865 -0
- vulntriage-0.12.0/tests/test_context.py +166 -0
- vulntriage-0.12.0/tests/test_epss.py +94 -0
- vulntriage-0.12.0/tests/test_ignore.py +35 -0
- vulntriage-0.12.0/tests/test_importscan.py +70 -0
- vulntriage-0.12.0/tests/test_kev.py +81 -0
- vulntriage-0.12.0/tests/test_models.py +106 -0
- vulntriage-0.12.0/tests/test_nvd.py +197 -0
- vulntriage-0.12.0/tests/test_output.py +201 -0
- vulntriage-0.12.0/tests/test_pypi.py +92 -0
- vulntriage-0.12.0/tests/test_ranker.py +1109 -0
- vulntriage-0.12.0/tests/test_sarif.py +199 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Provider selection (default: anthropic)
|
|
2
|
+
# Valid values: anthropic, openai, gemini, ollama
|
|
3
|
+
VULNTRIAGE_PROVIDER=anthropic
|
|
4
|
+
|
|
5
|
+
# Anthropic — default provider
|
|
6
|
+
# Get a key at https://console.anthropic.com
|
|
7
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
8
|
+
|
|
9
|
+
# OpenAI (optional, requires: pip install 'vulntriage[openai]')
|
|
10
|
+
# Get a key at https://platform.openai.com/api-keys
|
|
11
|
+
OPENAI_API_KEY=sk-...
|
|
12
|
+
|
|
13
|
+
# Google Gemini (optional, requires: pip install 'vulntriage[gemini]')
|
|
14
|
+
# Free tier available at https://aistudio.google.com/apikey
|
|
15
|
+
GOOGLE_API_KEY=AIza...
|
|
16
|
+
|
|
17
|
+
# Ollama — local/offline (optional, requires: pip install 'vulntriage[ollama]')
|
|
18
|
+
# Model to use (default: llama3.2)
|
|
19
|
+
OLLAMA_MODEL=llama3.2
|
|
20
|
+
# Ollama server URL (default: http://localhost:11434)
|
|
21
|
+
OLLAMA_HOST=http://localhost:11434
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
*.egg
|
|
6
|
+
.eggs/
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
.venv/
|
|
10
|
+
venv/
|
|
11
|
+
.Python
|
|
12
|
+
pip-wheel-metadata/
|
|
13
|
+
|
|
14
|
+
# Coverage
|
|
15
|
+
.coverage
|
|
16
|
+
htmlcov/
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
|
|
19
|
+
# Environment
|
|
20
|
+
.env
|
|
21
|
+
*.env
|
|
22
|
+
|
|
23
|
+
# Editor
|
|
24
|
+
.idea/
|
|
25
|
+
.vscode/
|
|
26
|
+
*.swp
|
|
27
|
+
*.swo
|
|
28
|
+
|
|
29
|
+
# OS
|
|
30
|
+
.DS_Store
|
|
31
|
+
|
|
32
|
+
# Internal / project-management artifacts — do not ship
|
|
33
|
+
CLAUDE.md
|
|
34
|
+
CLAUDE.md.bak
|
|
35
|
+
project.md
|
|
36
|
+
docs/
|
|
37
|
+
|
|
38
|
+
# Claude Code / ruflo / swarm session artifacts
|
|
39
|
+
.claude/
|
|
40
|
+
.claude.bak/
|
|
41
|
+
.claude-flow/
|
|
42
|
+
.swarm/
|
|
43
|
+
.mcp.json
|
|
44
|
+
ruvector.db
|
|
45
|
+
*.db-shm
|
|
46
|
+
*.db-wal
|
|
47
|
+
|
|
48
|
+
# Tool caches
|
|
49
|
+
.ruff_cache/
|
|
50
|
+
.mypy_cache/
|
|
51
|
+
|
|
52
|
+
# GBrain
|
|
53
|
+
.gbrain-source
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.11.13
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 vulntriage contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vulntriage
|
|
3
|
+
Version: 0.12.0
|
|
4
|
+
Summary: Rank pip-audit CVEs by real exploitability using Claude AI
|
|
5
|
+
Author: Nivish
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: ai,cve,pip-audit,security,vulnerability
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Topic :: Security
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Requires-Dist: anthropic>=0.26.0
|
|
17
|
+
Requires-Dist: rich>=13.7.0
|
|
18
|
+
Requires-Dist: typer>=0.12.0
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: black>=24.0.0; extra == 'dev'
|
|
21
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
23
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
24
|
+
Provides-Extra: gemini
|
|
25
|
+
Requires-Dist: google-genai>=1.0.0; extra == 'gemini'
|
|
26
|
+
Provides-Extra: ollama
|
|
27
|
+
Requires-Dist: ollama>=0.4.0; extra == 'ollama'
|
|
28
|
+
Provides-Extra: openai
|
|
29
|
+
Requires-Dist: openai>=1.0.0; extra == 'openai'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# vulntriage
|
|
33
|
+
|
|
34
|
+
Rank `pip-audit` CVEs by real exploitability using an LLM.
|
|
35
|
+
|
|
36
|
+
`pip-audit` reports every vulnerability your dependencies carry — but a CVSS 9.8 in a transitive dependency you never call is not the same risk as a CVSS 5.0 in your HTTP client that handles every request. `vulntriage` feeds your CVE list, your actual dependency stack, and authoritative threat intelligence (NVD CVSS, CISA KEV, EPSS) to an LLM, which ranks them by **real reachability** rather than raw severity score.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Requirements
|
|
41
|
+
|
|
42
|
+
- Python 3.11+
|
|
43
|
+
- `pip-audit` installed and on `PATH` (`pip install pip-audit`)
|
|
44
|
+
- An Anthropic API key — or credentials for OpenAI, Gemini, or a local Ollama instance
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install vulntriage
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
export ANTHROPIC_API_KEY="sk-ant-..."
|
|
60
|
+
|
|
61
|
+
# Scan the current directory (needs requirements.txt or pyproject.toml)
|
|
62
|
+
vulntriage scan
|
|
63
|
+
|
|
64
|
+
# Scan a specific project
|
|
65
|
+
vulntriage scan --project-root /path/to/project
|
|
66
|
+
|
|
67
|
+
# Gate CI on CRITICAL only (not the default HIGH)
|
|
68
|
+
vulntriage scan --fail-on CRITICAL
|
|
69
|
+
|
|
70
|
+
# Save a timestamped JSON report
|
|
71
|
+
vulntriage scan --output-dir ./reports
|
|
72
|
+
|
|
73
|
+
# Skip all network fetches (use cached threat intel only)
|
|
74
|
+
vulntriage scan --offline
|
|
75
|
+
|
|
76
|
+
# Output machine-readable JSON (status messages go to stderr)
|
|
77
|
+
vulntriage scan --format json
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Flags
|
|
81
|
+
|
|
82
|
+
| Flag | Default | Description |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| `--project-root / -p` | `.` | Directory containing `requirements.txt` or `pyproject.toml` |
|
|
85
|
+
| `--fail-on` | `HIGH` | Exit 1 if any CVE at or above this severity: `CRITICAL / HIGH / MEDIUM / LOW / INFO` |
|
|
86
|
+
| `--format / -f` | `table` | Output format: `table` (Rich) or `json` (pipe-safe) |
|
|
87
|
+
| `--output-dir` | — | Save a timestamped JSON report to this directory after each scan |
|
|
88
|
+
| `--offline` | — | Skip all external API calls; use cached threat intel only |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
### Output
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
╭─────────────────────────────────────────────────────────────╮
|
|
96
|
+
│ vulntriage — CVE Priority Report │
|
|
97
|
+
├───┬──────────────────────┬──────────────────┬──────┬──────┬───────┬───────────────────────────────────┬──────────────────────────────┬──────────────────────────────────╮
|
|
98
|
+
│ # │ CVE / PYSEC ID │ Package │ Risk │ CVSS │ EPSS │ Reasoning │ Breaking Changes │ Fix │
|
|
99
|
+
├───┼──────────────────────┼──────────────────┼──────┼──────┼───────┼───────────────────────────────────┼──────────────────────────────┼──────────────────────────────────┤
|
|
100
|
+
│ 1 │ CVE-2024-35195 │ requests 2.31.0 │ HIGH │ 9.1 │ 12.3% │ SSRF via proxied requests; direct │ verify=True is now the │ pip install requests>=2.32.0 │
|
|
101
|
+
│ │ ★ CISA KEV │ → 2.32.0 │ │ │ │ dep called at every API boundary │ default—audit any verify=False │ │
|
|
102
|
+
├───┼──────────────────────┼──────────────────┼──────┼──────┼───────┼───────────────────────────────────┼──────────────────────────────┼──────────────────────────────────┤
|
|
103
|
+
│ 2 │ CVE-2022-40897 │ setuptools 65.5.0│ LOW │ 7.5 │ 0.1% │ ReDoS in package metadata parser; │ No breaking changes in patch │ pip install setuptools>=65.5.1 │
|
|
104
|
+
│ │ │ → 65.5.1 │ │ │ │ not reachable at app runtime │ release │ │
|
|
105
|
+
╰───┴──────────────────────┴──────────────────┴──────┴──────┴───────┴───────────────────────────────────┴──────────────────────────────┴──────────────────────────────────╯
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`★ CISA KEV` — CISA has confirmed this CVE is actively exploited in the wild.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Threat Intelligence
|
|
113
|
+
|
|
114
|
+
Before the LLM call, `vulntriage` fetches authoritative threat data from three public feeds and injects it into the prompt:
|
|
115
|
+
|
|
116
|
+
| Feed | What it provides | Rate limit |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| [NVD REST API v2](https://nvd.nist.gov/developers/vulnerabilities) | CVSS v3.1/v3.0/v2 base score per CVE | 5 req/30s free; 50 req/30s with `NVD_API_KEY` |
|
|
119
|
+
| [CISA KEV catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog) | Whether each CVE is actively exploited in the wild | Single request, no key needed |
|
|
120
|
+
| [FIRST EPSS API](https://www.first.org/epss) | Exploitation probability percentage | Batch request, no key needed |
|
|
121
|
+
|
|
122
|
+
All three are cached at `~/.cache/vulntriage/` with a 24-hour TTL. The first scan pays the network cost; subsequent scans are instant.
|
|
123
|
+
|
|
124
|
+
**NVD scores are authoritative.** The NVD CVSS value always overrides whatever score the LLM returns.
|
|
125
|
+
|
|
126
|
+
### Speeding up NVD fetches
|
|
127
|
+
|
|
128
|
+
Without an NVD API key, the tool pauses 6.1 seconds between CVE lookups to stay under the public rate limit. With a key, the pause drops to 0.7 seconds — significant for projects with many CVEs.
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Get a free key at https://nvd.nist.gov/developers/request-an-api-key
|
|
132
|
+
export NVD_API_KEY="your-key-here"
|
|
133
|
+
vulntriage scan
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Offline mode
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
# Skip all three feeds; use whatever is in the local cache
|
|
140
|
+
vulntriage scan --offline
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Use `--offline` in air-gapped environments or when deterministic scan time matters. The scan proceeds without threat intel if the cache is empty — CVSS, KEV, and EPSS fields are simply absent from the prompt.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Suppressing CVEs
|
|
148
|
+
|
|
149
|
+
Create a `.vulnignore` file in your project root to suppress CVEs your team has reviewed and accepted:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
# Accepted — only reachable in development scripts, not at runtime
|
|
153
|
+
CVE-2022-40897
|
|
154
|
+
|
|
155
|
+
# Reviewed and accepted
|
|
156
|
+
CVE-2023-32681 Not reachable via our API surface — verified 2024-01-15
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Lines starting with `#` are comments. Text after the CVE ID is treated as a reason and ignored by the tool. Suppressed CVEs are excluded before the LLM call and do not count toward the exit code.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Provider Selection
|
|
164
|
+
|
|
165
|
+
`vulntriage` supports four LLM backends. Set `VULNTRIAGE_PROVIDER` to switch:
|
|
166
|
+
|
|
167
|
+
| Provider | Env var | Install extra | Default model |
|
|
168
|
+
|---|---|---|---|
|
|
169
|
+
| `anthropic` (default) | `ANTHROPIC_API_KEY` | — | `claude-sonnet-4-6` |
|
|
170
|
+
| `openai` | `OPENAI_API_KEY` | `pip install 'vulntriage[openai]'` | `gpt-4o-mini` |
|
|
171
|
+
| `gemini` | `GOOGLE_API_KEY` | `pip install 'vulntriage[gemini]'` | `gemini-2.0-flash` |
|
|
172
|
+
| `ollama` | — | `pip install 'vulntriage[ollama]'` | `llama3.2` |
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Use Gemini (free tier at aistudio.google.com/apikey)
|
|
176
|
+
export VULNTRIAGE_PROVIDER=gemini
|
|
177
|
+
export GOOGLE_API_KEY="AIza..."
|
|
178
|
+
vulntriage scan
|
|
179
|
+
|
|
180
|
+
# Use Ollama — fully local, no dependency data leaves your machine
|
|
181
|
+
export VULNTRIAGE_PROVIDER=ollama
|
|
182
|
+
vulntriage scan
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Privacy note:** All providers except Ollama send your dependency names to an external API. If your dependency list is sensitive, use `VULNTRIAGE_PROVIDER=ollama`.
|
|
186
|
+
|
|
187
|
+
### Ollama quickstart
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
brew install ollama
|
|
191
|
+
ollama pull llama3.2
|
|
192
|
+
pip install 'vulntriage[ollama]'
|
|
193
|
+
VULNTRIAGE_PROVIDER=ollama vulntriage scan
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
By default Ollama connects to `http://localhost:11434`. Override with `OLLAMA_HOST`. Use a different model with `OLLAMA_MODEL` (e.g. `OLLAMA_MODEL=mistral`). If the Ollama server is not running, `vulntriage` will start it automatically.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## CI Integration
|
|
201
|
+
|
|
202
|
+
`vulntriage scan` exits **1** if any CVE is ranked at or above `--fail-on` (default: `HIGH`), and **0** otherwise.
|
|
203
|
+
|
|
204
|
+
### GitHub Actions
|
|
205
|
+
|
|
206
|
+
```yaml
|
|
207
|
+
- name: Audit CVEs
|
|
208
|
+
env:
|
|
209
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
210
|
+
NVD_API_KEY: ${{ secrets.NVD_API_KEY }} # optional but speeds up NVD lookups
|
|
211
|
+
run: |
|
|
212
|
+
pip install pip-audit vulntriage
|
|
213
|
+
vulntriage scan --fail-on HIGH
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Gate on CRITICAL only:
|
|
217
|
+
|
|
218
|
+
```yaml
|
|
219
|
+
vulntriage scan --fail-on CRITICAL
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Save a report as a CI artifact:
|
|
223
|
+
|
|
224
|
+
```yaml
|
|
225
|
+
vulntriage scan --output-dir ./reports --format json
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### GitLab CI
|
|
229
|
+
|
|
230
|
+
```yaml
|
|
231
|
+
audit:
|
|
232
|
+
script:
|
|
233
|
+
- pip install pip-audit vulntriage
|
|
234
|
+
- vulntriage scan --fail-on HIGH
|
|
235
|
+
variables:
|
|
236
|
+
ANTHROPIC_API_KEY: $ANTHROPIC_API_KEY
|
|
237
|
+
NVD_API_KEY: $NVD_API_KEY
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## How it works
|
|
243
|
+
|
|
244
|
+
1. Runs `pip-audit --format json` as a subprocess
|
|
245
|
+
2. Reads `requirements.txt` or `pyproject.toml` to understand your actual stack
|
|
246
|
+
3. Loads `.vulnignore` and removes suppressed CVEs
|
|
247
|
+
4. Fetches threat intelligence from NVD, CISA KEV, and EPSS (skipped with `--offline`; all three cached 24h at `~/.cache/vulntriage/`)
|
|
248
|
+
5. Sends the enriched CVE list and stack context to the configured LLM
|
|
249
|
+
6. NVD CVSS overrides any score the LLM returns
|
|
250
|
+
7. Renders a ranked Rich table or JSON output
|
|
251
|
+
8. Exits 1 if any CVE is at or above `--fail-on` severity
|
|
252
|
+
|
|
253
|
+
LLM reasoning example:
|
|
254
|
+
|
|
255
|
+
> *"requests is a direct dependency called at every API boundary — HIGH (SSRF via proxied requests). setuptools is not reachable at application runtime — LOW despite CVSS 7.5."*
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Cost
|
|
260
|
+
|
|
261
|
+
Each scan makes one LLM API call. At `claude-sonnet-4-6` pricing (~$3/M input, $15/M output), a typical scan with 5–10 CVEs costs roughly **$0.004–0.01**. The static system prompt is cached across repeat scans (Anthropic 5-min TTL), cutting cost on subsequent runs by ~80%.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Scope
|
|
266
|
+
|
|
267
|
+
- pip only (no npm, cargo, etc.)
|
|
268
|
+
- Context from `requirements.txt` / `pyproject.toml` — no static call-graph analysis
|
|
269
|
+
- Threat intel cached at `~/.cache/vulntriage/` with a 24-hour TTL
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Development
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
git clone https://github.com/Nivish-21/Vuln
|
|
277
|
+
cd Vuln
|
|
278
|
+
python -m venv .venv && source .venv/bin/activate
|
|
279
|
+
pip install -e ".[dev]"
|
|
280
|
+
|
|
281
|
+
# Run tests
|
|
282
|
+
pytest
|
|
283
|
+
|
|
284
|
+
# Lint + format
|
|
285
|
+
black . && ruff check .
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## License
|
|
291
|
+
|
|
292
|
+
MIT
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# vulntriage
|
|
2
|
+
|
|
3
|
+
Rank `pip-audit` CVEs by real exploitability using an LLM.
|
|
4
|
+
|
|
5
|
+
`pip-audit` reports every vulnerability your dependencies carry — but a CVSS 9.8 in a transitive dependency you never call is not the same risk as a CVSS 5.0 in your HTTP client that handles every request. `vulntriage` feeds your CVE list, your actual dependency stack, and authoritative threat intelligence (NVD CVSS, CISA KEV, EPSS) to an LLM, which ranks them by **real reachability** rather than raw severity score.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- Python 3.11+
|
|
12
|
+
- `pip-audit` installed and on `PATH` (`pip install pip-audit`)
|
|
13
|
+
- An Anthropic API key — or credentials for OpenAI, Gemini, or a local Ollama instance
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install vulntriage
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
export ANTHROPIC_API_KEY="sk-ant-..."
|
|
29
|
+
|
|
30
|
+
# Scan the current directory (needs requirements.txt or pyproject.toml)
|
|
31
|
+
vulntriage scan
|
|
32
|
+
|
|
33
|
+
# Scan a specific project
|
|
34
|
+
vulntriage scan --project-root /path/to/project
|
|
35
|
+
|
|
36
|
+
# Gate CI on CRITICAL only (not the default HIGH)
|
|
37
|
+
vulntriage scan --fail-on CRITICAL
|
|
38
|
+
|
|
39
|
+
# Save a timestamped JSON report
|
|
40
|
+
vulntriage scan --output-dir ./reports
|
|
41
|
+
|
|
42
|
+
# Skip all network fetches (use cached threat intel only)
|
|
43
|
+
vulntriage scan --offline
|
|
44
|
+
|
|
45
|
+
# Output machine-readable JSON (status messages go to stderr)
|
|
46
|
+
vulntriage scan --format json
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Flags
|
|
50
|
+
|
|
51
|
+
| Flag | Default | Description |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| `--project-root / -p` | `.` | Directory containing `requirements.txt` or `pyproject.toml` |
|
|
54
|
+
| `--fail-on` | `HIGH` | Exit 1 if any CVE at or above this severity: `CRITICAL / HIGH / MEDIUM / LOW / INFO` |
|
|
55
|
+
| `--format / -f` | `table` | Output format: `table` (Rich) or `json` (pipe-safe) |
|
|
56
|
+
| `--output-dir` | — | Save a timestamped JSON report to this directory after each scan |
|
|
57
|
+
| `--offline` | — | Skip all external API calls; use cached threat intel only |
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### Output
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
╭─────────────────────────────────────────────────────────────╮
|
|
65
|
+
│ vulntriage — CVE Priority Report │
|
|
66
|
+
├───┬──────────────────────┬──────────────────┬──────┬──────┬───────┬───────────────────────────────────┬──────────────────────────────┬──────────────────────────────────╮
|
|
67
|
+
│ # │ CVE / PYSEC ID │ Package │ Risk │ CVSS │ EPSS │ Reasoning │ Breaking Changes │ Fix │
|
|
68
|
+
├───┼──────────────────────┼──────────────────┼──────┼──────┼───────┼───────────────────────────────────┼──────────────────────────────┼──────────────────────────────────┤
|
|
69
|
+
│ 1 │ CVE-2024-35195 │ requests 2.31.0 │ HIGH │ 9.1 │ 12.3% │ SSRF via proxied requests; direct │ verify=True is now the │ pip install requests>=2.32.0 │
|
|
70
|
+
│ │ ★ CISA KEV │ → 2.32.0 │ │ │ │ dep called at every API boundary │ default—audit any verify=False │ │
|
|
71
|
+
├───┼──────────────────────┼──────────────────┼──────┼──────┼───────┼───────────────────────────────────┼──────────────────────────────┼──────────────────────────────────┤
|
|
72
|
+
│ 2 │ CVE-2022-40897 │ setuptools 65.5.0│ LOW │ 7.5 │ 0.1% │ ReDoS in package metadata parser; │ No breaking changes in patch │ pip install setuptools>=65.5.1 │
|
|
73
|
+
│ │ │ → 65.5.1 │ │ │ │ not reachable at app runtime │ release │ │
|
|
74
|
+
╰───┴──────────────────────┴──────────────────┴──────┴──────┴───────┴───────────────────────────────────┴──────────────────────────────┴──────────────────────────────────╯
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`★ CISA KEV` — CISA has confirmed this CVE is actively exploited in the wild.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Threat Intelligence
|
|
82
|
+
|
|
83
|
+
Before the LLM call, `vulntriage` fetches authoritative threat data from three public feeds and injects it into the prompt:
|
|
84
|
+
|
|
85
|
+
| Feed | What it provides | Rate limit |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| [NVD REST API v2](https://nvd.nist.gov/developers/vulnerabilities) | CVSS v3.1/v3.0/v2 base score per CVE | 5 req/30s free; 50 req/30s with `NVD_API_KEY` |
|
|
88
|
+
| [CISA KEV catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog) | Whether each CVE is actively exploited in the wild | Single request, no key needed |
|
|
89
|
+
| [FIRST EPSS API](https://www.first.org/epss) | Exploitation probability percentage | Batch request, no key needed |
|
|
90
|
+
|
|
91
|
+
All three are cached at `~/.cache/vulntriage/` with a 24-hour TTL. The first scan pays the network cost; subsequent scans are instant.
|
|
92
|
+
|
|
93
|
+
**NVD scores are authoritative.** The NVD CVSS value always overrides whatever score the LLM returns.
|
|
94
|
+
|
|
95
|
+
### Speeding up NVD fetches
|
|
96
|
+
|
|
97
|
+
Without an NVD API key, the tool pauses 6.1 seconds between CVE lookups to stay under the public rate limit. With a key, the pause drops to 0.7 seconds — significant for projects with many CVEs.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Get a free key at https://nvd.nist.gov/developers/request-an-api-key
|
|
101
|
+
export NVD_API_KEY="your-key-here"
|
|
102
|
+
vulntriage scan
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Offline mode
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Skip all three feeds; use whatever is in the local cache
|
|
109
|
+
vulntriage scan --offline
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Use `--offline` in air-gapped environments or when deterministic scan time matters. The scan proceeds without threat intel if the cache is empty — CVSS, KEV, and EPSS fields are simply absent from the prompt.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Suppressing CVEs
|
|
117
|
+
|
|
118
|
+
Create a `.vulnignore` file in your project root to suppress CVEs your team has reviewed and accepted:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
# Accepted — only reachable in development scripts, not at runtime
|
|
122
|
+
CVE-2022-40897
|
|
123
|
+
|
|
124
|
+
# Reviewed and accepted
|
|
125
|
+
CVE-2023-32681 Not reachable via our API surface — verified 2024-01-15
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Lines starting with `#` are comments. Text after the CVE ID is treated as a reason and ignored by the tool. Suppressed CVEs are excluded before the LLM call and do not count toward the exit code.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Provider Selection
|
|
133
|
+
|
|
134
|
+
`vulntriage` supports four LLM backends. Set `VULNTRIAGE_PROVIDER` to switch:
|
|
135
|
+
|
|
136
|
+
| Provider | Env var | Install extra | Default model |
|
|
137
|
+
|---|---|---|---|
|
|
138
|
+
| `anthropic` (default) | `ANTHROPIC_API_KEY` | — | `claude-sonnet-4-6` |
|
|
139
|
+
| `openai` | `OPENAI_API_KEY` | `pip install 'vulntriage[openai]'` | `gpt-4o-mini` |
|
|
140
|
+
| `gemini` | `GOOGLE_API_KEY` | `pip install 'vulntriage[gemini]'` | `gemini-2.0-flash` |
|
|
141
|
+
| `ollama` | — | `pip install 'vulntriage[ollama]'` | `llama3.2` |
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Use Gemini (free tier at aistudio.google.com/apikey)
|
|
145
|
+
export VULNTRIAGE_PROVIDER=gemini
|
|
146
|
+
export GOOGLE_API_KEY="AIza..."
|
|
147
|
+
vulntriage scan
|
|
148
|
+
|
|
149
|
+
# Use Ollama — fully local, no dependency data leaves your machine
|
|
150
|
+
export VULNTRIAGE_PROVIDER=ollama
|
|
151
|
+
vulntriage scan
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Privacy note:** All providers except Ollama send your dependency names to an external API. If your dependency list is sensitive, use `VULNTRIAGE_PROVIDER=ollama`.
|
|
155
|
+
|
|
156
|
+
### Ollama quickstart
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
brew install ollama
|
|
160
|
+
ollama pull llama3.2
|
|
161
|
+
pip install 'vulntriage[ollama]'
|
|
162
|
+
VULNTRIAGE_PROVIDER=ollama vulntriage scan
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
By default Ollama connects to `http://localhost:11434`. Override with `OLLAMA_HOST`. Use a different model with `OLLAMA_MODEL` (e.g. `OLLAMA_MODEL=mistral`). If the Ollama server is not running, `vulntriage` will start it automatically.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## CI Integration
|
|
170
|
+
|
|
171
|
+
`vulntriage scan` exits **1** if any CVE is ranked at or above `--fail-on` (default: `HIGH`), and **0** otherwise.
|
|
172
|
+
|
|
173
|
+
### GitHub Actions
|
|
174
|
+
|
|
175
|
+
```yaml
|
|
176
|
+
- name: Audit CVEs
|
|
177
|
+
env:
|
|
178
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
179
|
+
NVD_API_KEY: ${{ secrets.NVD_API_KEY }} # optional but speeds up NVD lookups
|
|
180
|
+
run: |
|
|
181
|
+
pip install pip-audit vulntriage
|
|
182
|
+
vulntriage scan --fail-on HIGH
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Gate on CRITICAL only:
|
|
186
|
+
|
|
187
|
+
```yaml
|
|
188
|
+
vulntriage scan --fail-on CRITICAL
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Save a report as a CI artifact:
|
|
192
|
+
|
|
193
|
+
```yaml
|
|
194
|
+
vulntriage scan --output-dir ./reports --format json
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### GitLab CI
|
|
198
|
+
|
|
199
|
+
```yaml
|
|
200
|
+
audit:
|
|
201
|
+
script:
|
|
202
|
+
- pip install pip-audit vulntriage
|
|
203
|
+
- vulntriage scan --fail-on HIGH
|
|
204
|
+
variables:
|
|
205
|
+
ANTHROPIC_API_KEY: $ANTHROPIC_API_KEY
|
|
206
|
+
NVD_API_KEY: $NVD_API_KEY
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## How it works
|
|
212
|
+
|
|
213
|
+
1. Runs `pip-audit --format json` as a subprocess
|
|
214
|
+
2. Reads `requirements.txt` or `pyproject.toml` to understand your actual stack
|
|
215
|
+
3. Loads `.vulnignore` and removes suppressed CVEs
|
|
216
|
+
4. Fetches threat intelligence from NVD, CISA KEV, and EPSS (skipped with `--offline`; all three cached 24h at `~/.cache/vulntriage/`)
|
|
217
|
+
5. Sends the enriched CVE list and stack context to the configured LLM
|
|
218
|
+
6. NVD CVSS overrides any score the LLM returns
|
|
219
|
+
7. Renders a ranked Rich table or JSON output
|
|
220
|
+
8. Exits 1 if any CVE is at or above `--fail-on` severity
|
|
221
|
+
|
|
222
|
+
LLM reasoning example:
|
|
223
|
+
|
|
224
|
+
> *"requests is a direct dependency called at every API boundary — HIGH (SSRF via proxied requests). setuptools is not reachable at application runtime — LOW despite CVSS 7.5."*
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Cost
|
|
229
|
+
|
|
230
|
+
Each scan makes one LLM API call. At `claude-sonnet-4-6` pricing (~$3/M input, $15/M output), a typical scan with 5–10 CVEs costs roughly **$0.004–0.01**. The static system prompt is cached across repeat scans (Anthropic 5-min TTL), cutting cost on subsequent runs by ~80%.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Scope
|
|
235
|
+
|
|
236
|
+
- pip only (no npm, cargo, etc.)
|
|
237
|
+
- Context from `requirements.txt` / `pyproject.toml` — no static call-graph analysis
|
|
238
|
+
- Threat intel cached at `~/.cache/vulntriage/` with a 24-hour TTL
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Development
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
git clone https://github.com/Nivish-21/Vuln
|
|
246
|
+
cd Vuln
|
|
247
|
+
python -m venv .venv && source .venv/bin/activate
|
|
248
|
+
pip install -e ".[dev]"
|
|
249
|
+
|
|
250
|
+
# Run tests
|
|
251
|
+
pytest
|
|
252
|
+
|
|
253
|
+
# Lint + format
|
|
254
|
+
black . && ruff check .
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## License
|
|
260
|
+
|
|
261
|
+
MIT
|