Security tests for LLM apps, written in pytest.
Putting a language model in your app adds failure modes your test suite cannot see: it will follow an instruction hidden in a document it retrieved, repeat a key it was told to keep, or act on an authorisation the caller simply typed. LLMSecTest attacks your running app for all ten OWASP LLM Top 10 categories and tells you what it got out. Open-source and MIT-licensed; pre-alpha, and built in the open.
pip install "git+https://github.com/wehnsdaefflae/llmsectest"
Run your first scan »
Just pytest
No scanner off to the side. Security checks are pytest tests — same command, same build gate, same plumbing as your unit tests.
How it works »Mapped & scored
Every probe is tied to an OWASP LLM Top 10 category and carries a CVSS v4.0 base score, so a finding is triageable, not a wall of text.
The coverage map »CI-native
Reports emit as SARIF v2.1.0 — plus HTML, JSON and Markdown — the format code-scanning dashboards already read. No bespoke glue.
A sample finding »Quick-start
Get a scan running in seconds.
Install from source while it's pre-alpha, point it at a model or your running app, and read the SARIF. Full walkthrough in the docs.
~ $ pip install "git+https://github.com/wehnsdaefflae/llmsectest"
~ $ llmsectest --target anthropic:claude-3-5-haiku
~ $ llmsectest --target app:http://localhost:8000/chat # your live app
~ $ llmsectest --target app:http://localhost:8000/chat \
--report-formats=sarif,html,json,markdown
# => OWASP LLM Top 10 categories probed · findings written to results/<target>.sarif
OWASP LLM Top 10 — honest status
What's covered, and what isn't yet
The framework is real; coverage was filled in deliberately, category by category. Nothing was marked done before it was — all 10 of the OWASP LLM Top 10 (2025) categories are implemented and tested today. What remains is depth, not breadth. Every run reports any category it couldn't reach (a missing repo, model path or app marker) as an explicit skip, never a silent gap.
The numbering below is the 2025 edition. Earlier editions numbered these differently (supply chain was LLM05 and is now LLM03), so a category number from an older list will not line up with what the reports say.
- LLM01Prompt Injectiondone
- LLM02Sensitive Information Disclosuredone
- LLM03Supply Chain (dependency manifests, OSV)done
- LLM04Data & Model Poisoningdone
- LLM05Improper Output Handlingdone
- LLM06Excessive Agencydone
- LLM07System Prompt Leakagedone
- LLM08Vector & Embedding Weaknessesdone
- LLM09Misinformationdone
- LLM10Unbounded Consumptiondone
done implemented & tested — 10/10 · depth improvements continue on the roadmap
Known limitations, because "done" is not the same as "tells you everything". One category currently carries a caveat you should read before trusting a clean row: LLM06 (excessive agency) can only report what your application actually emits, so if yours describes an action in prose instead of emitting the signature you passed, a clean row means "not observed" rather than "not vulnerable". Every limitation we know about is written down in the changelog as it is found, and removed from there when it is fixed, not when it is forgotten.
Roadmap — shipped, building, planned
Where it's going
Built in the open across the funding period. Each phase ships before the next is claimed — the status here tracks the real state of the code, not a wish-list.
-
01
Foundation
shipped- pytest-native framework & plugin
- Unified adapter — OpenAI · Anthropic · Hugging Face · Ollama & LM Studio (local), with a fail-fast
--preflighthealth check - Reports — SARIF v2.1.0 · HTML · JSON · Markdown
- CVSS v4.0 scoring with OWASP mapping
- CLI & documentation site
-
02
OWASP coverage
shipped- All 10 of 10 categories live — LLM01–LLM10 (complete OWASP LLM Top 10 2025 coverage)
- Black-box application testing —
--target app:<url>, up to 8 categories with--app-prompt/-secret/-action/-canary/-rag-poison(LLM01/05/09/10 always-on) - LLM02 and LLM06 attack an application along four mechanisms each, not four wordings — including a secret asked for base64-encoded (the de-obfuscating oracle still catches it) and a forged authorization, the fabricated id and ticket reference an agent cannot check
- White-box scans — dependency manifests with an OSV known-CVE lookup (
--repo,--osv, LLM03) and an offline serialization-opcode scan of model files (--model-scan, LLM04) - Red-team jailbreak set (JailbreakBench / AdvBench,
--redteam-set) and an over-refusal false-refusal-rate metric on the benign twins (--redteam-benign) — and a target that refuses by quoting the demand is scored as having refused it, not as having complied - RAG-specific LLM08 — retrieval exposure (
--app-canary) and indirect prompt injection via a poisoned retrieved document (--app-rag-poison) - Misinformation — confabulation probes on guaranteed-nonexistent entities, scored by a non-circular disclaimer oracle (LLM09)
- A clean run reports what the target withstood, per category, so a scan of a well-defended app does not read like a scan that attacked nothing
- Runaway targets are handled honestly —
--app-timeoutis a real wall-clock deadline (so a trickling app is cut off like a stalled one), a probe that exhausts it is recorded as inconclusive rather than a silent pass, and on a bounded LLM10 request exhausting it is itself the finding - An app the scan cannot reach is never reported as a vulnerable one — a dead endpoint, a non-JSON reply or a server that dies mid-scan makes those probes inconclusive, and the run exits non-zero so an empty findings list from a scan that reached nothing cannot pass CI as a clean result
- Standalone HTML from any SARIF file (
--render-sarif), proven against committed output from three real scanners (ruff, Bandit, Semgrep) - Depth, not breadth — LLM08 white-box dimensions, a classifier refusal oracle (Llama-Guard / GLiGuard)
Release-by-release detail lives in the changelog.
-
03
Depth & reports
in progress- CycloneDX SBOM generation —
--sbom <path>(LLM03) - Deeper supply-chain analysis
- Embedding-inversion & stress tests
- PDF reports · remediation database · plugin API
- CycloneDX SBOM generation —
-
04
Integrations & v1.0
planned- CI/CD templates — GitHub Actions · GitLab CI · Jenkins
- Hardening & independent security audit
- v1.0 on PyPI · OWASP community submission
shipped in the code today · in progress being built now · planned ahead
Output — the target format
Findings you can act on
A run produces SARIF that drops straight into GitHub code scanning, GitLab, or any SARIF viewer — each finding carrying its OWASP category, CVSS vector and a remediation pointer. The snippet is the shape of the output the framework targets.
Illustrative — format target, not a recorded scan result.
{
"ruleId": "LLM01-prompt-injection",
"level": "error",
"properties": {
"owasp": "LLM01:2025 Prompt Injection",
"cvss": "CVSS:4.0/AV:N/AC:L/.../VC:H",
"score": 9.2
},
"message": {
"text": "System prompt recovered via instruction override."
}
}
Built in the open, for the people shipping LLM features.
App developers, security leads and researchers — the repo is public and the roadmap is honest. Watch it, try the adapter, or open an issue.
github.com/wehnsdaefflae/llmsectest »