Metadata-Version: 2.4
Name: bastioncorpus
Version: 0.6.0
Summary: The shared prompt-injection corpus for the bastion trilogy: one canonical, versioned dataset that agentbastion (prevent), bastionprobe (attack), and bastiontrace (investigate) all pull from.
Author-email: Stefano Rizzello <rizzellostefano@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/Rinkia/bastioncorpus
Project-URL: Repository, https://github.com/Rinkia/bastioncorpus
Project-URL: Issues, https://github.com/Rinkia/bastioncorpus/issues
Keywords: llm,security,prompt-injection,ai-agent,dataset,corpus,agent-security,red-team
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Dynamic: license-file

# bastioncorpus

**The shared prompt-injection corpus for the bastion trilogy.** One canonical,
versioned dataset — attack strings, false-positive traps, taxonomy — that all
three tools pull from instead of each rolling its own.

| tool | role | depends on bastioncorpus since | reads bastioncorpus as |
|------|------|-------------------------------|------------------------|
| [agentbastion](https://github.com/Rinkia/agentbastion) | **prevent** | **v0.9.0** | SemanticDetector intent templates |
| [bastionprobe](https://github.com/Rinkia/bastionprobe) | **attack** | **v0.17.0** | fireable payloads (`load_payloads`) |
| [bastiontrace](https://github.com/Rinkia/bastiontrace) | **investigate** | **v0.2.0** | injection signatures (`analyzer._PATTERNS`) |

All three take `bastioncorpus>=0.2.0` as a dependency and keep a built-in
fallback, so one source of truth feeds prevent, attack, and investigate.

Same taxonomy, three directions. **150 rows** (99 malicious payloads / 51
benign false-positive traps; `en`/`it`/`de`/`fr`/`es`). No LLM, no cloud, no
dependencies — pure data plus a loader and three format adapters.

## Install

```bash
pip install bastioncorpus
```

## Use

Inspect:

```bash
bastioncorpus stats
bastioncorpus list --category indirect_injection
bastioncorpus validate
```

Export into each tool's native file:

```bash
bastioncorpus export --format probe    --out payloads.jsonl     # bastionprobe loads this
bastioncorpus export --format trace    --out signatures.jsonl   # bastiontrace matches these
bastioncorpus export --format semantic --out corpus.jsonl --templates templates.jsonl  # agentbastion
```

From Python:

```python
from bastioncorpus import load_corpus, to_probe, to_semantic, to_trace

rows = load_corpus()
payloads   = to_probe(rows)       # fireable attack payloads
semantic   = to_semantic(rows)    # {"templates": [...], "corpus": [...]}
signatures = to_trace(rows)       # canary-stripped substring signatures
```

## Decode and rescan

Obfuscation jailbreaks hide an instruction in an encoding the model reads fluently but a text
detector does not. Every Bastion scanner runs its checks on the decoded views too:

```python
from bastioncorpus import variants

for d in variants(text):                  # encoded runs only (cheap): what scanners use
    print(d.encoding, d.text)             # e.g. "base64", "hex>base64", "binary", "tags"
variants(user_text, transforms=True)      # + rot13, leet, reversed, spaced letters
```

- **Run-based:** base64/base64url, base32, hex, binary, ascii85, base85, Morse,
  percent-encoding, `\u`/`\x` escapes, Unicode tag characters.
- **Text filter:** decoded output is kept only if it reads like text, so tokens, hashes, keys
  and image data produce nothing.
- **Bounds:** at most 4x the input and 1 MB of output; every regex is linear.

**Limits:** made-up ciphers, acrostics and "first letter of each word" schemes are not
decodable by enumeration. Detection raises the bar; the guarantee comes from action-level
controls (allow lists on what the agent may call), which encoding cannot bypass.

## Schema

One row per attack (or benign trap). `text` may carry a single `{canary}`
placeholder the attack runner fills per run; defense and forensics adapters
strip it.

| field | meaning |
|-------|---------|
| `id` | stable unique id |
| `text` | the string (optional `{canary}` slot) |
| `category` | canonical class: `direct_injection`, `indirect_injection`, `instruction_override`, `jailbreak_persona`, `exfiltration`, `delimiter_injection`, `obfuscation`, or `benign_*` |
| `label` | `malicious` \| `benign` (benign = false-positive trap, to measure over-blocking) |
| `lang` | language code (`en`, `it`, `de`, …) — never baked into `category` |
| `tactic` | framing tactic (`authority`, `data-field`, `egress-legit`, …) |
| `channel` | where planted (`tool_output`, `user_input`) |
| `check` | how a hit is scored (`canary`, `tool`, `none`) |
| `forbidden_tool` | tool a `check=tool` payload tries to trigger |
| `severity` | 0–5 |
| `intent` | plain-language restatement, for semantic templates |

Direct/`user_input` rows are defense and forensics material; only
`tool_output` payloads with a scoring `check` are fired as attacks.

## Why

Before this, each tool hand-maintained overlapping payload lists that drifted
apart. bastioncorpus makes one of them authoritative: fix a payload here, every
tool in the trilogy gets it on the next export.

## License

MIT
