ExploitSense is a local-first vulnerability analysis tool that scans your code for real security issues and then, only when you ask, uses a local AI model to explain exactly what's wrong, how an attacker would exploit it, and how to fix it properly.
Nothing leaves your machine without your explicit action. No cloud APIs. No telemetry. No subscriptions.
When you drop in a Python file (or paste some code), ExploitSense runs it through Bandit and a custom rule engine that covers the OWASP Top 10. Every finding gets normalized into a clean structure with a severity level, CWE reference, and the exact line where the issue lives. You see results in a fast, dark-themed dashboard that groups findings by severity.
The AI part only kicks in when you click the Explain button on a specific finding. At that point, ExploitSense sends a minimal, sanitized snippet to your local Ollama instance running mistral-nemo. The response comes back as a six-section report covering the vulnerability summary, why it exists, how an attacker would use it, a realistic attack example, a code-level fix, and best practices. The second time you ask about the same issue, nothing calls the model at all — it's served instantly from the local SHA-256 cache.
You need Python 3.11 or newer, Node.js 18 or newer, and Ollama installed and running. Once Ollama is up, pull the model:
ollama pull mistral-nemo:latestIf you want Python code scanning via Bandit to work (it's the best scanner in the system), install it alongside the other backend dependencies. JavaScript scanning is optional and works if you have ESLint installed globally.
First, copy the environment template:
cp .env.example .envThe defaults in that file are fine for a local setup. Then install the Python dependencies and start the API server:
cd backend
pip install -r requirements.txt
uvicorn app.main:app --reloadThe API will be live at http://localhost:8000. You can hit /health to confirm it's up.
In a separate terminal:
cd frontend
npm install
npm run devThen open http://localhost:5173. That's it.
From the backend directory:
cd backend
pytest tests/ -vThe test suite covers the Python and HTTP scanners, the normalizer, and the caching layer. The Ollama dependent tests use mocks so they work fully offline. If Bandit isn't installed, those tests skip themselves cleanly instead of failing.
If you'd rather scan from the terminal without the UI, there's a Click-based CLI. From the project root:
python cli/exploitsense_cli.py scan backend/tests/fixtures/vuln_python.pyYou can also export results to JSON:
python cli/exploitsense_cli.py scan backend/tests/fixtures/vuln_python.py --output results.jsonAnd if you already have a scan ID from a previous run, you can pull the full report:
python cli/exploitsense_cli.py report <scan_id>If you want everything containerized:
docker-compose up --buildThe backend runs on port 8000, the frontend on port 5173. Ollama still needs to be running on your host machine the containers reach it via host.docker.internal.
The rule engine loads any .py file you drop into a rules/ directory inside the backend. Each file just needs to export a RULES list. Here's what a rule looks like:
import re
RULES = [
{
"name": "my_custom_rule",
"pattern": re.compile(r"your_pattern_here", re.I),
"vuln_type": "HARDCODED_SECRET",
"severity": "HIGH",
"description": "What this rule catches and why it matters.",
}
]Drop the file in, restart the backend, and the rule is active. No code changes needed anywhere else.
Every time you click Explain, ExploitSense computes a SHA-256 hash of the vulnerability type, the sanitized snippet, and whether learning mode is on. If that hash exists in the local SQLite database, the cached explanation is returned immediately Ollama is never called. This means if you scan the same code twice, or if two separate files have the same vulnerability pattern, the AI only does work once.
The model is set in your .env file:
OLLAMA_MODEL=mistral-nemo:latest
You can change this to any model you have pulled locally. Smaller models like llama3.2 respond faster. Larger models give more thorough explanations. The timeout is also configurable — bump OLLAMA_TIMEOUT if you're running a slow machine or a very large model.
The backend lives in backend/app and is broken into scanners (one per language), a detection layer that normalizes findings, an AI module that handles prompts and caching, a store layer for SQLite, and a FastAPI layer with three routes: scan, explain, and report.
The frontend is a Vite + React + TypeScript app with a typed API client, per-finding hooks, and a component tree that goes App → Dashboard → VulnCard → ExplainPanel.
The CLI in cli/exploitsense_cli.py talks directly to the scanner and store without going through the HTTP API, so it works without the backend server running.
ExploitSense was built on the assumption that your code is sensitive. The sanitizer strips API keys, tokens, passwords, and email addresses from any snippet before it's sent anywhere. Full files are never sent to the AI only the specific snippet around the finding, capped at 500 characters. And even that only happens when you explicitly click Explain.