Autonomous prompt-injection and jailbreak testing for AI chat interfaces.
ANI helps authorized security testers evaluate how well chatbot interfaces withstand prompt injection, jailbreak, system prompt leakage, data exfiltration, RAG poisoning, tool/MCP/agent abuse, and multi-turn social-engineering attacks. It can run from a Firefox sidebar inside your logged-in browser session, or from the Python CLI for automated scans, adaptive loops, and CI/CD-friendly reports.
- Adaptive attack loop powered by a pluggable LLM brain (DeepSeek by default; works with OpenAI-compatible endpoints, Anthropic-style adapters, or a fully offline rules-only mode).
- Firefox sidebar workflow for testing authenticated web chat sessions without rebuilding login flows.
- Python CLI for repeatable scans, multi-turn chains, saved sessions, and HTML, JSON, or SARIF reports.
- 10 attack families covering injection, jailbreak, system prompt extraction, data exfiltration, encoding bypasses, RAG indirect injection, tool/MCP/agent abuse, and two multi-turn social-engineering chains.
- Model and interface detection to identify target chat elements and likely AI providers, with iframe and Shadow DOM awareness.
- Evidence-first reporting with clear vulnerable or secure verdicts and a normalised risk score.
- Externalized payloads in
payloads/<category>.jsonso researchers can add tests without touching Python. - Encrypted local storage of auth profiles and saved sessions (Fernet, key from
ANI_ENCRYPTION_KEYor auto-generated at~/.ani/encryption.key). - SARIF output drops straight into GitHub Code Scanning, GitLab, or Azure DevOps.
- Baseline + diff scans to detect regressions after a target deploys a guardrail.
- Per-payload custom indicators via JSON overrides for targets where the default substring set isn't the right fit.
- Hardened sidebar with safe DOM construction (no
innerHTMLfor AI output) and optional encrypted API key persistence inbrowser.storage.local.
flowchart LR
Tester[Tester in browser or CLI] --> Target[Target AI chat]
Target --> Response[Captured response]
Response --> Analyzer[Brain: DeepSeek / OpenAI-compatible / rules-only]
Analyzer --> Payload[Next payload]
Payload --> Target
Response --> Report[HTML / JSON / SARIF report and verdict]
- Open the target AI chat in Firefox or launch a CLI scan.
- Choose an attack category. For adaptive mode, an LLM brain reviews the response and crafts the next attempt.
- ANI submits a payload, captures the response via a MutationObserver with text-stability detection, and evaluates the result.
- The scan ends with evidence, a per-category verdict, and a final risk score.
ANI/
|-- ani-addon/ Firefox sidebar extension
|-- firefox-session-exporter/ Helper extension for exporting browser sessions
|-- payloads/ Externalized payload library (one JSON per category)
|-- src/ Python CLI and scan engine
| |-- attacks/ Attack implementations
| |-- browser/ Playwright browser control and detection
| |-- detection/ Vulnerability pattern analysis
| |-- reporting/ HTML, JSON, and SARIF report generation
| |-- utils/ Config, logger, Fernet crypto, LLM brain
| `-- cli.py Typer CLI entry point
|-- auth_profiles/ Auth profile JSON (encrypted when saved via CLI)
|-- sessions/ Saved browser sessions (encrypted at rest)
|-- tests/ Pytest unit and integration tests
|-- reports/ Generated reports (gitignored)
|-- requirements.txt
|-- setup.py
|-- MANIFEST.in Ensures templates ship with `pip install .`
|-- .env.example Template for environment variables
`-- start.bat Windows launcher (option 0 = first-time setup)
Run start.bat and choose 0. Setup / Install. This creates a virtualenv, installs requirements.txt, and runs playwright install chromium. After that, use the other menu options to launch scans.
Manual equivalent:
python -m venv venv
venv\Scripts\activate
pip install -r requirements.txt
playwright install chromiumYou can also install the package locally:
pip install -e .- Open Firefox and go to
about:debugging#/runtime/this-firefox. - Select Load Temporary Add-on....
- Open
ani-addon/manifest.json. - Pin or open the ANI sidebar.
- The sidebar requests the
storagepermission so the DeepSeek API key can be saved encrypted inbrowser.storage.localand wiped from the DOM after each read.
ANI reads optional environment variables (see .env.example):
| Variable | Purpose |
|---|---|
ANI_API_KEY |
Optional default DeepSeek API key. |
ANI_ENCRYPTION_KEY |
Fernet key (URL-safe base64 32-byte) for encrypting auth_profiles/*.json and sessions/*.json. If unset, ANI generates one and stores it at ~/.ani/encryption.key with 0o600 perms. |
ANI_AUTH_COOKIE |
Fallback auth cookie value (prefer --cookie-file for shell safety). |
ANI_LOG_FILE |
Path to a rotating log file (off by default). |
ANI_LOG_LEVEL |
DEBUG, INFO, WARNING, or ERROR. |
ANI_LLM_BACKEND |
rules (offline), deepseek, or openai_compatible (default; works with OpenAI, Together, Groq, OpenRouter, Ollama /v1, LM Studio). |
ANI_LLM_MODEL |
Model name for the chosen backend (default deepseek-chat). |
ANI_LLM_BASE_URL |
Base URL for openai_compatible backends. |
ANI_LLM_API_KEY |
API key for the LLM backend (falls back to ANI_API_KEY). |
- Open the target chat in Firefox.
- Open the ANI sidebar.
- Enter your DeepSeek API key (or click Save to persist it encrypted in
browser.storage.local). - Select adaptive mode and set the maximum rounds.
- Run an attack category and watch the live progression.
- Stop anytime, or let the scan reach its final verdict.
python -m src.cli scan "https://your-target.example" --auth manual
python -m src.cli list-tests
python -m src.cli sessions list
python -m src.cli scan "https://your-target.example" --tests prompt_injection,jailbreak --output reports/scan.htmlpython -m src.cli scan "https://your-target.example" --adaptive --rounds 10 --auth session --session-file session_example.jsonpython -m src.cli scan "https://your-target.example" --auth token --cookie-file cookie.txt --format sarif --output reports/ani.sarif
python -m src.cli scan "https://your-target.example" --baseline reports/previous.json --output reports/diff.html| Category | Type | Purpose |
|---|---|---|
prompt_injection |
Single-shot | Tests whether user input can override trusted instructions. |
jailbreak |
Single-shot | Probes role-play, policy bypass, and restriction-breaking behavior. |
system_prompt |
Single-shot | Attempts to reveal hidden or internal instructions. |
data_exfiltration |
Single-shot | Checks for unsafe URL, markdown, or data-leak generation. |
encoding_bypass |
Single-shot | Uses encoded or obfuscated prompts to bypass filters. |
advanced |
Single-shot | Multi-step and higher-complexity attack flows. |
gradual_escalation |
Multi-turn | 4-turn chain that starts benign and escalates to a system-prompt leak. |
persona_building |
Multi-turn | 3-turn chain that primes a "truth-telling game" persona. |
rag_injection |
Single-shot | Indirect prompt injection via documents and retrieval (OWASP LLM03). |
tool_use_abuse |
Single-shot | Function call, MCP, agent, and browser tool abuse (OWASP LLM05/07/08). |
Payloads are loaded from payloads/<category>.json at runtime. Payload counts shown by list-tests are computed live from each attack class, so they stay in sync with the JSON.
The --adaptive loop in the CLI and the sidebar both call a Brain interface. Three adapters ship:
RulesOnlyBrain— fully offline. Falls back through the static payload list and analyses responses with substring indicators. No network calls. Ideal for CI.DeepSeekBrain— calls the DeepSeek API with the same prompt engineering as the sidebar.OpenAICompatibleBrain— calls any OpenAI-compatible/v1/chat/completionsendpoint (OpenAI, Together, Groq, OpenRouter, Ollama's local server, LM Studio, etc.). SetANI_LLM_BASE_URLandANI_LLM_API_KEY.
Pick the backend with ANI_LLM_BACKEND (or --llm-backend). Default is openai_compatible pointed at DeepSeek.
- Reports are written to
reports/. Formats:html,json,sarif. - Saved browser sessions are written to
sessions/(encrypted with Fernet). - Authentication profiles live in
auth_profiles/(encrypted with Fernet when created via the CLI). - Encryption keys live at
~/.ani/encryption.key(mode 0o600) or whereverANI_ENCRYPTION_KEYpoints. - Local
.env, reports, sessions, caches, and exported session JSON files are ignored by git.
SARIF 2.1.0 output (--format sarif) emits one result per vulnerable finding with the rule ID ANI-<CATEGORY>-<TEST_ID>. Levels: error for CRITICAL/HIGH, warning for MEDIUM, note for LOW/INFO. Regression/fixed metadata is included as properties. Pipe straight into GitHub Code Scanning.
Save a "known-good" run with ani scan ... --format json --output baseline.json. On a later scan, pass --baseline baseline.json and each result gets a regression, fixed, or unchanged badge in the HTML report and the corresponding property in JSON/SARIF.
Create an indicators JSON file shaped like:
{
"prompt_injection": [
{ "test_id": "PI-001", "indicators": ["custom phrase 1"], "critical_indicators": ["custom critical"] },
{ "indicators": ["category-wide indicator"] }
]
}Pass it with --indicators indicators.json. The override either matches a specific test_id or applies to the whole category.
ANI is defensive security tooling. Several layers of hardening are applied to keep your testing footprint safe:
- HTML reports use Jinja2 with
autoescapeenabled and include aContent-Security-Policymeta tag, so any AI response or payload that contains<script>or<img onerror=...>is rendered as inert text. - Sidebar results are constructed with
createElement+textContent. The XSS-proneinnerHTML = ...pattern is gone. - Chat detection is iframe- and Shadow-DOM-aware and uses Playwright's native
is_visible()so display:none ancestors don't produce false positives. - Response extraction uses a MutationObserver with text-stability detection (no more slicing the last 2500 chars of
body.innerTextand capturing prior messages). - Credentials at rest:
auth_profiles/*.jsonandsessions/*.jsonare written via Fernet (AES-128-CBC + HMAC-SHA256). Plain-JSON files written by older versions are still readable with a deprecation warning. - Network capture:
BrowserControllerredactsCookie,Authorization, andSet-Cookieheaders in its in-memory request log. - Cookie input: the CLI prefers
--cookie-fileand theANI_AUTH_COOKIEenv var over the--cookieflag (which logs a deprecation warning when used). - Canonical
Severityenum:src/detection/vulnerability.pyre-importsSeverityfromsrc/attacks/base.py. - Risk score is normalized against the whole scan (not just the findings), so a single critical finding no longer scores 100% by itself.
- Sidebar API keys are read from a module-level variable and the DOM input is cleared on each read.
The tests/ directory contains the regression suite. Run with:
python -m pytestIt covers:
- Pattern extraction (URLs, emails, API keys) and the pattern matcher's group dispatch.
VulnerabilityClassifierconstruction (catches the priorJAILbreaktypo), zero/non-zero risk score paths, score capping, and Severity re-export identity.BaseAttack.check_indicatorssubstring + case-insensitive behavior.BaseAttack.get_indicatorswith and without custom overrides.- HTML report autoescape (regression test for the XSS fix).
- Externalized payload loading for every category.
- New multi-turn attack turn counts and vulnerability aggregation.
- New RAG / tool-abuse attack payloads and indicator matching.
- LLM brain factory wiring and the rules-only brain's offline behavior.
- Adaptive CLI loop: round counting, early break on success, max-rounds respect, vulnerable marking.
- SARIF output validity, level mapping, and regression-metadata passthrough.
- Baseline diff: regression / fixed / unchanged annotation, missing-file tolerance.
- Custom indicators file loading, invalid file tolerance, override and fallback paths.
A new test fails if anyone removes autoescape from the report Jinja2 environment.
ANI is for authorized security testing only. Test systems only when you have explicit written permission from the owner, and handle captured responses, sessions, cookies, and reports as sensitive data.
Created by Abhirup Guha.
