promptwl 0.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. promptwl-0.3.2/.gitignore +11 -0
  2. promptwl-0.3.2/CHANGELOG.md +125 -0
  3. promptwl-0.3.2/CONTRIBUTING.md +107 -0
  4. promptwl-0.3.2/LICENSE +21 -0
  5. promptwl-0.3.2/PKG-INFO +225 -0
  6. promptwl-0.3.2/README.md +199 -0
  7. promptwl-0.3.2/SECURITY.md +45 -0
  8. promptwl-0.3.2/examples/benign.txt +16 -0
  9. promptwl-0.3.2/examples/ensemble_demo.py +71 -0
  10. promptwl-0.3.2/examples/invisible.py +154 -0
  11. promptwl-0.3.2/examples/invisible_demo.py +88 -0
  12. promptwl-0.3.2/examples/naive_guardrail.py +68 -0
  13. promptwl-0.3.2/examples/recall_demo.py +99 -0
  14. promptwl-0.3.2/examples/string_transforms.py +193 -0
  15. promptwl-0.3.2/examples/tokenizer_probe.py +167 -0
  16. promptwl-0.3.2/manifest.json +181 -0
  17. promptwl-0.3.2/promptwl/__init__.py +94 -0
  18. promptwl-0.3.2/pyproject.toml +64 -0
  19. promptwl-0.3.2/scripts/validate.py +59 -0
  20. promptwl-0.3.2/wordlists/agents/memory-and-session-poisoning.txt +21 -0
  21. promptwl-0.3.2/wordlists/agents/tool-and-rag-injection.txt +26 -0
  22. promptwl-0.3.2/wordlists/evasion/encoding-obfuscation.txt +24 -0
  23. promptwl-0.3.2/wordlists/evasion/invisible-characters.txt +20 -0
  24. promptwl-0.3.2/wordlists/evasion/language-switching.txt +20 -0
  25. promptwl-0.3.2/wordlists/extraction/system-prompt-leak.txt +24 -0
  26. promptwl-0.3.2/wordlists/extraction/training-data-extraction.txt +22 -0
  27. promptwl-0.3.2/wordlists/injection/delimiter-confusion.txt +26 -0
  28. promptwl-0.3.2/wordlists/injection/instruction-override.txt +24 -0
  29. promptwl-0.3.2/wordlists/injection/payload-splitting.txt +23 -0
  30. promptwl-0.3.2/wordlists/jailbreak/authority-impersonation.txt +22 -0
  31. promptwl-0.3.2/wordlists/jailbreak/hypothetical-framing.txt +24 -0
  32. promptwl-0.3.2/wordlists/jailbreak/persona-override.txt +24 -0
  33. promptwl-0.3.2/wordlists/jailbreak/refusal-suppression.txt +24 -0
  34. promptwl-0.3.2/wordlists/multilingual/ar.txt +11 -0
  35. promptwl-0.3.2/wordlists/multilingual/de.txt +11 -0
  36. promptwl-0.3.2/wordlists/multilingual/es.txt +11 -0
  37. promptwl-0.3.2/wordlists/multilingual/fr.txt +11 -0
  38. promptwl-0.3.2/wordlists/multilingual/hi.txt +11 -0
  39. promptwl-0.3.2/wordlists/multilingual/it.txt +11 -0
  40. promptwl-0.3.2/wordlists/multilingual/ja.txt +11 -0
  41. promptwl-0.3.2/wordlists/multilingual/pt.txt +11 -0
  42. promptwl-0.3.2/wordlists/multilingual/ru.txt +11 -0
  43. promptwl-0.3.2/wordlists/multilingual/zh.txt +11 -0
  44. promptwl-0.3.2/wordlists/multimodal/image-and-file-injection.txt +26 -0
  45. promptwl-0.3.2/wordlists/tokens/anomalous-tokens.txt +171 -0
  46. promptwl-0.3.2/wordlists/tokens/boundary/separators.txt +36 -0
  47. promptwl-0.3.2/wordlists/tokens/control-and-artifact.txt +80 -0
  48. promptwl-0.3.2/wordlists/tokens/encoding-chains.txt +77 -0
  49. promptwl-0.3.2/wordlists/tokens/fragmentation/base-words.txt +24 -0
  50. promptwl-0.3.2/wordlists/tokens/unicode-confusables.txt +96 -0
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ .env
5
+ .DS_Store
6
+
7
+ # Python build / packaging artifacts
8
+ dist/
9
+ build/
10
+ *.egg-info/
11
+ .eggs/
@@ -0,0 +1,125 @@
1
+ # Changelog
2
+
3
+ All notable changes to `promptwl` are documented here.
4
+ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
+
6
+ ---
7
+
8
+ ## [0.3.2] — 2026-08-20
9
+
10
+ ### Added
11
+ - `pyproject.toml` — `pip install promptwl` now works. Uses hatchling;
12
+ bundles `wordlists/` and `manifest.json` into the wheel via `force-include`
13
+ so the package is fully self-contained after install.
14
+ - CI: added `package` job — builds the wheel and smoke-tests the installed
15
+ package from `/tmp` (isolated from the repo) on every push/PR.
16
+
17
+ ### Changed
18
+ - `promptwl/__init__.py`: `_ROOT` now detects whether it is running from a
19
+ pip install (wordlists bundled next to `__init__.py`) or a git clone
20
+ (wordlists at repo root). Both paths work transparently.
21
+ - `README.md`: `pip install promptwl` is now the primary quick-start; git
22
+ clone retained as the "no install" alternative.
23
+
24
+ ---
25
+
26
+ ## [0.3.1] — 2026-08-20
27
+
28
+ ### Added
29
+ - `wordlists/tokens/unicode-confusables.txt` — UTS-39 confusable character pair
30
+ table (Cyrillic, Greek, fullwidth Latin, mathematical styled Latin → ASCII
31
+ skeleton) plus confusable-substituted attack keywords as live test inputs.
32
+ - `wordlists/tokens/encoding-chains.txt` — catalog of encoding chain patterns
33
+ from Tier 1 (single transform: leet, base64, rot13, reverse, …) through
34
+ Tier 4 (chain with unknown-shift residual); documents where enumerate-and-invert
35
+ stops working and semantic defense must take over.
36
+ - Expanded `wordlists/tokens/anomalous-tokens.txt` with 51 new entries: GPT-4 /
37
+ o200k_base community outliers (2024), Llama SentencePiece special tokens,
38
+ Mistral role delimiters, cross-family model-card special tokens, BPE residue
39
+ subwords, null/DEL C0 glitch entries.
40
+ - Journal entry F-011 (confusables + encoding-chain findings).
41
+
42
+ ### Changed
43
+ - Token category: 188 → 343 phrases.
44
+ - Total corpus: 539 → 694 phrases (+29%).
45
+ - `promptwl/__init__.py` version bump to 0.3.1.
46
+ - README: updated stats example, expanded token-anomaly section with a table of
47
+ all six token corpus files, added arXiv:2411.01084 and UTS-39 to references.
48
+ - `CONTRIBUTING.md`: added token corpus format conventions, invisible-character
49
+ hygiene (F-006) scanning instructions, and expanded smoke-test checklist.
50
+ - `SECURITY.md`: expanded scope, out-of-scope, and reporting sections.
51
+ - `MASCOT.md`: added corpus version history table and journal description.
52
+
53
+ ---
54
+
55
+ ## [0.3.0] — 2026-08-20
56
+
57
+ ### Added
58
+ - `wordlists/tokens/fragmentation/base-words.txt` — 15 security-flavored seed
59
+ words for the cross-tokenizer fragmentation probe.
60
+ - `wordlists/tokens/boundary/separators.txt` — 26 separator codepoints (real,
61
+ exotic, and invisible) stored as `U+NNNN NAME` notation.
62
+ - `examples/tokenizer_probe.py` — cross-tokenizer probe: stdlib always-on
63
+ (utf8-bytes, codepoints, graphemes) + optional tiktoken / HF adapters.
64
+ Key finding: inserting an invisible ZWSP turns `"password"` from 1 token
65
+ to 3 in cl100k_base and o200k_base.
66
+ - Journal entry F-010.
67
+
68
+ ### Changed
69
+ - Total corpus: 410 → 539 phrases.
70
+ - Token category: 147 → 188 phrases.
71
+
72
+ ---
73
+
74
+ ## [0.2.1] — 2026-08-19
75
+
76
+ ### Added
77
+ - Expanded `wordlists/tokens/anomalous-tokens.txt` with a fuller public glitch
78
+ catalog (additional Rumbelow/Watkins archaeology, GPT-2/J family clusters).
79
+ - `wordlists/tokens/control-and-artifact.txt` — C0/C1 controls, bidi/Trojan
80
+ Source overrides (U+202A–U+202E), zero-width/NBSP chars, BPE residue strings.
81
+ - `examples/invisible.py` — scanner, sanitizer, and reveal utilities for invisible
82
+ codepoints (Unicode Tags, zero-width bitstream, bidi overrides).
83
+ - `examples/invisible_demo.py` — three-section demo: Tags block ASCII smuggling,
84
+ zero-width bitstream, Trojan Source bidi overrides.
85
+ - Journal entry F-009 (bidi/Trojan Source + C0/C1 controls).
86
+
87
+ ---
88
+
89
+ ## [0.2.0] — 2026-08-19
90
+
91
+ ### Added
92
+ - `wordlists/agents/` — tool-and-rag-injection, memory-and-session-poisoning.
93
+ - `wordlists/multilingual/` — 10 language files (es, fr, de, pt, it, ru, zh,
94
+ ar, hi, ja).
95
+ - `wordlists/multimodal/` — image-and-file-injection.
96
+ - `examples/string_transforms.py` — encoding transforms + decode/normalize defense
97
+ (`confusable_fold`, breadth-first `candidates`, `defended`).
98
+ - `examples/ensemble_demo.py` — scoreboard: naive filter vs. decode-defend defense
99
+ across 8 transform chains; shows residual (caesar7, homoglyph) at 0%.
100
+ - Journal entries F-005 (string-composition) through F-008 (0.2.0 expansion).
101
+
102
+ ### Changed
103
+ - Total corpus: ~200 → ~380 phrases.
104
+
105
+ ---
106
+
107
+ ## [0.1.0] — 2026-08-18
108
+
109
+ ### Added
110
+ - `wordlists/injection/` — instruction-override, delimiter-confusion,
111
+ payload-splitting.
112
+ - `wordlists/jailbreak/` — persona-override, refusal-suppression,
113
+ hypothetical-framing, authority-impersonation.
114
+ - `wordlists/extraction/` — system-prompt-leak, training-data-extraction.
115
+ - `wordlists/evasion/` — encoding-obfuscation, invisible-characters,
116
+ language-switching.
117
+ - `wordlists/tokens/` — anomalous-tokens (initial SolidGoldMagikarp cluster).
118
+ - `promptwl/` zero-dependency Python loader (`load`, `phrases`, `categories`,
119
+ `stats`, `Entry`).
120
+ - `examples/naive_guardrail.py` — caricature English keyword blocklist.
121
+ - `examples/recall_demo.py` — scores the naive filter; headline: 55% English,
122
+ 0% translated.
123
+ - `manifest.json`, `scripts/validate.py`, CI workflow.
124
+ - `MASCOT.md`, `assets/parrot.svg`, `CONTRIBUTING.md`, `SECURITY.md`.
125
+ - Journal entries F-001 (monolingual blind spot) through F-004 (Pliny survey).
@@ -0,0 +1,107 @@
1
+ # Contributing to promptwl 🦜
2
+
3
+ Thanks for helping keep Parrot's notebook current — attacks on LLMs evolve fast,
4
+ and a maintained wordlist only stays useful if defenders keep it fresh.
5
+
6
+ ## Ground rules
7
+
8
+ 1. **Defensive framing only.** Contribute *patterns* that are already publicly
9
+ documented (research papers, OWASP LLM Top 10, open-source scanners). Do **not**
10
+ submit novel weaponized exploits, working end-to-end attack chains, or
11
+ step-by-step instructions for causing real-world harm.
12
+ 2. **Pattern level, not payload level.** Prefer the reusable shape of an attack
13
+ (e.g. `ignore all previous instructions`) over a specific targeted exploit against
14
+ a named production system.
15
+ 3. **Cite your source.** Add a `# source-level:` comment to any new file, or note
16
+ the reference in your PR. A link to where a pattern is documented keeps the
17
+ project credible and reviewers happy.
18
+ 4. **A finding isn't done without a mitigation.** If you add a new attack class,
19
+ add a `# defense:` comment in the file header too.
20
+
21
+ ## File format
22
+
23
+ **Standard wordlist** — one phrase per line; `#` lines are metadata/comments and
24
+ are skipped by the loader:
25
+
26
+ ```
27
+ # category: injection / my-new-file
28
+ # purpose: one sentence on what these patterns test
29
+ # use: what a defender does with them
30
+ # source-level: OWASP LLM01, arXiv:XXXX.XXXXX, ...
31
+ # defense: what stops this attack class
32
+
33
+ phrase one
34
+ phrase two
35
+ # subsection comment — skipped by loader
36
+ phrase three
37
+ ```
38
+
39
+ **Token corpus files** — two additional conventions apply:
40
+
41
+ - Invisible or control characters **must** be stored as `U+NNNN NAME` notation,
42
+ never as literal bytes. This prevents silent corruption and keeps the file
43
+ diffable. Example: `U+200B ZERO WIDTH SPACE`, not a literal ZWSP.
44
+ - Confusable character tables use `U+NNNN SCRIPT NAME → U+NNNN ASCII NAME` pairs
45
+ (see `wordlists/tokens/unicode-confusables.txt` Part A for the format).
46
+ Confusable-substituted test strings (Part B) may contain literal non-ASCII chars
47
+ because those are visible and are the actual test input.
48
+
49
+ ## Invisible-character hygiene (F-006)
50
+
51
+ **Before you open a PR**, scan your changes for accidental invisible codepoints.
52
+ The scanner is built into the repo:
53
+
54
+ ```bash
55
+ python3 -c "
56
+ from examples.invisible import scan
57
+ from pathlib import Path
58
+ hits = []
59
+ for f in Path('wordlists').rglob('*.txt'):
60
+ findings = scan(f.read_text(encoding='utf-8'))
61
+ if findings:
62
+ hits.append((f, findings))
63
+ for f, findings in hits:
64
+ print(f'{f}: {len(findings)} invisible chars')
65
+ "
66
+ ```
67
+
68
+ If any non-token file shows hits, replace the literal chars with `U+NNNN NAME`
69
+ notation or remove them.
70
+
71
+ ## Adding entries
72
+
73
+ - Add lines to an existing file under `wordlists/<category>/`, or create a new
74
+ file with a `#`-comment header (see existing files for the format).
75
+ - If you add a **new file**, register it in `manifest.json` with a `path`, `title`,
76
+ and `description`. If you add a **new category**, add a full category block.
77
+
78
+ ## Validate before you open a PR
79
+
80
+ ```bash
81
+ python3 scripts/validate.py
82
+ ```
83
+
84
+ Every file listed in `manifest.json` must exist and load without error. Also run
85
+ the smoke-test suite:
86
+
87
+ ```bash
88
+ python3 examples/recall_demo.py
89
+ python3 examples/ensemble_demo.py
90
+ python3 examples/invisible_demo.py
91
+ python3 examples/tokenizer_probe.py
92
+ ```
93
+
94
+ All four should exit cleanly (exit code 0).
95
+
96
+ ## What makes a good contribution
97
+
98
+ - New **languages / translations** of existing patterns — multilingual guardrails
99
+ are widely under-tested.
100
+ - New **categories** tracking emerging attack surfaces: agent tool-call injection,
101
+ RAG poisoning, multimodal prompt injection, voice-interface injection.
102
+ - New **token corpus entries**: publicly-documented glitch tokens, confusable
103
+ character pairs, encoding chain patterns, or boundary separators not yet listed.
104
+ - **Citations** — a link to where a pattern is documented in public research or
105
+ OWASP makes the contribution much easier to review.
106
+
107
+ Squawk responsibly. 🦜 — Parrot
promptwl-0.3.2/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 promptwl contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,225 @@
1
+ Metadata-Version: 2.5
2
+ Name: promptwl
3
+ Version: 0.3.2
4
+ Summary: A red-team & guardrail wordlist for LLMs. SecLists, but for language models.
5
+ Project-URL: Homepage, https://github.com/parrot-r/wordlist
6
+ Project-URL: Bug Tracker, https://github.com/parrot-r/wordlist/issues
7
+ Project-URL: Changelog, https://github.com/parrot-r/wordlist/blob/main/CHANGELOG.md
8
+ Author: Parrot
9
+ Maintainer: Parrot
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: eval,guardrail,jailbreak,llm,prompt-injection,red-team,security,wordlist
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Security
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/markdown
26
+
27
+ <p align="center">
28
+ <img src="assets/parrot.svg" alt="Parrot, the promptwl mascot" width="180">
29
+ </p>
30
+
31
+ <h1 align="center">promptwl 🦜</h1>
32
+
33
+ <p align="center"><strong>A red-team &amp; guardrail wordlist for LLMs. SecLists, but for language models.</strong></p>
34
+
35
+ <p align="center">
36
+ <a href="https://owasp.org/www-project-top-10-for-large-language-model-applications/"><img alt="OWASP LLM Top 10" src="https://img.shields.io/badge/OWASP-LLM%20Top%2010-blue"></a>
37
+ <img alt="phrases" src="https://img.shields.io/badge/phrases-690%2B-e4572e">
38
+ <img alt="languages" src="https://img.shields.io/badge/languages-11-845d41">
39
+ <img alt="license" src="https://img.shields.io/badge/license-MIT-green">
40
+ <img alt="intent" src="https://img.shields.io/badge/intent-defensive-9aa0a6">
41
+ </p>
42
+
43
+ ---
44
+
45
+ > A parrot repeats what it hears without understanding it. So does a language model
46
+ > handed the wrong input. This repo is [**Parrot**](MASCOT.md)'s field notebook of every
47
+ > phrase that makes a model repeat something it never should have.
48
+
49
+ Every pentester `git clone`s [SecLists](https://github.com/danielmiessler/SecLists) on day one of an engagement. There's no clean, versioned, machine-loadable equivalent for the LLM era — so as agents, tool-use, and RAG spread and **prompt injection sits at the top of the [OWASP LLM Top 10](https://owasp.org/www-project-top-10-for-large-language-model-applications/) (LLM01)**, everyone re-scrapes the same patterns from scattered blog posts.
50
+
51
+ `promptwl` is that missing corpus: categorized, defensively-framed wordlists of the *patterns* attackers use against language models — ready to load into your guardrail evals, red-team runs, and CI.
52
+
53
+ ```python
54
+ import promptwl
55
+
56
+ for entry in promptwl.load(): # 530+ phrases, with metadata
57
+ verdict = my_guardrail(entry.text) # your classifier under test
58
+ assert verdict == "block", f"missed: {entry.text!r} ({entry.category})"
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Who this is for
64
+
65
+ | You are a… | You use promptwl to… |
66
+ |---|---|
67
+ | 🛡️ **AI developer** | Benchmark your input filter / guardrail's recall, regression-test it on every model bump, and wire a known-attack corpus into CI. |
68
+ | 🔴 **Security researcher** | Bootstrap an LLM red-team engagement with an organized starting corpus instead of scraping blog posts. |
69
+ | 🧪 **Eval / safety engineer** | Build reproducible refusal-robustness and injection-resistance benchmarks. |
70
+
71
+ ## What's inside
72
+
73
+ | Category | OWASP | What it covers |
74
+ |---|---|---|
75
+ | `injection/` | LLM01 | Instruction override, delimiter/role-boundary confusion, multi-turn payload splitting |
76
+ | `jailbreak/` | LLM01 | Persona overrides, refusal suppression, hypothetical/fictional framing, authority impersonation |
77
+ | `extraction/` | LLM07 | System-prompt & config leak probes, training-data / memorization probes |
78
+ | `evasion/` | LLM01 | Encoding, homoglyph, spacing, invisible-character, and cross-language obfuscation |
79
+ | `agents/` | LLM01 | **Indirect** injection in docs, tool output, web, email; persistent-memory poisoning |
80
+ | `multilingual/` | LLM01 | Core override/jailbreak patterns in 10 languages — because English-only filters silently fail |
81
+ | `multimodal/` | LLM01 | Instructions hidden in images, alt-text, file metadata, or document layers read by vision pipelines |
82
+ | `tokens/` | — | Anomalous / "glitch" tokens, control characters, and bidi (Trojan Source) artifacts for tokenizer robustness |
83
+
84
+ Each `.txt` is one phrase per line; lines starting with `#` are metadata/comments. Load them however you like — plain `grep`, `cat`, or the zero-dependency Python package.
85
+
86
+ ## Quick start
87
+
88
+ **pip install** — zero dependencies, standard library only:
89
+
90
+ ```bash
91
+ pip install promptwl
92
+ ```
93
+
94
+ ```python
95
+ import promptwl
96
+
97
+ promptwl.categories() # ['injection', 'jailbreak', 'extraction', ...]
98
+ promptwl.stats() # {'injection': 57, ..., 'total': 694}
99
+ promptwl.phrases("multilingual") # non-English patterns for one category
100
+ promptwl.phrases("agents") # list[str] for one category
101
+ for e in promptwl.load(): # Entry(text, category, file, title)
102
+ ...
103
+ ```
104
+
105
+ **Or no install** — the corpus is plain text files if you just want to grep:
106
+
107
+ ```bash
108
+ git clone https://github.com/parrot-r/wordlist
109
+ cat wordlists/injection/*.txt
110
+ ```
111
+
112
+ ## Example: score a guardrail's recall
113
+
114
+ ```python
115
+ import promptwl
116
+
117
+ def evaluate(guardrail) -> float:
118
+ """Fraction of known-attack phrases the guardrail flags."""
119
+ entries = list(promptwl.load())
120
+ caught = sum(1 for e in entries if guardrail(e.text) == "block")
121
+ return caught / len(entries)
122
+
123
+ print(f"recall: {evaluate(my_guardrail):.1%}")
124
+ ```
125
+
126
+ Pair it with your own benign corpus to measure false-positive rate, and you have a two-sided guardrail benchmark.
127
+
128
+ ## See it yourself (30-second demo)
129
+
130
+ The repo ships a runnable demo: a deliberately naive — but realistic — English keyword filter, scored against the whole corpus.
131
+
132
+ ```bash
133
+ python3 examples/recall_demo.py
134
+ ```
135
+
136
+ ```
137
+ HEADLINE — same phrasing, English vs translated
138
+ ----------------------------------------------------
139
+ English (injection etc.) 55% █████████████···········
140
+ The same, translated 0% ························
141
+
142
+ → the filter catches ~55% of these attacks in English
143
+ and ~0% the moment the attacker switches language.
144
+ ```
145
+
146
+ The same filter also scores **0%** on encoding evasion, indirect/agent injection, and glitch tokens — it can only see the English phrasings it was written for. That's the whole point: **you can't fix what you don't measure.** Wire promptwl into CI and watch the number.
147
+
148
+ A second demo pits **string-composition obfuscation against a decode/normalize defense**:
149
+
150
+ ```bash
151
+ python3 examples/ensemble_demo.py
152
+ ```
153
+
154
+ Encodings (leetspeak, Base64, ROT13, reversal, chains) drop the filter to 0%; an iterative decode + Unicode-normalize pre-pass fully recovers the *known* transforms — while a Caesar-7 shift and Cyrillic homoglyphs stay at 0%, showing exactly where enumerate-and-invert ends and semantic defense has to start.
155
+
156
+ A third demo catches **ASCII smuggling** — invisible payloads hidden in zero-width characters and the Unicode Tags block:
157
+
158
+ ```bash
159
+ python3 examples/invisible_demo.py
160
+ ```
161
+
162
+ A `"lgtm, minor cleanup"` commit message that carries 28 invisible codepoints decoding to a hidden instruction — `grep` finds nothing, the scanner flags all 28, reveals the text, and strips it. Sanitize on **ingestion and egress**: the footprint channel (payloads smuggled into an agent's own commits/comments) is the half most pipelines forget.
163
+
164
+ ## The token-anomaly corpus (broader than garak)
165
+
166
+ `tokens/` goes well beyond a list of scary Unicode. It covers six distinct surfaces, each tested with its own probe or corpus:
167
+
168
+ | File | What it tests |
169
+ |---|---|
170
+ | `anomalous-tokens.txt` | 141 publicly-known glitch / under-trained tokens across GPT, Llama, Mistral, and open families |
171
+ | `control-and-artifact.txt` | C0/C1 control chars, bidi overrides (Trojan Source), BPE residue strings |
172
+ | `unicode-confusables.txt` | UTS-39 confusable pair table + confusable-substituted attack keywords (Cyrillic, Greek, fullwidth Latin) |
173
+ | `encoding-chains.txt` | Tier 1–4 encoding chain catalog — from single base64 to multi-layer chains with unknown-shift residuals |
174
+ | `fragmentation/base-words.txt` | Seed words the cross-tokenizer probe fragments to measure boundary-shift |
175
+ | `boundary/separators.txt` | 26 real, exotic, and invisible separator codepoints that shift apparent token boundaries |
176
+
177
+ The cross-tokenizer probe answers **"what happens to this string at the tokenizer level?"** — where does each tokenizer place a boundary, and how does an invisible insertion change the split? Zero-dependency by default; `tiktoken` / `transformers` adapters auto-activate if installed.
178
+
179
+ ```bash
180
+ python3 examples/tokenizer_probe.py
181
+ ```
182
+
183
+ ```
184
+ base word: 'password'
185
+ variant utf8-bytes codepoints graphemes cl100k_base o200k_base
186
+ plain 8 8 8 1 1
187
+ +zero_width 11* 9* 9* 3* 3* ← invisible → 1 token becomes 3
188
+ +combining 10* 9* 8 2* 2* ← mark rides the grapheme (grapheme count unchanged)
189
+ ```
190
+
191
+ An invisible zero-width space a reviewer can't see turns a **1-token** word into **3 tokens**. A Cyrillic lookalike for `a` (U+0430) fools ASCII-only filters — apply UTS-39 confusable-fold before matching. A two-layer `leet+base64` chain defeats keyword filters that only look one level deep. These are the gaps garak's filter-side `badchars` probe doesn't measure — normalize before you tokenize *and* before you filter.
192
+
193
+ ## Scope & ethics
194
+
195
+ This is a **defensive** project. It catalogs *patterns that are already publicly documented* (OWASP LLM Top 10, published red-team research, open-source tools like garak and Giskard) and organizes them so defenders can build and test filters, guardrails, and evals.
196
+
197
+ - ✅ Building and benchmarking input/output guardrails
198
+ - ✅ Red-teaming **systems you are authorized to test**
199
+ - ✅ Reproducible safety and robustness evals
200
+ - ❌ Attacking systems you don't own or have permission to test
201
+
202
+ It intentionally does **not** ship novel weaponized exploits or step-by-step instructions for causing harm. See [`SECURITY.md`](SECURITY.md).
203
+
204
+ ## Contributing
205
+
206
+ New patterns, categories, and translations are welcome — the whole point is to keep pace with how attacks on AI evolve. See [`CONTRIBUTING.md`](CONTRIBUTING.md). Keep entries at the **pattern** level, defensively framed, and add them to `manifest.json`.
207
+
208
+ ## The mascot
209
+
210
+ This project is kept by **Parrot** 🦜 — an African Grey coding agent whose whole
211
+ existence is the punchline: *if Parrot can repeat it, so can your model.* Meet the
212
+ character in [`MASCOT.md`](MASCOT.md).
213
+
214
+ ## License
215
+
216
+ [MIT](LICENSE). Use it freely; a star helps Parrot find a bigger flock. ⭐🦜
217
+
218
+ ## Prior art & references
219
+
220
+ - [OWASP Top 10 for LLM Applications](https://owasp.org/www-project-top-10-for-large-language-model-applications/)
221
+ - [SecLists](https://github.com/danielmiessler/SecLists) — the inspiration for the format
222
+ - [garak](https://github.com/NVIDIA/garak) — LLM vulnerability scanner
223
+ - Rumbelow & Watkins (2023), *SolidGoldMagikarp* — anomalous tokens
224
+ - Levi et al. (2024), arXiv:2411.01084 — string-composition attacks on LLM filters
225
+ - Unicode Consortium, [UTS-39](https://unicode.org/reports/tr39/) — confusable character detection