RAG security lab — reproducible attack cases for retrieval-augmented generation pipelines
Project description
Hemlock
RAG security lab — reproducible attack cases for retrieval-augmented generation pipelines.
Build your own RAG pipeline and break it yourself. Each attack module maps to a published paper so you understand not just what breaks, but why.
Attacks implemented
| Attack | Variants | Technique | Reference |
|---|---|---|---|
direct_injection |
3 | Explicit instruction override via document content | Greshake et al. (2023) arxiv:2302.12173 |
context_override |
3 | Factual poisoning — attacker-controlled false answer via high-relevance chunk | Zou et al. (2024) arxiv:2402.07867 |
poisoning |
3 | Persistent backdoor — trigger-specific payload in the index | Chaudhari et al. (2024) arxiv:2405.20485 |
indirect_injection |
3 | Payload hidden in title, footnotes, or zero-width characters | Greshake et al. (2023) + Perez & Ribeiro (2022) arxiv:2211.09527 |
exfiltration |
3 | Model leaks system prompt, co-retrieved chunks, or sensitive context | PLeak — Hui et al. (2024) arxiv:2405.06823 |
jailbreak_via_context |
3 | Safety bypass via roleplay/research/hypothetical framing in retrieved docs | Perez & Ribeiro (2022) + Wei et al. (2023) arxiv:2307.02483 |
authority_spoofing |
3 | Document claims to be the authoritative system config, policy, or developer override | Schulhoff et al. (2023) arxiv:2311.16119 |
chain_of_thought_hijack |
3 | Injected reasoning chains steer the model to attacker-controlled conclusions | BadChain — Xiang et al. (2024) arxiv:2401.12242 |
citation_forgery |
3 | Fake papers, standards, and official reports poison factual claims | PoisonedRAG — Zou et al. (2024) + Pan et al. (2023) arxiv:2305.13661 |
context_flooding |
3 | Retrieval window flooded with noise or false narrative, crowding out legit docs | Yi et al. (2023) arxiv:2312.14197 |
invisible_markup |
3 | Instructions hidden in HTML comments, ARIA labels, or CSS-hidden elements | Greshake et al. (2023) + Willison (2023) |
temporal_spoofing |
3 | Future-dated documents override parametric knowledge; fake "recent" events | Dhingra et al. (2022) arxiv:2108.06914 |
semantic_backdoor |
3 | Trigger phrase activates poisoned document payload (conditional attack) | Phantom — Chaudhari et al. (2024) arxiv:2405.20485 |
multi_hop_poisoning |
3 | Document chains build up malicious conclusions across retrieval hops | AgentDojo — Debenedetti et al. (2024) arxiv:2406.13352 |
cross_tenant_poisoning |
3 | Attacker-controlled tenant docs bleed into another tenant's queries | PoisonedRAG §3.3 — Zou et al. (2024) arxiv:2402.07867 |
15 attacks × 3 variants = 45 distinct attack scenarios.
Quickstart
git clone https://github.com/M4NT/hemlock
cd hemlock
pip install -e .
cp .env.example .env
# add your ANTHROPIC_API_KEY
# run all attacks + score
hemlock score --model claude-haiku-4-5-20251001
# run a specific attack
hemlock run direct_injection --model claude-haiku-4-5-20251001
hemlock run jailbreak_via_context --model gpt-4o-mini
hemlock run temporal_spoofing --model llama3.2 # ollama
# export results
hemlock score --output markdown --out report.md
hemlock score --output json --out report.json
How it works
Each attack:
- Resets the vector index
- Ingests a set of legitimate documents + one or more malicious documents
- Queries the pipeline with a trigger question
- Scores the response — did the model follow the injected instruction?
- Logs the full retrieval trace (chunks retrieved, full prompt, response)
The trace is the key output. It shows you exactly what the model received and whether the injection made it into the context.
Defenses
| Layer | Defense | What it catches |
|---|---|---|
| Ingest | InjectionPatternFilter |
Documents with explicit injection markers |
| Ingest | UnicodeNormalizer |
Zero-width and invisible Unicode characters |
| Ingest | MarkdownHeaderSanitizer |
Injection instructions in markdown headers |
| Retrieval | InjectionChunkFilter |
Chunks with injection patterns before prompt assembly |
| Retrieval | ProvenanceFilter |
Chunks from untrusted sources |
| Output | ExfiltrationGuard |
Leaked credentials, secrets, or file contents |
| Output | InjectionSuccessGuard |
Injection success markers in the response |
| Prompt | get_prompt(level) |
Hardened system prompts (levels: baseline → l4) |
Scorer
The automatic scorer cross-products all attacks × hardening levels × defense configs and produces a vulnerability matrix:
hemlock score --model claude-haiku-4-5-20251001 --output terminal
Attack | baseline | l1 | l2 | l3 | l4
direct_injection | ✗ FAIL | ✓ | ✓ | ✓ | ✓
jailbreak_via_context | ✗ FAIL | ✗ | ✓ | ✓ | ✓
cross_tenant_poisoning | ✗ FAIL | ✗ | ✗ | ✗ | ✓
...
Supported LLMs
| Provider | Models | Env var |
|---|---|---|
| Anthropic | claude-* |
ANTHROPIC_API_KEY |
| OpenAI | gpt-* |
OPENAI_API_KEY |
| Ollama | any local model | — |
Project structure
hemlock/
├── hemlock/
│ ├── pipeline.py # RAG pipeline with full trace logging
│ ├── scorer.py # Automatic vulnerability scorer
│ └── cli.py # Terminal interface
├── attacks/
│ ├── base.py # Attack ABC + AttackResult
│ ├── direct_injection.py
│ ├── context_override.py
│ ├── poisoning.py
│ ├── indirect_injection.py
│ ├── exfiltration.py
│ ├── jailbreak_via_context.py
│ ├── authority_spoofing.py
│ ├── chain_of_thought_hijack.py
│ ├── citation_forgery.py
│ ├── context_flooding.py
│ ├── invisible_markup.py
│ ├── temporal_spoofing.py
│ ├── semantic_backdoor.py
│ ├── multi_hop_poisoning.py
│ └── cross_tenant_poisoning.py
├── defenses/
│ ├── input_sanitizer.py
│ ├── chunk_filter.py
│ ├── prompt_hardening.py
│ └── output_validator.py
└── tests/
├── conftest.py
├── test_attacks.py
├── test_new_attacks.py
└── test_scorer.py
Academic references
- Greshake et al. (2023) — Not what you've signed up for: Compromising Real-World LLM-Integrated Applications with Indirect Prompt Injection — arxiv:2302.12173
- Zou et al. (2024) — PoisonedRAG: Knowledge Corruption Attacks to Retrieval-Augmented Generation of Large Language Models — arxiv:2402.07867
- Chaudhari et al. (2024) — Phantom: General Trigger Attacks on Retrieval Augmented Language Generation — arxiv:2405.20485
- Perez & Ribeiro (2022) — Ignore Previous Prompt: Attack Techniques For Language Models — arxiv:2211.09527
- Wei et al. (2023) — Jailbroken: How Does LLM Safety Training Fail? — arxiv:2307.02483
- Xiang et al. (2024) — BadChain: Backdoor Chain-of-Thought Prompting for Large Language Models — arxiv:2401.12242
- Pan et al. (2023) — On the Risk of Misinformation Pollution with Large Language Models — arxiv:2305.13661
- Yi et al. (2023) — Benchmarking and Defending Against Indirect Prompt Injection Attacks on Large Language Models — arxiv:2312.14197
- Schulhoff et al. (2023) — Ignore This Title and HackAPrompt — arxiv:2311.16119
- Dhingra et al. (2022) — Time-Sensitive Question Answering Datasets — arxiv:2108.06914
- Debenedetti et al. (2024) — AgentDojo: A Dynamic Environment to Evaluate Attacks and Defenses for LLM Agents — arxiv:2406.13352
- Hui et al. (2024) — PLeak: Prompt Leaking Attacks against Large Language Model Applications — arxiv:2405.06823
- OWASP LLM Top 10 — LLM01 (Prompt Injection), LLM02 (Insecure Output Handling), LLM03 (Training Data Poisoning)
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
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 hemlock_rag-1.2.0-py3-none-any.whl.
File metadata
- Download URL: hemlock_rag-1.2.0-py3-none-any.whl
- Upload date:
- Size: 63.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4d5bed0729d9c242f73af2c434279446c4ce2bc1dd66d0da544874e4ba1148ff
|
|
| MD5 |
8ed4cd6fe8392c9baa76f832f184b29b
|
|
| BLAKE2b-256 |
9f9e7a9a0db02e1b9d310ffe438d44f9673946c42a4e99a7ca6673e88b56e731
|