VulnReach
July 25, 2026 ยท View on GitHub
Runtime-aware SCA โ proves which CVEs are actually reachable, not just installed.
VulnReach is now an official OWASP Project. ๐
Demo
Step 1 โ SCA surfaces 74 CVEs across dependencies

Step 2 โ VulnReach proves which ones are actually reachable

Findings โ evidence chain per CVE

Dashboard โ 42 confirmed reachable across 9 repos

Language support: Python is fully production-ready (taint, AST, route, runtime). Java and JavaScript have functional call graph analysis and are experimental. Go, C#, and PHP are on the roadmap. See ROADMAP.md for details.
VulnReach builds on standard SCA output by adding reachability context โ proving through static analysis, taint tracking, and live runtime coverage which of the detected CVEs can actually be reached in your application.
Project Status
Latest Development (shipped)
- Dependency-aware parallel runner pipeline for faster scans
- Python reachability โ production-ready (taint, AST, route, runtime layers all functional)
- Java reachability โ functional call graph with Maven/Gradle dependency parsing (experimental)
- JavaScript reachability โ functional call graph with route entry point detection (experimental)
- Scan cancellation โ
POST /scan/{id}/cancelstops in-progress scans - Stable scan response contract:
summary+ classified buckets onGET /scan/{id} - Shared scan response normalization across API and package local mode (parity)
- Secure-by-default runtime boundary:
- base compose runs without Docker socket mount
- dynamic scans require explicit opt-in via
VULNREACH_ALLOW_DOCKER_DAEMON=true - runtime profile uses restricted
docker-socket-proxy
- Deterministic fixture quality gates for Java/JavaScript/Go in CI
- Accepted as an official OWASP Project โ owasp.community/projects/vulnreach
- AI next-steps endpoint โ
POST /findings/{id}/next-stepsproduces analyst-facing remediation guidance (immediate actions, validation probes, upgrade paths, monitoring) for a deterministic finding. Lazy / on-demand: scans never call the LLM, and LLM failures degrade gracefully. The deterministic verdict is read-only. See docs/api.md.
Experimental
scan.runtime.ebpftracing mode (Linux-focused, explicit opt-in)- AI-assisted OpenAPI generation and Intelligent DAST flows
See:
How it works
Each CVE is classified through a five-layer evidence chain:
1. SCA (Trivy) โ is the package installed and vulnerable?
2. Taint analysis (tainter) โ does user input flow to the vulnerable sink?
3. AST analysis โ is the vulnerable function in your call graph?
4. Route exposure โ is the call path reachable from an HTTP endpoint?
5. Runtime coverage โ was the vulnerable code actually executed?
The result is a prioritised finding list with four tiers:
| Tier | Meaning |
|---|---|
DYNAMICALLY_REACHABLE | Runtime coverage confirmed execution โ fix immediately |
STATICALLY_REACHABLE | Code path proven via AST/taint โ high priority |
UNCERTAIN | Weak signal only โ investigate |
NOT_REACHABLE | No evidence โ suppress from alert queue |
Quick Start
With Docker Compose (recommended)
Security notice โ before starting, copy
.env.exampleto.env.localand replace everyCHANGE_MEvalue with a strong random secret.
Do not expose VulnReach on a public network without setting real credentials and configuringCORS_ORIGINS.
git clone https://github.com/ihrishikesh0896/vulnreach.git
cd vulnreach
# 1. Create your local config
cp .env.example .env.local
# 2. Fill in every CHANGE_ME โ generate secrets with: openssl rand -hex 32
$EDITOR .env.local
# 3. Start the stack
docker compose up --build
# Optional: enable dynamic runtime scans (Docker daemon access via restricted socket proxy)
# docker compose -f docker-compose.yml -f docker-compose.runtime.yml up --build
Run a scan
Auth options:
- Short-lived JWT:
POST /login - Long-lived API token (API key): create in UI
Settings -> API Keys, then use it asAuthorization: Bearer <API_KEY>
# Get a token (replace with the credentials you set in .env.local)
TOKEN=$(curl -s -X POST http://localhost:8000/login \
-H "Content-Type: application/json" \
-d '{"username":"<your-admin-user>","password":"<your-admin-password>"}' | jq -r .access_token)
# Start scan from a GitHub repo
curl -X POST http://localhost:8000/scan \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"repo_url":"https://github.com/yourorg/yourapp"}'
# Poll for results
curl http://localhost:8000/scan/<scan_id> \
-H "Authorization: Bearer $TOKEN" | jq .summary
Features
- Reachability-filtered SCA โ classifies each CVE by evidence strength, not just CVSS score
- Runtime confirmation โ Docker-based coverage collection via
coverage.py - Taint tracking โ traces user input โ vulnerable sinks (SQL, subprocess, YAML, pickle)
- LLM-steered DAST โ Claude/OpenAI/Ollama generates and validates exploit payloads (optional)
- CI/CD gates โ
policy.block_iffails builds on confirmed critical findings - JWT auth โ multi-user, role-based access (admin / analyst)
- API tokens (API keys) โ long-lived machine auth for curl/CI (
Authorization: Bearer <API_KEY>) - PDF export โ
GET /scan/{id}/export/pdf - No vendor lock-in โ LLM features default to
provider: none; Ollama supported for offline use
In Practice
Scan target: multi-tier-dvpa โ an intentionally vulnerable Python/Django application with 72 raw CVEs across 11 packages.
| Layer | Result |
|---|---|
| Raw CVEs (Trivy) | 72 |
| Classified findings (VulnReach) | 90 |
| DYNAMICALLY_REACHABLE โ fix now | 49 |
| STATICALLY_REACHABLE โ fix this sprint | 23 |
| UNCERTAIN โ investigate | 18 |
| NOT_REACHABLE โ suppress | 0 |
| CI pipeline gate | BLOCKED |
46% of findings were moved out of the undifferentiated "fix everything" queue into a prioritised action list. In a typical production service (where many transitive dependencies are never called), this figure rises to 70โ90%.
Full methodology, evidence chain detail, and package-level breakdown: docs/benchmark.md
Documentation
Usage
- USAGE_PACKAGE.md โ package/CLI installation, dependencies, startup, usage
- USAGE_UI.md โ UI/server installation, dependencies, startup, usage
Operators / Deployers
- docs/deployment.md โ Docker Compose setup, env vars, production notes
- docs/configuration.md โ full
scan.ymlconfig reference - docs/api.md โ REST endpoints and schemas
Architecture / Security
- docs/architecture.md โ pipeline design and execution model
- docs/threat-model.md โ trust boundaries, STRIDE, abuse cases
- docs/incubator-readiness.md โ OSS/OWASP readiness status
- docs/DAST.md โ DAST concepts and flow
Contributors
- ROADMAP.md โ planned features, known limitations, language support status
- docs/development.md โ internals, agents, storage, extension points
- OWASP.md โ OWASP project notes
- SECURITY.md โ vulnerability disclosure and key rotation
- CONTRIBUTING.md โ contribution process
- CHANGELOG.md โ release history
Requirements
- Python 3.11+
- PostgreSQL 13+
- Docker + Docker Compose v2 (for dynamic analysis)
trivyon PATH (install)jq(used in quick start examples โ install)
Optional (all skip gracefully if absent):
semgrepโpip install semgreptainterโpip install tainter(taint-flow analysis; see development guide)
License
Apache 2.0 โ see LICENSE.
