injection / README.md
bogdanraduta's picture
Upload README.md with huggingface_hub
d3e334c verified
|
Raw
History Blame Contribute Delete
5.31 kB
---
license: apache-2.0
library_name: onnx
pipeline_tag: text-classification
tags:
- prompt-injection
- jailbreak-detection
- guardrails
- onnx
- multilingual
language: [bg, hr, cs, da, nl, en, et, fi, fr, de, el, hu, ga, it, lv, lt, mt, pl, pt, ro, sk, sl, es, sv, tr, az]
base_model: FacebookAI/xlm-roberta-base
---
# injection
Prompt injection and jailbreak detection over 26 languages. XLM-RoBERTa base, three
independent labels, INT8 ONNX, CPU. Built for
[`flowx-border`](https://github.com/flowx-ai/border), where it is the T2 `injection`
detector.
Labels are multi-label rather than exclusive: `direct_injection`, `indirect_injection`,
`jailbreak`. A single text can be more than one.
## What changed in this version, and why it is the headline
Three corpora in, the false positives on ordinary customer traffic are gone. Same
architecture and hyperparameters throughout; only the corpus moved.
Measured through the shipped configuration at threshold 0.43:
| | v3 | v4 | **v5, this one** |
|---|---|---|---|
| ordinary support questions it fires on | 7 of 12 | 1 of 12 | **0 of 12** |
| technical identifiers, at 0.43 | 4 of 4 | 0 of 4 | 1 of 4 |
| technical identifiers, at 0.95 | 4 of 4 | 0 of 4 | **0 of 4** |
| the three canonical attacks | 3 of 3 | 3 of 3 | **3 of 3** |
| mean per-language F1 | 0.9755 | 0.9855 | **0.9891** |
| worst language | – | mt 0.8367 | **mt 0.8817** |
v5 added one register, `mundane_account_access`, and it lives in the corpus generator's
shared mundane set so `moderation` and the five single-label classifiers inherit it too. The
gap it fills was invisible because the three registers already there are all prose *about*
things in the third person, a password reset notice or an appointment booking. None of them
was a customer speaking, so "How do I reset my password?" was out of distribution for every
corpus anchored on them, and two detectors independently learned to treat customers as
hostile.
**It is not a clean sweep, and the card should say so.** A bare UUID reads
`direct_injection` at 0.944 under v5, clearing 0.43 where v4 had it at zero. It stays below
0.95. Net across both shapes v5 is ahead and the regression is real.
The previous model read a bare UUID, a git commit hash, a data URI and a sha256 digest as
`jailbreak` or `direct_injection`, and read *"Someone is using my account, how do I lock
it?"* as `direct_injection` at 0.98. Since the detector ships `on_fail: block`, that made the
default policy refuse most of what a support assistant is asked. Both classes of false
positive came from the same corpus property: every benign register was conversational prose,
so an imperative request and a high-entropy identifier were equally out of distribution.
## Evaluation
4,327 held-out rows, threshold 0.43, max_length 96.
| label | precision | recall | F1 | FPR |
|---|---|---|---|---|
| `direct_injection` | 0.9528 | 0.9957 | 0.9738 | 0.0057 |
| `indirect_injection` | 0.9709 | 0.9901 | 0.9804 | 0.0021 |
| `jailbreak` | 0.9367 | 0.9850 | 0.9603 | 0.0077 |
Mean per-language F1 0.9891. Weakest are `mt` 0.8817, then `ga` 0.9762 and `cs` 0.9767. Maltese is not in XLM-RoBERTa's pretraining set,
and that is a fact about the base model rather than a diagnosis: the same gap in another
detector here closed entirely on corpus size alone, so read 0.8367 as a number to improve
and not as a ceiling.
## Corpus
45,541 examples: 12,285 attacks and 33,256 benign, 27.0 percent attack share, generated with
`gpt-oss:120b`. 26 languages evenly at 1,656 to 1,690 rows each. 19 registers, including `technical_identifiers` and
`technical_payload`, and four `mundane_*` registers shared with the other classifiers in
this family, of which `mundane_account_access` is v5's addition.
276 generated rows were rejected before writing: 127 placeholders, 77 duplicates, 50 in the
wrong script for their language, 22 outside their length band.
## Known weakness
A bare UUID reads `direct_injection` at 0.944, which clears the shipped 0.43 and not 0.95.
v4 had it at zero, so this is a regression on the technical shape bought alongside a fix to
the account-access one. Both are corpus properties rather than thresholds.
The account-access failure v4 could not fix is gone: *"Please cancel my subscription."* read
`direct_injection` at 0.9775 under v4, identical at 0.43 and 0.95 so no threshold reached it.
Two of 35,025 rows matched that phrasing then; v5 has 1,862 rows in the account-access
register and 16 carrying that exact phrasing as benign.
## Use it at 0.43
The library ships 0.43, above this run's calibrated 0.02. The calibration deliberately is not
adopted: its own report flags 0.02 as the lowest value in the sweep, which compresses scores
toward zero, and macro F1 is 0.9671 even at 0.95, so the sweep is a plateau rather than a
peak. A missed injection costs more than a review, so the shipped value sits well below the
plateau's top without chasing the floor.
## Export
INT8 with the Gather-only quantisation recipe. 0 of 300 decisions changed against the fp32
graph, probability drift p99 0.00005, max 0.15393, and the max is one row where the p99 is what the gate reads. `onnx/export_manifest.json` carries the
weight hashes.
## Licence
Apache-2.0. Trained on synthetic data generated for this purpose.