Exhaustive LLM-driven whitebox vulnerability research pipeline that finds vulnerabilities in any source code. Built on a code graph + LLM hybrid architecture that enumerates and validates all source-to-sink paths.
Scales from small scripts to enterprise codebases: Linux Kernel, VSCode, Microsoft Agent Framework, GitHub Desktop.
What It Does
Finds every valid vulnerability in source code through exhaustive enumeration:
- Parses every source file across 16 languages
- Builds a complete call graph with cross-file imports
- Tags every untrusted input source and dangerous operation sink
- Enumerates every source-to-sink path through the call graph
- Tracks taint through each function on each path (intra- + inter-procedural)
- Cross-references sanitizers against sink categories via normalized taxonomy
- Validates ambiguous paths with LLM for genuine exploitability
- Auto-classifies clear-cut cases deterministically (sanitizer match → blocked)
- Sweeps every file NOT covered by paths with a blind-spot code review (Project Black methodology)
- Injects relevant product CVEs into every LLM prompt for pattern matching
- Analyzes memory corruption for C/C++/Rust codebases
- Synthesizes multi-step exploit chains via networkx transitive closure
Every source (untrusted input entry point) is paired with every compatible sink (dangerous operation) and all call-graph paths between them are enumerated. Nothing is sampled. The path enumeration is always exhaustive — LLM validation depth scales with your hardware (see Exhaustive vs Smart).
Architecture
[Target Repo]
|
Step 0 - Fingerprint + SBOM (deterministic, full file inventory)
Step 1 - Classify target (16+ target types, dual-language detection)
Step 2 - Dependency vuln scan (NVD + EPSS/KEV ranked)
Step 2b - Secrets scan (gitleaks rules)
Step 3 - Static analysis (Semgrep scan + file inventory)
Step 3b - Code graph construction (call graph + source/sink/sanitizer tags + memory analysis)
Step 4 - Threat model + CVE catalog (1 LLM pass, product-specific CVE search)
Step 4b - Path enumeration (exhaustive source-to-sink path discovery + inter-procedural taint)
Step 4c - Per-path LLM analysis (exploitability validation, one path at a time + CVE context)
Step 4d - Blind spot coverage (file-by-file LLM review of all uncovered source files)
Step 5 - Memory findings extraction (results from step 3b's memory analysis)
Step 6 - Chain synthesis (networkx attack graph + transitive closure)
Step 7 - Validation (confidence-based filtering)
Step 8 - Anomaly check (prompt injection detection)
Step 9 - Report + PoC (root cause, exploit path, remediation)
|
Output: Verified findings with proof of exhaustive coverage
The code graph is built deterministically. The LLM is used only for exploitability reasoning on pre-traced paths, never for discovery.
Supported Targets
| Target | Language | Size | Status |
|---|---|---|---|
| Linux Kernel | C | ~30M LOC | Supported via memory analysis + call graph |
| VSCode | TypeScript | ~400K LOC | Full tree-sitter parsing + path enumeration |
| Microsoft Agent Framework | C# / .NET | Varies | Full C# sink coverage |
| GitHub Desktop | TypeScript / Electron | ~200K LOC | TypeScript + Node.js patterns |
| React, Angular, Vue | JS/TS | Varies | SSTI, XSS, prototype pollution |
| Django, Flask, Express | Python/JS | Varies | HTTP routes, ORM, auth decorators |
| Spring Boot | Java | Varies | All Spring annotations, SpEL, deserialization |
| Go services | Go | Varies | All Go sinks, goroutine races |
| Rust crates | Rust | Varies | Memory safety + unsafe FFI |
Theoretical Limits
What the system can do:
- Exhaustively enumerate all statically resolvable source-to-sink paths
- Find every injection vulnerability (command, SQL, path, SSRF, SSTI, etc.)
- Find every deserialization vulnerability
- Find every auth bypass and access control issue
- Find every memory corruption issue in C/C++/Rust
- Find every hardcoded credential
- Find every race condition and TOCTOU
- Find every XXE, LDAP injection, XPath injection
- Find every weak crypto and weak random usage
Known fundamental limits (inherent to static analysis):
- Dynamic dispatch through function pointers in C: conservatively flagged, exact target unknown without runtime info
- Virtual dispatch through vtables in C++/Java/C#: conservative resolution
- Reflection-based calls: flagged as suspicious with severity based on context
- C macros: AST parser does not expand macros
- Assembly code: not analyzed
- Cross-translation-unit inlining in C: call graph shows as separate functions
For these edge cases, the system reports them as uncertain via LLM analysis rather than giving false negatives.
Hardware Requirements
Tested configuration:
- GPU: RTX 4070 Ti SUPER (16GB VRAM)
- CPU: Ryzen 7 7700 (8C/16T)
- RAM: 32GB DDR5 6000MHz
- Model: Qwen2.5-Coder-14B-Abliterated Q4_K_M (14B dense, 8.6GB)
- Draft model: Qwen2.5-Coder-0.5B Q4_K_M (~400MB) for speculative decoding
- Total VRAM: ~9GB models, ~7GB free for KV cache
- Performance: 25-35 tok/s base, 35-50 effective tok/s with speculative decoding
- Context: 32K tokens (model training limit)
- Server: llama.cpp with jinja, flash attention, Q4 KV cache
Installation
1. Install llama.cpp
winget install llama.cpp # Windows
brew install llama.cpp # macOS / Linux
pip install -r requirements.txt
pip install huggingface_hub
2. Download Models
huggingface-cli download bartowski/Qwen2.5-Coder-14B-Instruct-abliterated-GGUF `
Qwen2.5-Coder-14B-Instruct-abliterated-Q4_K_M.gguf --local-dir models/
huggingface-cli download bartowski/Qwen2.5-Coder-0.5B-Instruct-abliterated-GGUF `
Qwen2.5-Coder-0.5B-Instruct-abliterated-Q4_K_M.gguf --local-dir models/
3. Start Model Server
python start_server.py
Reads model path from config.yaml. Use --no-speculative to disable speculative decoding.
4. Get NVD API Key
https://nvd.nist.gov/developers/request-an-api-key (30 seconds)
5. Download CVE Database
$env:NVD_API_KEY="your-key-here"
python -m src.main update-cve
This downloads ~45 minutes of data: 364K+ CVEs with EPSS + KEV, product-specific search.
6. Estimate Resources (Optional)
python -m src.main estimate /path/to/target-repo
7. Run Audit
python run_audit.py /path/to/target-repo
python run_audit.py /path/to/target-repo --resume # resume from checkpoint
Pipeline Details
Step 3b: Code Graph Construction
Builds the complete foundation for exhaustive analysis:
- AST parsing: 9 languages via tree-sitter, 7 via regex fallback
- Call graph: every function to every callee, direct + virtual dispatch, cross-file via import resolution
- Call sites: arguments extracted from every call expression for inter-procedural taint
- Source tags: every untrusted entry point annotated with source type
- Sink tags: every dangerous operation tagged with vulnerability class
- Sanitizer tags: every validator, encoder, auth check identified with
protected_againstcategories - Memory analysis: 5 parallel analyzers run inline for C/C++/Rust
Step 4b: Path Enumeration
For every source-to-sink pair that is compatible:
- Use the call graph to find all call paths from source to sink
- Trace taint through each function on the path (intra-procedural + inter-procedural accumulation)
- Cross-reference sanitizers against sink categories using a normalized taxonomy -- sanitizer
protected_againstvalues are mapped to sink categories viaSINK_TO_SANITIZER_TAXONOMY - Blocked paths still proceed to LLM analysis (sanitizer effectiveness verified, not blind-trusted)
- Record the complete path inventory
A medium codebase (~800 files) might produce 5,000-50,000 source-to-sink paths. Large codebases (Linux kernel, VSCode) use chunked processing and path prioritization to scale to millions of paths.
Step 4c: Per-Path LLM Analysis
For each enumerated path:
- Paths are deduplicated by unique (source, sink, category) combination
- Clear-cut cases get deterministic verdicts: sanitizer-taxonomy match → auto-BLOCKED, unreachable sink → auto-BLOCKED
- Only ambiguous paths go to the LLM (real function chains, taint present, no matching sanitizer)
- Smart prioritization (hardware-aware — scales to any GPU):
- The system enumerates ALL paths but adapts LLM analysis to your hardware
- Default smart limit (~2000 paths, ~1 hr on RTX 4070 Ti) ensures zero missed CRITICAL/HIGH vulns
- Have better hardware? Set
smart_limit_max: 0in config.yaml to analyze literally every single path — true exhaustive coverage. A 4090 or multi-GPU setup can run 16-32 workers and chew through 100K+ paths - Coverage guarantee: ≥1 path per unique sink category in the codebase — no vuln class is ever skipped
- Context-aware vuln class weights: Python repos boost SSTI/deserialization; C repos boost memory corruption; Java repos boost deserialization/SpEL
- Adaptive cap: small repos (<5000 paths) automatically analyze everything regardless of limit
- Every LLM prompt includes relevant CVE examples matching the path's CWE + product stack
max_llm_paths: 0enables automatic smart limit (smart_limit_maxconfig key, default 2000). Set a number for exact cap- Intra-step checkpointing: each path result saved incrementally to
path_analysis_progress.jsonl. Interrupted?--resumepicks up from the last completed path — zero work lost - Self-consistency: uncertain/low-confidence verdicts get 3 runs at temp 0.4; majority vote breaks the tie
Step 4d: Blind Spot Coverage
File-by-file LLM sweep for every source file NOT covered by path analysis. Uses an adversarial red-team prompt: "You're the attacker who found a zero-day here last week. How do you break this file?" Single-file focus, ranked by lethality (RCE > file ops > auth bypass > info disclosure). Explicitly skips safe-but-incomplete checks and theoretical issues -- only reports concrete, exploitable findings.
Step 5: Memory Corruption + LLM Validation
Five deterministic analyzers (allocation, buffer, lifetime, integer overflow, format string) flag memory issues via pattern matching on C/C++/Rust. CRITICAL/HIGH findings get LLM validation with source context around the flagged line: "Real vulnerability or false positive given bounds check / safe API / language safety?" Filters regex false positives before the report.
Step 6: Chain Synthesis
Builds an attack graph from all confirmed exploitable findings using networkx:
- Findings are classified by vulnerability role (code_exec, file_access, information_theft, access_escalation, etc.)
- Role transitions define valid chains: file_access → code_exec (LFI to RCE), information_theft → access_escalation (credential reuse), etc.
- Computes full transitive closure via graph shortest paths -- A → C is valid if A → B → C exists
- Chains up to 50 most confident multi-step exploits are surfaced
Steps 7-9: Validation, Anomaly, Report
- Step 7: Confidence-based filtering -- CRITICAL and HIGH severity memory findings pass with >= 0.6 confidence; path analysis results are filtered by VERIFIED_EXPLOITABLE verdict
- Step 8: Prompt injection detection via InjectionGuard statistical baseline
- Step 9: Exhaustive report with coverage statistics, PoC ideas, and remediation
Report Format
Every finding in the report includes:
- Summary: what the vulnerability is
- Root Cause: why it exists in the code
- Code Chain: exact data flow from source to sink
- PoC Steps to Reproduce: step-by-step instructions to trigger it
- Impact: concrete damage description
- Remediation: how to fix it with code examples
- How an Attack Can Exploit This: realistic attack scenario
Plus overall Coverage Statistics showing:
- Total source files parsed
- Functions analyzed, call graph edges
- Entry points identified
- Sources, sinks, sanitizers tagged
- Total paths enumerated
- Paths analyzed by LLM
- Verified exploitable, blocked, uncertain
- Memory corruption findings
- Exploit chains synthesized
Vulnerability Coverage
Injection
- Command injection (system, exec, subprocess, AddScript, PowerShell)
- SQL injection (raw queries, ORM raw, second-order)
- NoSQL injection (MongoDB $where, $expr, $function)
- LDAP injection
- XPath injection
- SSTI (Server-Side Template Injection)
- Header injection / HTTP request smuggling
Deserialization
- Python (pickle, marshal, yaml.load)
- Java (ObjectInputStream, XMLDecoder, ysoserial gadgets)
- .NET (BinaryFormatter, JavaScriptSerializer, TypeNameHandling)
- Ruby (Marshal.load, YAML.load)
- PHP (unserialize)
Memory Corruption (C/C++/Unsafe Rust)
- Stack buffer overflow (strcpy, gets, sprintf)
- Heap buffer overflow (memcpy with user-controlled size)
- Use-after-free
- Double-free
- Null pointer dereference
- Integer overflow leading to corruption
- Format string vulnerability
- Off-by-one
- Type confusion
Path and File
- Path traversal (directory traversal, zip slip)
- Arbitrary file read/write
- Insecure file permissions
- TOCTOU (time-of-check time-of-use)
- Symbolic link attacks
Network
- SSRF (Server-Side Request Forgery)
- HTTP request smuggling
- Open redirect
Authentication and Authorization
- IDOR (Insecure Direct Object Reference)
- Missing function-level access control
- Privilege escalation
- JWT attacks (alg:none, no signature verification, key confusion)
- OAuth redirect misuse
- Hardcoded credentials
- Weak session management
Cryptography
- Weak algorithms (MD5, SHA1, DES, RC4)
- Weak random (non-CSRNG for security)
- Missing MAC/signature
- ECB mode
- Static IV/nonce
- TLS verification disabled
Business Logic
- Parameter tampering
- Race conditions (TOCTOU, double-spend)
- Workflow bypass
- Type confusion
Time Estimates
The system auto-adapts to codebase size. LLM analysis (Step 4c) is the bottleneck — these estimates assume an RTX 4070 Ti (8 parallel workers). Better hardware = faster, more coverage:
| GPU / Workers | Paths/hr | Example (10K paths) | Example (100K paths) |
|---|---|---|---|
| RTX 3060 / 4 workers | ~1800 | ~5.5 hr | ~55 hr |
| RTX 4070 Ti / 8 workers | ~3600 | ~3 hr | ~30 hr |
| RTX 4090 / 16 workers | ~7200 | ~1.5 hr | ~14 hr |
| Dual GPU / 32 workers | ~14400 | ~45 min | ~7 hr |
Approximate estimates with smart limit (crit+high always included):
| Repo Size | Files | Source Files | Paths | Est. Time (8 workers) |
|---|---|---|---|---|
| Small (~200 files) | ~150 | ~100 | ~500 | 15-20 min |
| Medium (~800 files) | ~600 | ~400 | ~3,000 | 45-60 min |
| Large (~2,000 files) | ~1,400 | ~1,000 | ~15,000 | 1-2 hours |
| Very Large (~5,000 files) | ~3,500 | ~2,500 | ~25,000 | 2-3 hours |
| Enterprise (~30K+ files) | ~25K+ | ~8,000 | ~100,000 | 4-6 hours |
Non-LLM steps (parsing, call graph, path enumeration) are deterministic and fast — ~5-20 min even for enterprise repos.
Path analysis time ≈ paths × (15s / workers). With 8 workers: ~2s per path.
To analyze literally every path (true exhaustive mode): set smart_limit_max: 0 in config.yaml. Time scales linearly with path count. Add more GPU workers via parallel_analyzers to reduce time proportionally.
Run python -m src.main estimate /path/to/repo for per-project estimates.
Exhaustive Mode vs Smart Mode
The system is designed to find every vulnerability — path enumeration is always exhaustive. The LLM analysis can be exhaustive or smart-prioritized, depending on your hardware:
| Mode | Config | What it does |
|---|---|---|
| Exhaustive | smart_limit_max: 0 |
LLM analyzes every single enumerated path. True 100% coverage. Requires serious hardware for large repos. |
| Smart (default) | smart_limit_max: 2000 |
LLM analyzes top N paths + all CRITICAL/HIGH sinks + coverage of every vuln class. Zero missed high-impact vulns. Runs comfortably on consumer GPUs. |
Even in smart mode, non-LLM steps (parsing, call graph, path enumeration) are always exhaustive — every file, every function, every source-to-sink pair is enumerated. The smart limit only affects how many paths get LLM validation. If the deterministic code graph finds a sanitizer blocking a path, the verdict is instant (no LLM needed) and those paths don't count against the limit.
Scaling up: add parallel_analyzers: 16 (or 32) in config.yaml to double/quadruple LLM throughput. If you have access to cloud GPUs or a multi-GPU rig, set smart_limit_max: 0 and parallel_analyzers: 32 for true exhaustive analysis at enterprise scale.
Scaling for Large Codebases
The system includes a dedicated scaling module (src/analysis/scaling.py):
Chunking
- Files processed in chunks of 500
- Files larger than 10MB are skipped (likely generated)
- Test and vendor directories are excluded
- Per-directory processing for memory efficiency
Parallelism
- Source, sink, sanitizer tagging parallelized across threads
- Configurable worker count (default 16, up to 32 for enterprise)
Path Prioritization
- Paths scored by severity, vulnerability class weight (context-aware by language/framework), function chain depth, and sanitizer presence
- Zero missed CRITICAL/HIGH: all CRITICAL and HIGH severity paths always analyzed, regardless of limit
- Coverage guarantee: ≥1 path per unique sink category found in the codebase — no entire vuln class is ever skipped
- Context-aware weights: C repos boost memory corruption; Python repos boost SSTI/deserialization; Java repos boost serialization/SpEL; JS repos boost prototype pollution/NoSQL
- Smart limit auto-activates when paths exceed
smart_limit_max(default 2000). Setsmart_limit_max: 0for unlimited - Deduplication by unique sink (file + line + category) prevents analyzing the same dangerous operation multiple times
Memory Management
- Streaming report writer (findings written incrementally)
- File size limits
- Automatic config adaptation based on repo size
Adaptive Configuration
| Files | Config Profile | Smart Limit | Workers |
|---|---|---|---|
| Under 100 | minimal | 200 | 4 |
| Under 1000 | standard | 500 | 8 |
| Under 10000 | large | 1000 | 16 |
| 10000+ | enterprise | 2000 (adaptive) | 32 |
Smart limit is a ceiling, not a quota — CRITICAL+HIGH paths are always included regardless of the limit.
Run python -m src.main estimate /path/to/repo to see estimated scope and time for any target.
Checkpointing
Full step-level checkpoints + intra-step incremental checkpoint for Step 4c (the longest step):
data/checkpoints/<hash>/
├── progress.md Human-readable: batch 87/450, 23 candidates
├── code_graph.json Complete code graph
├── path_enum.json All source-to-sink paths
├── path_analysis_progress.jsonl [NEW] Incremental checkpoint: one line per analyzed path
├── path_analysis.json Final merged per-path LLM results
├── chains.json Exploit chains
├── report.md Final report
path_analysis_progress.jsonl is written atomically after each path is analyzed. If the process is interrupted (Ctrl+C, crash, power loss) during Step 4c, --resume picks up from the last completed path — zero work lost. No more restarting from scratch.
Commands
| Command | Description |
|---|---|
python start_server.py |
Start server (reads model from config.yaml) |
python start_server.py --no-speculative |
Start without draft model |
python run_audit.py <path> |
Full exhaustive audit |
python run_audit.py <path> --resume |
Resume from checkpoint |
python -m src.main setup |
Setup instructions |
python -m src.main update-cve |
Download/build CVE database |
python -m src.main estimate <path> |
Estimate resources for a target |
Configuration
model:
name: "Qwen2.5-Coder-14B-Abliterated"
file: "models/Qwen2.5-Coder-14B-Instruct-abliterated-Q4_K_M.gguf"
server:
context_length: 32768
flash_attn: true
speculative:
enabled: true
pipeline:
max_path_depth: 8
max_paths_per_pair: 20
max_llm_paths: 0 # 0 = auto smart limit, N = exact cap
smart_limit_max: 2000 # LLM path ceiling (0 = exhaustive — analyze ALL paths)
llm_temperature: 0.3
parallel_analyzers: 8 # LLM workers (scale with GPU: 4=3060, 8=4070Ti, 16=4090, 32=multi-GPU)
scaling:
max_files_per_chunk: 500
max_file_size_bytes: 10000000
skip_test_directories: true
skip_vendor_directories: true
max_functions_per_chunk: 50000
max_paths_total: 100000
llm_priority_top_n: 1000
num_workers: 16
auto_adapt_to_size: true
Project Structure
models/ GGUF files (main + draft)
src/
orchestrator.py Master pipeline + checkpointing
main.py CLI entry point
pipeline/
step0_fingerprint.py Fingerprinting + file inventory
step1_classify.py 16+ target types
step2_deps.py Dependency vuln scan (NVD + EPSS/KEV)
step2_secrets.py Secrets scanner (gitleaks rules)
step3_static.py Semgrep scan + file inventory
step3b_codegraph.py Code graph + source/sink/sanitizer tags + memory analysis
step4_threat_model.py Threat model + CVE catalog
step4b_path_enum.py Exhaustive source-to-sink path enumeration
step4c_path_analyze.py Per-path LLM exploitability validation
step4d_blindspot.py File-by-file blind spot review (Project Black)
step6_chains.py Attack graph + transitive chain synthesis
step8_anomaly.py Prompt injection detection
step9_report.py Exhaustive report with PoCs
analysis/
ast_parser.py Multi-language tree-sitter parser (+ regex fallback)
call_graph.py Call graph with import resolution
source_tag.py All untrusted entry points
sink_tag.py All dangerous operations
sanitizer_tag.py All sanitizers (with protected_against taxonomy)
intra_taint.py Intra-procedural taint tracking
inter_taint.py Inter-procedural taint propagation
path_enum.py Source-to-sink path enumeration + sanitizer taxonomy
path_analyze.py Per-path LLM analysis
attack_graph.py networkx-based transitive chain synthesis
scaling.py Large codebase support
semgrep_runner.py External SAST integration
secrets_scanner.py Entropy-filtered secrets detection
memory/
orchestrator.py Memory analysis coordinator
alloc_tracker.py Allocation tracking
buffer_analyzer.py Buffer overflow detection
lifetime.py Use-after-free, double-free
int_overflow.py Integer overflow detection
format_string.py Format string vulnerability (C/C++ unsafe only)
knowledge/
cve_db.py, downloader.py, importer.py, embeddings.py, epss.py, kev.py, sbom.py
llm/
client.py, prompts.py, context.py, guard.py
data/
cve/nvd.sqlite CVE database (364K+)
checkpoints/ Per-repo audit state
config.yaml, requirements.txt
start_server.py, run_audit.py
Design Principles
- Exhaustive coverage: every non-test source file reviewed, every source-to-sink path enumerated, nothing sampled
- Deterministic foundation plus LLM reasoning: code graph built deterministically, LLM validates pre-traced paths
- Short, focused prompts: per-path analysis with ~2K token prompts
- Clean context per batch: no memory between batches
- Sanitizer-aware taint tracking: cross-taxonomy mapping between sink categories and sanitizer
protected_againstvalues - Inter-procedural taint accumulation: tainted variables propagate across function boundaries, not overwritten
- LLM-based validation: LLM checks sanitizer effectiveness, static tags are signals not blockers
- Product-specific CVEs: CVSS version-tagged, targeted to actual tech stack
- 5 parallel memory analyzers: allocation, buffer, lifetime, integer overflow, format string (with language-specific FP filtering)
- Config-driven model: switch models by changing one line in config.yaml
- Scales to enterprise codebases: single-pass file inventory, node-based AST reuse, adaptive config
- Local only: no data leaves your machine, no API costs
References
- Project Black: Local AI for Penetration Testing (https://projectblack.io/blog/local-ai-for-cyber-security/)
- RAPTOR: Autonomous Security Research Framework (https://github.com/gadievron/raptor)
- Qwen2.5-Coder-14B-Abliterated (https://huggingface.co/bartowski/Qwen2.5-Coder-14B-Instruct-abliterated-GGUF)
- tree-sitter (https://tree-sitter.github.io/)
Comments