Praesidium — dynamic CVE reachability confirmation engine
Project description
Praesidium
Praesidium is a dynamic CVE reachability confirmation engine that combines static analysis with LLM-powered hypothesis generation to determine if vulnerabilities are actually exploitable in your codebase.
Features
- 🔍 Static Call Graph Analysis - Traces code paths from entry points to vulnerable functions
- 🤖 LLM-Powered Hypothesis Generation - Uses Groq's GPT models to generate realistic exploit attempts
- 🎯 Dynamic Confirmation - Monkeypatches vulnerable functions to confirm actual reachability
- 📊 Multi-CVE Support - Analyze multiple CVEs against your codebase in one command
- 🔑 Easy Configuration - Simple CLI for API key management
- 📈 FastAPI/Flask Support - Works with popular Python web frameworks
Setup
uv sync --extra dev
cp .env.example .env # fill in GROQ_API_KEY (get one at console.groq.com)
Run tests
uv run pytest -v
test_graph_builder.py and test_dynamic_harness.py run against the dummy
fixture app in tests/fixtures/dummy_target_app/ and need no API key —
these are your fast inner-loop tests while building Hour 0:30–3:30.
Run the full pipeline (needs Groq API key)
uv run python scripts/smoke_test.py
Runs both a reachable and a not-reachable case against the dummy fixture, printing the full trace. This is your first real integration check before Checkpoint 1 with Engineer B — swap the dummy fixture for the real target app + real CVE once B has it ready.
CLI Usage
Installation
# Install from this directory
uv pip install -e .
# Or install from PyPI (once published)
pip install praesidium
Quick Start
1. Configure API Key
# Set your Groq API key (get one at console.groq.com)
uv run praesidium config set-key gsk_your_api_key_here
# Verify configuration
uv run praesidium config show
2. Analyze Multiple CVEs
Create a JSON file with your CVE configurations:
[
{
"cve_id": "CVE-2023-12345",
"flagged_function": "save_file",
"flagged_module": "app.services.upload",
"flagged_file": "app/services/upload.py",
"entry_points": ["app.routes.upload_endpoint"],
"advisory_summary": "Path traversal vulnerability",
"function_signature": "def save_file(filename, contents)"
},
{
"cve_id": "CVE-2023-67890",
"flagged_function": "process_data",
"flagged_module": "app.services.data",
"flagged_file": "app/services/data.py",
"entry_points": ["app.routes.data_endpoint"],
"advisory_summary": "SQL injection vulnerability"
}
]
Run the analysis:
# Analyze current directory
uv run praesidium run --cves cves.json
# Analyze specific directory
uv run praesidium run --cves cves.json --target /path/to/app
# Get summary only
uv run praesidium run --cves cves.json --summary
# Output as JSON
uv run praesidium run --cves cves.json --output json > results.json
3. Check Single CVE
For single CVE analysis:
# Generate sample config
uv run praesidium init-config cve.json
# Edit the config, then run
uv run praesidium check /path/to/app --cve-config cve.json
Or use command-line options:
uv run praesidium check /path/to/app \
--cve-id CVE-2023-12345 \
--flagged-function save_file \
--flagged-module app.services.upload \
--flagged-file app/services/upload.py \
--entry-points app.routes.upload_endpoint \
--advisory-summary "Path traversal vulnerability"
Available Commands
praesidium config set-key <API_KEY>- Configure Groq API keypraesidium config show- Display current configurationpraesidium run --cves <FILE>- Analyze multiple CVEs from JSON filepraesidium check <PATH>- Check single CVE against target apppraesidium init-config <FILE>- Generate sample CVE config file
Python API (the interface contract)
from src import CVETarget, run_pipeline
target = CVETarget(
cve_id="CVE-2024-XXXXX",
flagged_function="...",
flagged_module="...",
flagged_file="...",
entry_points=["..."],
advisory_summary="...",
function_signature="...",
)
result = run_pipeline(target, target_app_root="/path/to/target/app")
# result: EngineOutput(cve_id, label, trace, static_path, hypothesis_attempts)
CVETarget and EngineOutput in schemas.py are the two shapes from
build-plan.md Section 2 — sync any changes to these with Engineer B
immediately, don't let them drift silently.
LLM provider
Hypothesis generation (hypothesis.py) calls Groq's OpenAI-compatible chat
completions endpoint, using openai/gpt-oss-120b by default (Groq's
current recommended production model for reasoning workloads — the earlier
llama-3.3-70b-versatile was deprecated). Override with GROQ_MODEL in
.env if you want to try openai/gpt-oss-20b for lower latency during
iteration. Groq's speed matters here specifically because Stage 2 can fire
up to 3 calls per CVE (1 initial + 2 retries) inside a live demo.
Publishing to PyPI
To publish Praesidium to PyPI:
# 1. Update version in pyproject.toml
# 2. Build the package
uv build
# 3. Test on TestPyPI first (recommended)
uv pip install twine
uv run twine upload --repository testpypi dist/*
# 4. Test install from TestPyPI
pip install --index-url https://test.pypi.org/simple/ praesidium
# 5. If everything works, publish to PyPI
uv run twine upload dist/*
Note: You'll need PyPI accounts and API tokens. See PyPI documentation for details.
Known scope boundaries (by design, not oversight)
- Static graph resolution is name-based, not points-to analysis — decorators
and dynamic dispatch can produce false negatives at Stage 1. This is why
Stage 2 exists; don't try to fix it in
graph_builder.py. - Retry cap is hard-set to 2 attempts (
nodes.py::MAX_RETRIES) to keep demo runtime bounded. Don't raise it without checking demo timing. EntryPointDriversupportscallableandflask_routeshapes only. If B's fixture needs CLI-arg driving, extenddynamic_harness.py.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file praesidium-0.2.2.tar.gz.
File metadata
- Download URL: praesidium-0.2.2.tar.gz
- Upload date:
- Size: 71.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e8a24526ceea0a77b4d5996230f62b92f1899005482b0ea838c25d53a3ebd13a
|
|
| MD5 |
f238caf25535e1b39961bf3776a6e9c0
|
|
| BLAKE2b-256 |
13246569b8743a081a6ab6dcabdb6d05d5a7f32d0edfdc2fe0c29b77127e6bbc
|
File details
Details for the file praesidium-0.2.2-py3-none-any.whl.
File metadata
- Download URL: praesidium-0.2.2-py3-none-any.whl
- Upload date:
- Size: 19.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d3e0e8e5613a0b5632d3b84a7e289e6a422beb939a74b63e5a4c9ab2681cb5ac
|
|
| MD5 |
ffc8bc9bf1191869cf569160820ff5b6
|
|
| BLAKE2b-256 |
ecc2e900f506e89cb78668e6db45d7f0b9d808aa5c7c390980266e3d71b8a8ef
|