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.
- promptwl-0.3.2/.gitignore +11 -0
- promptwl-0.3.2/CHANGELOG.md +125 -0
- promptwl-0.3.2/CONTRIBUTING.md +107 -0
- promptwl-0.3.2/LICENSE +21 -0
- promptwl-0.3.2/PKG-INFO +225 -0
- promptwl-0.3.2/README.md +199 -0
- promptwl-0.3.2/SECURITY.md +45 -0
- promptwl-0.3.2/examples/benign.txt +16 -0
- promptwl-0.3.2/examples/ensemble_demo.py +71 -0
- promptwl-0.3.2/examples/invisible.py +154 -0
- promptwl-0.3.2/examples/invisible_demo.py +88 -0
- promptwl-0.3.2/examples/naive_guardrail.py +68 -0
- promptwl-0.3.2/examples/recall_demo.py +99 -0
- promptwl-0.3.2/examples/string_transforms.py +193 -0
- promptwl-0.3.2/examples/tokenizer_probe.py +167 -0
- promptwl-0.3.2/manifest.json +181 -0
- promptwl-0.3.2/promptwl/__init__.py +94 -0
- promptwl-0.3.2/pyproject.toml +64 -0
- promptwl-0.3.2/scripts/validate.py +59 -0
- promptwl-0.3.2/wordlists/agents/memory-and-session-poisoning.txt +21 -0
- promptwl-0.3.2/wordlists/agents/tool-and-rag-injection.txt +26 -0
- promptwl-0.3.2/wordlists/evasion/encoding-obfuscation.txt +24 -0
- promptwl-0.3.2/wordlists/evasion/invisible-characters.txt +20 -0
- promptwl-0.3.2/wordlists/evasion/language-switching.txt +20 -0
- promptwl-0.3.2/wordlists/extraction/system-prompt-leak.txt +24 -0
- promptwl-0.3.2/wordlists/extraction/training-data-extraction.txt +22 -0
- promptwl-0.3.2/wordlists/injection/delimiter-confusion.txt +26 -0
- promptwl-0.3.2/wordlists/injection/instruction-override.txt +24 -0
- promptwl-0.3.2/wordlists/injection/payload-splitting.txt +23 -0
- promptwl-0.3.2/wordlists/jailbreak/authority-impersonation.txt +22 -0
- promptwl-0.3.2/wordlists/jailbreak/hypothetical-framing.txt +24 -0
- promptwl-0.3.2/wordlists/jailbreak/persona-override.txt +24 -0
- promptwl-0.3.2/wordlists/jailbreak/refusal-suppression.txt +24 -0
- promptwl-0.3.2/wordlists/multilingual/ar.txt +11 -0
- promptwl-0.3.2/wordlists/multilingual/de.txt +11 -0
- promptwl-0.3.2/wordlists/multilingual/es.txt +11 -0
- promptwl-0.3.2/wordlists/multilingual/fr.txt +11 -0
- promptwl-0.3.2/wordlists/multilingual/hi.txt +11 -0
- promptwl-0.3.2/wordlists/multilingual/it.txt +11 -0
- promptwl-0.3.2/wordlists/multilingual/ja.txt +11 -0
- promptwl-0.3.2/wordlists/multilingual/pt.txt +11 -0
- promptwl-0.3.2/wordlists/multilingual/ru.txt +11 -0
- promptwl-0.3.2/wordlists/multilingual/zh.txt +11 -0
- promptwl-0.3.2/wordlists/multimodal/image-and-file-injection.txt +26 -0
- promptwl-0.3.2/wordlists/tokens/anomalous-tokens.txt +171 -0
- promptwl-0.3.2/wordlists/tokens/boundary/separators.txt +36 -0
- promptwl-0.3.2/wordlists/tokens/control-and-artifact.txt +80 -0
- promptwl-0.3.2/wordlists/tokens/encoding-chains.txt +77 -0
- promptwl-0.3.2/wordlists/tokens/fragmentation/base-words.txt +24 -0
- promptwl-0.3.2/wordlists/tokens/unicode-confusables.txt +96 -0
|
@@ -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.
|
promptwl-0.3.2/PKG-INFO
ADDED
|
@@ -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 & 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
|