vulnlens 0.3.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.
- vulnlens-0.3.0/LICENSE +21 -0
- vulnlens-0.3.0/PKG-INFO +368 -0
- vulnlens-0.3.0/README.md +328 -0
- vulnlens-0.3.0/pyproject.toml +75 -0
- vulnlens-0.3.0/setup.cfg +4 -0
- vulnlens-0.3.0/tests/test_coverage.py +198 -0
- vulnlens-0.3.0/tests/test_cvss.py +27 -0
- vulnlens-0.3.0/tests/test_doctor.py +30 -0
- vulnlens-0.3.0/tests/test_engines_and_output.py +246 -0
- vulnlens-0.3.0/tests/test_eval_score.py +35 -0
- vulnlens-0.3.0/tests/test_htmlreport.py +43 -0
- vulnlens-0.3.0/tests/test_ignore.py +114 -0
- vulnlens-0.3.0/tests/test_lockfiles.py +120 -0
- vulnlens-0.3.0/tests/test_osv.py +83 -0
- vulnlens-0.3.0/tests/test_priority.py +151 -0
- vulnlens-0.3.0/tests/test_server.py +173 -0
- vulnlens-0.3.0/tests/test_source.py +84 -0
- vulnlens-0.3.0/tests/test_tiers.py +88 -0
- vulnlens-0.3.0/tests/test_triage.py +270 -0
- vulnlens-0.3.0/vulnlens/__init__.py +3 -0
- vulnlens-0.3.0/vulnlens/__main__.py +5 -0
- vulnlens-0.3.0/vulnlens/cli.py +278 -0
- vulnlens-0.3.0/vulnlens/context.py +39 -0
- vulnlens-0.3.0/vulnlens/doctor.py +102 -0
- vulnlens-0.3.0/vulnlens/engines/__init__.py +20 -0
- vulnlens-0.3.0/vulnlens/engines/base.py +67 -0
- vulnlens-0.3.0/vulnlens/engines/deps.py +59 -0
- vulnlens-0.3.0/vulnlens/engines/secrets.py +220 -0
- vulnlens-0.3.0/vulnlens/engines/semgrep.py +150 -0
- vulnlens-0.3.0/vulnlens/fixplan.py +61 -0
- vulnlens-0.3.0/vulnlens/htmlreport.py +29 -0
- vulnlens-0.3.0/vulnlens/ignore.py +140 -0
- vulnlens-0.3.0/vulnlens/intel/__init__.py +0 -0
- vulnlens-0.3.0/vulnlens/intel/cvss.py +49 -0
- vulnlens-0.3.0/vulnlens/intel/exploit.py +86 -0
- vulnlens-0.3.0/vulnlens/intel/osv.py +243 -0
- vulnlens-0.3.0/vulnlens/lockfiles.py +384 -0
- vulnlens-0.3.0/vulnlens/models.py +161 -0
- vulnlens-0.3.0/vulnlens/net.py +93 -0
- vulnlens-0.3.0/vulnlens/priority.py +241 -0
- vulnlens-0.3.0/vulnlens/report.py +155 -0
- vulnlens-0.3.0/vulnlens/sarif.py +97 -0
- vulnlens-0.3.0/vulnlens/scan.py +148 -0
- vulnlens-0.3.0/vulnlens/server.py +321 -0
- vulnlens-0.3.0/vulnlens/source.py +191 -0
- vulnlens-0.3.0/vulnlens/triage.py +381 -0
- vulnlens-0.3.0/vulnlens/web/index.html +65 -0
- vulnlens-0.3.0/vulnlens.egg-info/PKG-INFO +368 -0
- vulnlens-0.3.0/vulnlens.egg-info/SOURCES.txt +51 -0
- vulnlens-0.3.0/vulnlens.egg-info/dependency_links.txt +1 -0
- vulnlens-0.3.0/vulnlens.egg-info/entry_points.txt +2 -0
- vulnlens-0.3.0/vulnlens.egg-info/requires.txt +15 -0
- vulnlens-0.3.0/vulnlens.egg-info/top_level.txt +1 -0
vulnlens-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Harish Akshay H
|
|
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.
|
vulnlens-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vulnlens
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Security scanner that sorts code, dependency and secret findings into Act now / Review / Context, using CISA KEV, EPSS and CVSS.
|
|
5
|
+
Author: Harish Akshay H
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://harishakshay.github.io/open-source-vulnerability-scanner/
|
|
8
|
+
Project-URL: Source, https://github.com/harishakshay/open-source-vulnerability-scanner
|
|
9
|
+
Project-URL: Issues, https://github.com/harishakshay/open-source-vulnerability-scanner/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/harishakshay/open-source-vulnerability-scanner/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: security,sast,sca,cve,epss,kev,sarif,secrets,vulnerability-scanner,devsecops
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Information Technology
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Security
|
|
24
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Provides-Extra: ai
|
|
29
|
+
Requires-Dist: anthropic>=1.0; extra == "ai"
|
|
30
|
+
Provides-Extra: semgrep
|
|
31
|
+
Requires-Dist: semgrep>=1.100; extra == "semgrep"
|
|
32
|
+
Provides-Extra: all
|
|
33
|
+
Requires-Dist: anthropic>=1.0; extra == "all"
|
|
34
|
+
Requires-Dist: semgrep>=1.100; extra == "all"
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
37
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
38
|
+
Requires-Dist: twine>=5; extra == "dev"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# VulnLens
|
|
42
|
+
|
|
43
|
+
[](https://github.com/harishakshay/open-source-vulnerability-scanner/actions/workflows/ci.yml)
|
|
44
|
+

|
|
45
|
+

|
|
46
|
+
[](LICENSE)
|
|
47
|
+
|
|
48
|
+
**A security scanner that tells you which findings actually matter.**
|
|
49
|
+
|
|
50
|
+
**[Live demo →](https://harishakshay.github.io/open-source-vulnerability-scanner/)** · an interactive
|
|
51
|
+
report of a real scan; no install needed.
|
|
52
|
+
|
|
53
|
+
Security scanners report hundreds of issues, so developers stop reading them. VulnLens runs
|
|
54
|
+
three engines (code, dependencies and secrets) and then ranks every finding by **real-world
|
|
55
|
+
exploitation evidence** instead of severity alone:
|
|
56
|
+
|
|
57
|
+
- **CISA KEV**: is this CVE being exploited in the wild *right now*?
|
|
58
|
+
- **EPSS** (FIRST.org): how likely is exploitation in the next 30 days?
|
|
59
|
+
- **CVSS v3**: how bad is it if exploited? (computed from the vector, not guessed)
|
|
60
|
+
- **TruffleHog verification**: is the leaked credential actually live?
|
|
61
|
+
|
|
62
|
+
Every finding lands in one of three piles, by the strength of the evidence:
|
|
63
|
+
|
|
64
|
+
| | What's in it |
|
|
65
|
+
|---|---|
|
|
66
|
+
| **Act now** | An exploited or likely-exploited CVE (KEV, high EPSS) in a runtime dependency, a verified live secret, or a flaw AI triage confirmed |
|
|
67
|
+
| **Review** | A signal in shipped code that needs a judgment: a risky code pattern, an unverified key, a CVE nobody is known to exploit |
|
|
68
|
+
| **Context** | The same signals in tests, examples, docs, vendored code or dev dependencies |
|
|
69
|
+
|
|
70
|
+
You also get an upgrade plan that collapses dozens of CVEs into a few version bumps, a dashboard,
|
|
71
|
+
and SARIF for the GitHub Security tab.
|
|
72
|
+
|
|
73
|
+
## Quickstart (about 5 minutes)
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install "vulnlens[semgrep] @ git+https://github.com/harishakshay/open-source-vulnerability-scanner"
|
|
77
|
+
vulnlens doctor # checks the engines, prints the install step for anything missing
|
|
78
|
+
vulnlens scan . # Act now / Review / Context in the terminal
|
|
79
|
+
vulnlens scan . --html report.html # the full interactive report, one offline file
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Then add it to CI so new Act-now findings fail the build: see [GitHub Action](#github-action).
|
|
83
|
+
Python 3.10+, no runtime dependencies beyond the standard library. TruffleHog (the secrets engine)
|
|
84
|
+
is a separate binary; `vulnlens doctor` names the exact release file for your OS.
|
|
85
|
+
|
|
86
|
+

|
|
87
|
+
|
|
88
|
+
<sub>[OWASP PyGoat](https://github.com/adeyosemanputra/pygoat), an intentionally vulnerable app: 273 findings,
|
|
89
|
+
11 to act on now, and the one CVE that is being actively exploited ranked first. The **exploitability map** plots
|
|
90
|
+
every dependency CVE by severity (CVSS) against likelihood of exploitation (EPSS); the dashed lines
|
|
91
|
+
are the P1 thresholds. [Light mode](https://raw.githubusercontent.com/harishakshay/open-source-vulnerability-scanner/main/docs/images/overview.png) ·
|
|
92
|
+
[findings](https://raw.githubusercontent.com/harishakshay/open-source-vulnerability-scanner/main/docs/images/findings.png) · [upgrade plan](https://raw.githubusercontent.com/harishakshay/open-source-vulnerability-scanner/main/docs/images/upgrade-plan.png)</sub>
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
$ vulnlens scan https://github.com/adeyosemanputra/pygoat
|
|
96
|
+
scanned 2026-10-16T16:31:30+00:00 in 100.0s
|
|
97
|
+
|
|
98
|
+
semgrep ok 86 finding(s)
|
|
99
|
+
osv ok 187 finding(s) 46 packages from 4 file(s)
|
|
100
|
+
trufflehog ok 0 finding(s) verification disabled
|
|
101
|
+
|
|
102
|
+
Grade: F 273 findings
|
|
103
|
+
Act now: 11 Review: 242 Context: 20
|
|
104
|
+
P0: 1 P1: 23 P2: 84 P3: 147 P4: 18
|
|
105
|
+
1 actively exploited (CISA KEV)
|
|
106
|
+
|
|
107
|
+
Upgrade plan: 21 upgrade(s) resolve 186 dependency finding(s)
|
|
108
|
+
P0 Pillow 9.4.0 -> 12.3.0 (19 vuln(s)) KEV requirements.txt
|
|
109
|
+
P1 Django 4.2 -> 5.2.17 (51 vuln(s)) requirements.txt
|
|
110
|
+
P1 django 3.2.18 -> 5.2.17 (20 vuln(s)) dockerized_labs/sensitive_data_exposure/requirements.txt
|
|
111
|
+
P1 PyJWT 2.4.0 -> 2.15.0 (13 vuln(s), 1 unfixed) requirements.txt
|
|
112
|
+
|
|
113
|
+
Act now (11): evidence of real risk in shipped code
|
|
114
|
+
1. P0 Pillow 9.4.0: libwebp: OOB write in BuildHuffmanTable
|
|
115
|
+
requirements.txt:19 [osv: GHSA-j7hp-h8jx-5ppr]
|
|
116
|
+
- In CISA KEV: actively exploited in the wild (listed 2023-09-13)
|
|
117
|
+
- EPSS: 99.98% chance of exploitation in the next 30 days (riskier than 99.98% of CVEs)
|
|
118
|
+
- CVSS 8.8 (high)
|
|
119
|
+
- Fix: upgrade Pillow to 10.0.1 or later
|
|
120
|
+
2. P1 django 3.2.18: Django potential denial of service vulnerability in UsernameField on Win...
|
|
121
|
+
dockerized_labs/sensitive_data_exposure/requirements.txt:1 [osv: GHSA-qmf9-6jqf-j8fq]
|
|
122
|
+
- EPSS: 49.8% chance of exploitation in the next 30 days (riskier than 98.9% of CVEs)
|
|
123
|
+
- CVSS 7.5 (high)
|
|
124
|
+
- Fix: upgrade django to 3.2.23 or later
|
|
125
|
+
...
|
|
126
|
+
...and 249 more to act on or review. Use --top, --html or --format json for the full list.
|
|
127
|
+
20 more in tests, examples, docs or dev dependencies (Context).
|
|
128
|
+
|
|
129
|
+
Next
|
|
130
|
+
vulnlens scan https://github.com/adeyosemanputra/pygoat --html report.html full interactive report (one offline file)
|
|
131
|
+
add --ai-dry-run, then --ai-triage let Claude judge the Review pile (shows the cost first)
|
|
132
|
+
add it to CI: see 'GitHub Action' in the README (fails the build on Act-now findings)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
*(Real output from scanning [OWASP PyGoat](https://github.com/adeyosemanputra/pygoat), an
|
|
136
|
+
intentionally vulnerable app, on 2026-10-16: 273 findings, 11 to act on now, and the one CVE in
|
|
137
|
+
active exploitation ranked first.)*
|
|
138
|
+
|
|
139
|
+
## How prioritization works
|
|
140
|
+
|
|
141
|
+
| Priority | Meaning | Rule |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| **P0** | Fix now | CVE is in CISA KEV, or a secret is **verified live** |
|
|
144
|
+
| **P1** | Fix this sprint | EPSS ≥ 10%, CVSS ≥ 9.0, or a high-severity code flaw that is high-confidence or also flagged by a rule from another rule family |
|
|
145
|
+
| **P2** | Plan a fix | High severity, or EPSS ≥ 1% |
|
|
146
|
+
| **P3** | Low | Medium severity, or an unverified secret |
|
|
147
|
+
| **P4** | Informational | Everything else |
|
|
148
|
+
|
|
149
|
+
Findings in code that doesn't ship (tests, examples, docs, vendored copies) and development-only
|
|
150
|
+
dependencies are **capped at P3**, with the reason shown. A verified live secret stays P0 wherever it is.
|
|
151
|
+
|
|
152
|
+
Each finding includes the **reasons** for its priority, so the ranking can be checked rather
|
|
153
|
+
than taken on trust. The rules live in [`vulnlens/priority.py`](https://github.com/harishakshay/open-source-vulnerability-scanner/blob/main/vulnlens/priority.py).
|
|
154
|
+
|
|
155
|
+
### Noise reduction
|
|
156
|
+
|
|
157
|
+
- **Alias merging**: the same vulnerability often appears as `GHSA-…`, `PYSEC-…` and `CVE-…`.
|
|
158
|
+
VulnLens merges them with union-find over OSV aliases and keeps the record with the best data.
|
|
159
|
+
- **Same-line merging**: several Semgrep rules often flag one bug (three rules fired on one SQL
|
|
160
|
+
injection line in the demo app). They are reported once; agreement counts as corroboration only
|
|
161
|
+
between different rule families, since sibling rules usually encode the same pattern.
|
|
162
|
+
- **One finding per vulnerable package**: a package pinned in many lockfiles (monorepos, example
|
|
163
|
+
folders) is reported once, pointing at the production file, with the other files listed.
|
|
164
|
+
- **Implausible secrets are dropped**: an unverified match that is a code identifier, a placeholder
|
|
165
|
+
(`YOUR_API_KEY`), a local dev URL or low-randomness text is not reported (verified ones always are).
|
|
166
|
+
- **Upgrade plan**: for each vulnerable package, the smallest version that fixes everything found.
|
|
167
|
+
It picks the *nearest* fix on your version line (Django 3.2.0 → 3.2.14, not 4.0.6).
|
|
168
|
+
- **Secrets are never echoed**: only TruffleHog's redacted form ever reaches the output.
|
|
169
|
+
|
|
170
|
+
## AI triage (optional)
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
pip install "vulnlens[ai] @ git+https://github.com/harishakshay/open-source-vulnerability-scanner"
|
|
174
|
+
export ANTHROPIC_API_KEY=...
|
|
175
|
+
vulnlens scan . --ai-triage
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Static analysers match patterns. With `--ai-triage`, Claude (`claude-haiku-5-5`, the cheapest model, by default; `--ai-model` picks another) reads
|
|
179
|
+
each code finding together with about 60 lines of surrounding source and returns a structured
|
|
180
|
+
verdict: **real**, **false positive** or **needs review**, with a confidence, the reasoning, an
|
|
181
|
+
exploit scenario and a suggested fix.
|
|
182
|
+
|
|
183
|
+
- A confident *false positive* drops to P3 "Low", never lower: the AI alone cannot make a finding
|
|
184
|
+
disappear, and a finding whose code contains text aimed at AI reviewers keeps its priority.
|
|
185
|
+
- A high-confidence *real* finding moves up one priority level (never above P1).
|
|
186
|
+
- Only code findings are sent, and each snippet is **redacted first**: lines where a secret was
|
|
187
|
+
found are blanked and key-shaped strings (API keys, tokens, private keys, URL passwords) are
|
|
188
|
+
masked. Secret findings themselves are never sent; dependency findings are ranked by KEV/EPSS.
|
|
189
|
+
- Verdicts are cached per code snippet, so re-scanning unchanged code costs nothing. Before any
|
|
190
|
+
call, VulnLens prints an estimated cost; `--ai-dry-run` shows it and sends nothing, and triage is
|
|
191
|
+
skipped if the estimate is above `--ai-max-cost` (default $2). `--ai-max` caps how many findings
|
|
192
|
+
are reviewed, most urgent first. Every scan reports what triage actually cost.
|
|
193
|
+
|
|
194
|
+
How accurate is it? On [`evals/triage`](https://github.com/harishakshay/open-source-vulnerability-scanner/tree/main/evals/triage), 54 labeled findings from OWASP PyGoat and Flask, Claude Haiku 5.5 dismissed 17 findings as false positives and **all 17 were** (61% of the false positives removed), wrongly dismissed **0 of 26** real issues, and cost **$0.035** for the run. One label was corrected after the run (the model turned out to be right); with the original labels precision was 94% and one real issue was missed. Labels were checked by Claude Opus 5.5, not a human security expert, and the sample is small, so treat these as a first measurement.
|
|
195
|
+
|
|
196
|
+
## Install
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
pip install "vulnlens[semgrep] @ git+https://github.com/harishakshay/open-source-vulnerability-scanner"
|
|
200
|
+
vulnlens doctor # checks every engine and prints the exact install step for anything missing
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
TruffleHog (the secrets engine) is a separate binary: `vulnlens doctor` names the release file for
|
|
204
|
+
your OS. VulnLens recommends TruffleHog 3.99.2 and warns on anything older than 3.88, whose
|
|
205
|
+
detectors are much noisier.
|
|
206
|
+
|
|
207
|
+
The dependency engine needs no binary: it parses lockfiles itself and queries the
|
|
208
|
+
[OSV.dev](https://osv.dev) API. Missing engines are reported as `NOT INSTALLED`, and the scan
|
|
209
|
+
is marked incomplete. An incomplete scan gets grade `?` instead of a letter, so a partial scan
|
|
210
|
+
never looks clean.
|
|
211
|
+
|
|
212
|
+
**Supported lockfiles:** `requirements*.txt`, `Pipfile.lock`, `poetry.lock`, `uv.lock`,
|
|
213
|
+
`pyproject.toml` (exact `==` pins; ignored next to a lockfile), `package-lock.json`,
|
|
214
|
+
`npm-shrinkwrap.json`, `yarn.lock` (classic + berry), `pnpm-lock.yaml`, `go.mod` (with `replace`),
|
|
215
|
+
`Cargo.lock`, `composer.lock`, `Gemfile.lock`. Dependency files VulnLens can't read yet
|
|
216
|
+
(`pom.xml`, `build.gradle`, `bun.lock`, ...) and dependencies without an exact version are listed
|
|
217
|
+
under **Not checked** in every report, so a good grade is never mistaken for full coverage.
|
|
218
|
+
|
|
219
|
+
## Usage
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
vulnlens scan . # terminal report
|
|
223
|
+
vulnlens scan https://github.com/owner/repo # shallow, hook-free clone of a public repo
|
|
224
|
+
vulnlens scan . --sarif results.sarif --json report.json
|
|
225
|
+
vulnlens scan . --fail-on P1 # exit 1 if anything is P0/P1 (CI gate)
|
|
226
|
+
vulnlens scan . --engines deps # only dependencies
|
|
227
|
+
vulnlens scan . --offline --semgrep-config rules.yml # no network (registry rulesets need semgrep.dev)
|
|
228
|
+
vulnlens scan . --exclude 'tests/**' # skip paths (repeatable)
|
|
229
|
+
vulnlens scan . --baseline main.json --fail-on P1 # only fail on findings new since main.json
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Remote repository URLs are never secret-verified: those credentials aren't yours to test.
|
|
233
|
+
|
|
234
|
+
### Silencing findings
|
|
235
|
+
|
|
236
|
+
A `.vulnlensignore` file at the scan root takes path globs and `rule:`, `id:` (advisory or CVE)
|
|
237
|
+
and `fingerprint:` entries, each with an optional `# reason`. A `vulnlens: ignore` comment on the
|
|
238
|
+
flagged line or the line above silences it there (`vulnlens: ignore[rule-glob]` limits it to
|
|
239
|
+
matching rules). Every suppression is listed with its reason in the JSON report. A remote
|
|
240
|
+
repository's own ignore rules are not honored, so its author can't hide findings from you.
|
|
241
|
+
Fingerprints come from the flagged line's content, not its number, so they stay stable as code moves.
|
|
242
|
+
|
|
243
|
+
Exit codes: `0` ok, `1` findings at or above `--fail-on`, `2` scan error (bad target, or with
|
|
244
|
+
`--fail-on`, any engine that did not run, so a CI gate never passes on a partial scan).
|
|
245
|
+
|
|
246
|
+
## Web dashboard and HTML reports
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
vulnlens serve # http://localhost:8765 (keeps the last 50 scans)
|
|
250
|
+
vulnlens serve --load report.json # browse a saved `scan --json` report
|
|
251
|
+
vulnlens scan . --html report.html # standalone report: one file, opens offline
|
|
252
|
+
vulnlens html a.json b.json -o site/index.html # bundle several saved reports into one page
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The standalone HTML report is the same dashboard with the results embedded. Attach it to a ticket,
|
|
256
|
+
publish it as a CI artifact, or host it as a static page.
|
|
257
|
+
|
|
258
|
+
Start scans from the browser, watch progress live, filter findings by priority, type or text,
|
|
259
|
+
expand any finding to see *why* it got its priority, and download JSON or SARIF. It uses the
|
|
260
|
+
standard library only, with no build step or CDN, so it works offline.
|
|
261
|
+
|
|
262
|
+
The server binds to `127.0.0.1` and rejects requests whose `Host` or `Origin` is not itself, so a
|
|
263
|
+
malicious website cannot use your browser to start scans (DNS rebinding / CSRF). All scan data is
|
|
264
|
+
rendered as text, never as HTML, because findings contain content from the scanned repository.
|
|
265
|
+
|
|
266
|
+
## GitHub Action
|
|
267
|
+
|
|
268
|
+
```yaml
|
|
269
|
+
# .github/workflows/vulnlens.yml
|
|
270
|
+
name: VulnLens
|
|
271
|
+
on:
|
|
272
|
+
push:
|
|
273
|
+
branches: [main]
|
|
274
|
+
pull_request:
|
|
275
|
+
permissions:
|
|
276
|
+
contents: read
|
|
277
|
+
security-events: write # upload results to Security → Code scanning
|
|
278
|
+
jobs:
|
|
279
|
+
scan:
|
|
280
|
+
runs-on: ubuntu-latest
|
|
281
|
+
steps:
|
|
282
|
+
- uses: actions/checkout@v4
|
|
283
|
+
- uses: harishakshay/open-source-vulnerability-scanner@v0
|
|
284
|
+
id: vulnlens
|
|
285
|
+
with:
|
|
286
|
+
fail-on: act # fail only on Act-now findings; use `review` to be stricter
|
|
287
|
+
- if: always()
|
|
288
|
+
run: echo "Act now ${{ steps.vulnlens.outputs.act }}, review ${{ steps.vulnlens.outputs.review }}"
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
The Action installs VulnLens, Semgrep and TruffleHog (downloaded from its release and checked
|
|
292
|
+
against the published SHA-256), scans, writes a job summary, and uploads SARIF. Act-now findings
|
|
293
|
+
appear as errors in **Security → Code scanning**, Review as warnings, Context as notes.
|
|
294
|
+
|
|
295
|
+
| Input | Default | |
|
|
296
|
+
|---|---|---|
|
|
297
|
+
| `path` | `.` | directory to scan |
|
|
298
|
+
| `fail-on` | *(never)* | `act`, `review`, or a priority `P0`-`P4` |
|
|
299
|
+
| `engines` | `code,deps,secrets` | |
|
|
300
|
+
| `baseline` | | an earlier JSON report: only new findings count (useful on pull requests) |
|
|
301
|
+
| `upload-sarif` | `true` | needs `security-events: write` |
|
|
302
|
+
| `semgrep-config` | `p/default` | |
|
|
303
|
+
|
|
304
|
+
Outputs: `act`, `review`, `context`, `grade`, `sarif-file`. Pin `@v0` for every 0.x release or
|
|
305
|
+
`@v0.3.0` for an exact one.
|
|
306
|
+
|
|
307
|
+
## Architecture
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
vulnlens/
|
|
311
|
+
cli.py argument parsing, outputs, exit codes
|
|
312
|
+
scan.py runs engines in parallel, enriches, prioritizes
|
|
313
|
+
source.py local path or safe remote clone (https + host allow-list + public-IP check)
|
|
314
|
+
engines/
|
|
315
|
+
semgrep.py SAST, plus same-line / same-weakness merging
|
|
316
|
+
deps.py lockfiles -> OSV.dev
|
|
317
|
+
secrets.py TruffleHog with verification, redacted output
|
|
318
|
+
lockfiles.py 13 lockfile/manifest parsers, dev-dependency flags, unpinned counts
|
|
319
|
+
context.py prod / test / example / docs / vendored classification
|
|
320
|
+
ignore.py .vulnlensignore, inline ignores, --exclude, --baseline
|
|
321
|
+
doctor.py `vulnlens doctor`: engine and network checks with install steps
|
|
322
|
+
intel/
|
|
323
|
+
osv.py batch query, alias de-duplication, fixed versions
|
|
324
|
+
cvss.py CVSS v3.0/v3.1 base-score calculator (FIRST spec)
|
|
325
|
+
exploit.py EPSS + CISA KEV with on-disk cache
|
|
326
|
+
priority.py P0-P4 rules and A-F grade
|
|
327
|
+
triage.py AI triage with Claude: structured verdicts, caching, cost tracking
|
|
328
|
+
fixplan.py per-package upgrade plan
|
|
329
|
+
sarif.py SARIF 2.1.0 for GitHub code scanning
|
|
330
|
+
server.py `vulnlens serve`: local dashboard API (stdlib http.server)
|
|
331
|
+
web/index.html dashboard UI: prebuilt single file from web/dashboard (React + TypeScript + Tailwind)
|
|
332
|
+
htmlreport.py standalone HTML export (dashboard with results embedded)
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
No runtime dependencies beyond the Python standard library.
|
|
336
|
+
|
|
337
|
+
## Limitations
|
|
338
|
+
|
|
339
|
+
- Dependency findings mean a vulnerable version is *installed*, not that the vulnerable function is
|
|
340
|
+
*reachable* from your code. Reachability analysis is on the roadmap.
|
|
341
|
+
- Dependencies without an exact version (`requests>=2.0`) can't be matched to advisories. They
|
|
342
|
+
are counted and listed under "Not checked"; a lockfile gives the most accurate results.
|
|
343
|
+
- Semgrep registry rulesets (`p/default`) change over time, so the same commit can give slightly
|
|
344
|
+
different code findings later. Each report records the Semgrep version and ruleset used.
|
|
345
|
+
- On Python 3.10 (no `tomllib`), `pyproject.toml` is read for exact `"name==x"` pins only: Poetry
|
|
346
|
+
tables, dev groups and unpinned counts need Python 3.11+.
|
|
347
|
+
- CVSS v4-only advisories fall back to the advisory's severity label.
|
|
348
|
+
- Version ordering for the upgrade plan is ecosystem-agnostic, so check unusual version schemes.
|
|
349
|
+
|
|
350
|
+
## Roadmap
|
|
351
|
+
|
|
352
|
+
- [x] **AI triage** with Claude, plus a labeled evaluation set (`evals/triage`)
|
|
353
|
+
- [ ] Reachability: is the vulnerable function actually called?
|
|
354
|
+
- [x] Local web dashboard (`vulnlens serve`)
|
|
355
|
+
- [ ] PR comments
|
|
356
|
+
|
|
357
|
+
## Development
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
pip install -e ".[dev]"
|
|
361
|
+
pytest
|
|
362
|
+
python -m vulnlens scan examples/demo-app # deliberately vulnerable sample
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
See [CONTRIBUTING.md](https://github.com/harishakshay/open-source-vulnerability-scanner/blob/main/CONTRIBUTING.md) to get involved, [SECURITY.md](https://github.com/harishakshay/open-source-vulnerability-scanner/blob/main/SECURITY.md) to report a
|
|
366
|
+
vulnerability, and [CHANGELOG.md](https://github.com/harishakshay/open-source-vulnerability-scanner/blob/main/CHANGELOG.md) for release notes.
|
|
367
|
+
|
|
368
|
+
MIT License.
|