What if the model guessed the bugs and mechanics proved them?
Indict: LLM vulnerability hunting where a sandbox decides
- Runs on
- Python
- First released
- Updated
Indict is a command-line tool that runs six vulnerability-hunting engines over a git repository and writes one report. A finding counts as verified only if a proof of concept ran in a sandbox and reached the reported line. It's for security researchers and maintainers who want the bugs a language model finds to be proven.
I built it for the AI Tinkerers and Superlinked hackathon on 20 September 2026. Judges scored valid vulnerabilities, weighted by significance and checked by hand. I have no placing to report. The README describes earlier preparation, so I won't call it a one-day build.
The model proposes, mechanics decide
Language models suggest candidate bugs. Mechanical checks accept or reject them. The pipeline runs ingest, recon, sweep, rank, engines, consensus, adjudicate, proof of concept, chain and report.
- Six engines produce candidates: a semgrep and bandit sweep, an LLM call-chain hunter, LLM-generated taint rules, a data-flow explorer, git-history variant analysis, and LLM-written fuzz harnesses.
- The consensus step removes duplicates.
- An adversarial falsifier tries to kill each survivor. It can do that only by citing concrete mechanical evidence, such as "file does not exist". Indict rejects a dismissal without it, and every kill goes into a ledger with its reason.
- Each survivor gets a proof of concept, run in a Docker sandbox with no network, a read-only filesystem, and capped CPU and memory. The sandbox traces execution.
- A finding is "verified" only if the traced run reached the reported line and a probe string such as
VULN_CONFIRMED_PROTOTYPE_POLLUTIONfired. The probe output comes from the sandbox, so the model can't fake it.
Two later steps use verified results. The chain step combines verified pairs into one chained proof of concept. The learn step turns a verified bug into a semgrep rule, and Indict stores the rule only if it hits the known-bad line, within 10 lines.
None of the bug-finding ideas is new. The variant engine mines git history for fixes and looks for the same mistake elsewhere, which is the Big Sleep idea. The other engines borrow from vulnhuntr, IRIS and RepoAudit. What I added is the gate.
What I measured
I ran a calibration set of 5 known CVEs: lodash, minimist, moment, pyyaml and requests. The scoring script reports 5 of 5 rediscovered, each verified by a proof of concept, with a false-positive rate of 0.0. I turned the learned rules off for this run so earlier results couldn't leak in.
That set proves little. The bugs are already public, so it shows the pipeline works and says nothing about finding new bugs.
On real targets at the hackathon, most findings ended up "adjudicated" instead of verified, meaning they survived the falsifier but have no working proof of concept. On one target, 18 findings gave 2 verified. The repo records no report of any finding to a maintainer and no confirmation from one, and I don't list unpatched issues here.
Limits
- It needs Docker, semgrep, and either API keys or a local 30B model, which can crash a laptop.
- The proof-of-concept probes match a bug class, and some classes can't be proven this way.
- The hackathon model endpoint served only 8k tokens of context, so I set
INDICT_MAX_TOKENS=4096andINDICT_MAX_FILE_LINES=400. - Docker image builds have to run one at a time, and they can push the host into swap.
- Learned rules are global, so a rule from one repository applies to the next.
- I didn't find a licence file in the repo root.
Against other tools
Semgrep, bandit, gosec and osv-scanner are established and have no model in the loop. Indict runs semgrep and bandit inside its sweep. Vulnhuntr, RepoAudit and IRIS each implement one technique that Indict borrows. Indict adds a shared gate on top.
Try it
The source is at https://github.com/hridaya423/indict. There's no hosted demo.
uv sync
brew install semgrep universal-ctags
uv tool install osv-scanner
uv tool install bandit
bash sandbox/build_images.sh
cp .env.example .env
./indict --doctor
./indict --no-llm calibrate/smoke/vulnapp
The last command is an offline smoke test. For a real scan, add model keys to .env and run ./indict <git-url-or-path> --engines sweep,hunter --models deepseek,glm --passes 2 --workers 6. bash calibrate/demo.sh is a 5-second demo, and ./indict --board opens the dashboard at 127.0.0.1:8404.