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.
Files changed (53) hide show
  1. vulnlens-0.3.0/LICENSE +21 -0
  2. vulnlens-0.3.0/PKG-INFO +368 -0
  3. vulnlens-0.3.0/README.md +328 -0
  4. vulnlens-0.3.0/pyproject.toml +75 -0
  5. vulnlens-0.3.0/setup.cfg +4 -0
  6. vulnlens-0.3.0/tests/test_coverage.py +198 -0
  7. vulnlens-0.3.0/tests/test_cvss.py +27 -0
  8. vulnlens-0.3.0/tests/test_doctor.py +30 -0
  9. vulnlens-0.3.0/tests/test_engines_and_output.py +246 -0
  10. vulnlens-0.3.0/tests/test_eval_score.py +35 -0
  11. vulnlens-0.3.0/tests/test_htmlreport.py +43 -0
  12. vulnlens-0.3.0/tests/test_ignore.py +114 -0
  13. vulnlens-0.3.0/tests/test_lockfiles.py +120 -0
  14. vulnlens-0.3.0/tests/test_osv.py +83 -0
  15. vulnlens-0.3.0/tests/test_priority.py +151 -0
  16. vulnlens-0.3.0/tests/test_server.py +173 -0
  17. vulnlens-0.3.0/tests/test_source.py +84 -0
  18. vulnlens-0.3.0/tests/test_tiers.py +88 -0
  19. vulnlens-0.3.0/tests/test_triage.py +270 -0
  20. vulnlens-0.3.0/vulnlens/__init__.py +3 -0
  21. vulnlens-0.3.0/vulnlens/__main__.py +5 -0
  22. vulnlens-0.3.0/vulnlens/cli.py +278 -0
  23. vulnlens-0.3.0/vulnlens/context.py +39 -0
  24. vulnlens-0.3.0/vulnlens/doctor.py +102 -0
  25. vulnlens-0.3.0/vulnlens/engines/__init__.py +20 -0
  26. vulnlens-0.3.0/vulnlens/engines/base.py +67 -0
  27. vulnlens-0.3.0/vulnlens/engines/deps.py +59 -0
  28. vulnlens-0.3.0/vulnlens/engines/secrets.py +220 -0
  29. vulnlens-0.3.0/vulnlens/engines/semgrep.py +150 -0
  30. vulnlens-0.3.0/vulnlens/fixplan.py +61 -0
  31. vulnlens-0.3.0/vulnlens/htmlreport.py +29 -0
  32. vulnlens-0.3.0/vulnlens/ignore.py +140 -0
  33. vulnlens-0.3.0/vulnlens/intel/__init__.py +0 -0
  34. vulnlens-0.3.0/vulnlens/intel/cvss.py +49 -0
  35. vulnlens-0.3.0/vulnlens/intel/exploit.py +86 -0
  36. vulnlens-0.3.0/vulnlens/intel/osv.py +243 -0
  37. vulnlens-0.3.0/vulnlens/lockfiles.py +384 -0
  38. vulnlens-0.3.0/vulnlens/models.py +161 -0
  39. vulnlens-0.3.0/vulnlens/net.py +93 -0
  40. vulnlens-0.3.0/vulnlens/priority.py +241 -0
  41. vulnlens-0.3.0/vulnlens/report.py +155 -0
  42. vulnlens-0.3.0/vulnlens/sarif.py +97 -0
  43. vulnlens-0.3.0/vulnlens/scan.py +148 -0
  44. vulnlens-0.3.0/vulnlens/server.py +321 -0
  45. vulnlens-0.3.0/vulnlens/source.py +191 -0
  46. vulnlens-0.3.0/vulnlens/triage.py +381 -0
  47. vulnlens-0.3.0/vulnlens/web/index.html +65 -0
  48. vulnlens-0.3.0/vulnlens.egg-info/PKG-INFO +368 -0
  49. vulnlens-0.3.0/vulnlens.egg-info/SOURCES.txt +51 -0
  50. vulnlens-0.3.0/vulnlens.egg-info/dependency_links.txt +1 -0
  51. vulnlens-0.3.0/vulnlens.egg-info/entry_points.txt +2 -0
  52. vulnlens-0.3.0/vulnlens.egg-info/requires.txt +15 -0
  53. 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.
@@ -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
+ [![CI](https://github.com/harishakshay/open-source-vulnerability-scanner/actions/workflows/ci.yml/badge.svg)](https://github.com/harishakshay/open-source-vulnerability-scanner/actions/workflows/ci.yml)
44
+ ![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)
45
+ ![Dependencies: stdlib only](https://img.shields.io/badge/runtime%20deps-stdlib%20only-success)
46
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](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
+ ![VulnLens dashboard: priority breakdown, exploitability map and fix-first list for OWASP PyGoat](https://raw.githubusercontent.com/harishakshay/open-source-vulnerability-scanner/main/docs/images/overview-dark.png)
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.