1
0
Fork 0

feat(active): report a run of images whose order carries a value

Session 6 of the 2026 threat plan (attack row 3): A05 output was red.
CamoLeak renders a secret as one image per character from a URL alphabet;
each URL is ordinary, so each image graded LOW and the gate WARNed.

- active:markdown-image-sequence (MEDIUM): at least 4 ordinary external
  images, inline or reference-style, on one URL template whose varying
  tokens are 2 chars or fewer, or digit runs. Digits that count up in
  document order are a counter and carry nothing. The per-image grade is
  unchanged; the finding sits beside it.
- Accepted twins (flag tables, figures 1a/1b) go to review; measured
  evasions (word dictionary, 3-char symbols, counter increments, run of 3,
  a template per symbol) are in LIMITATIONS.

Guard rule before/after: FP 106/198, 196/2918, 108/389 identical. Those
corpora hold no external markdown image, so the rule was also run on 577
package READMEs (312 with external images): 0 firings at run lengths 3-8.
OKF import 47/31 identical. Matrix 140/140, ReDoS 163 patterns 0 flagged,
suite 1179 passed + 5 xfailed. Row 3: 13 caught, 1 gap, 5 open.
This commit is contained in:
Kjell Tore Guttormsen 2026-10-31 19:33:51 +01:00
commit eeb7bf07f6
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
13 changed files with 403 additions and 12 deletions

View file

@ -206,18 +206,18 @@ tier + disposition, emitted to `log.md`).
Concrete attack classes, grouped by OWASP Top 10 for LLM Applications **2026**
anchor. Numbers carry the year, OWASP's own form, because the 2026 edition
renumbered the list; a class no entry describes carries no number. Every row is
driven by a **live payload** in the coverage matrix — run it to watch all 145 pass
driven by a **live payload** in the coverage matrix — run it to watch all 146 pass
in your own environment:
```bash
python -m llm_ingestion_guard.coverage # 139/139 classes; exit 0 = all as documented
python -m llm_ingestion_guard.coverage # 140/140 classes; exit 0 = all as documented
```
| Anchor | Attack classes it stops (representative) |
|---|---|
| **LLM01:2026 · prompt injection** | 83 instruction-override lexicon classes (ignore / forget / disregard / suspend-constraints, role-play, jailbreak, …); hidden carriers — every invisible codepoint in Unicode 18.0 (zero-width, BIDI override, Unicode-tag, variation-selector and Sneaky-Bits smuggling, private use; legitimate emoji, CJK, Arabic-script, Indic and right-to-left uses exempt on context), HTML-comment, `data:` URI — stripped on input **and** re-checked at the persist gate (a comment the model leaves in its artifact is reported unless it sits in code); text hidden by styling — a font too small to read, or text coloured like its own background; high-entropy base64/hex blobs; an injection hidden inside a base64 blob (decoded, then re-scanned) |
| **LLM02:2026 · sensitive-info disclosure** | Secret egress in output — AWS keys, tokens, private keys, the credential set; a base64-*wrapped* secret (decoded → `decoded:egress:*`) |
| **LLM10:2026 · improper output handling** | Zero-click exfil carriers (EchoLeak, CVE-2025-32711) — markdown-image auto-fetch, inline / reference / autolink links, raw active HTML, standalone `data:` URIs |
| **LLM10:2026 · improper output handling** | Zero-click exfil carriers (EchoLeak, CVE-2025-32711) — markdown-image auto-fetch, inline / reference / autolink links, raw active HTML, standalone `data:` URIs; a run of images whose *order* carries a value while each URL is ordinary (CamoLeak) |
| **LLM03:2026 · excessive agency** | A tool surface on the quarantined transform; credential use beyond the per-stage allowlist; capability isolation (`scoped_env`) |
| **Fail-secure** *(the guard's own failure semantics)* | Forced-fallback attack (scan hit + transform failure → halt); compound weak-signal escalation; an un-scannable artifact fails **closed** — never a silent persist |
| **OKF structure (T1–T6)** | Body + frontmatter-value injection; YAML anchor / merge / nested / block DoS (parse-safety); non-`https` / active-scheme `resource` URLs and the `resource` allowlist; path traversal / reserved-name shadow; cross-link graph; provenance stamping |
@ -249,7 +249,7 @@ column is the part it does **not** stop. Each one was measured or is a documente
| **AML.T0143 · LLM Prompt Propagation** | Instructions that the lexicon or a carrier check sees are caught at the output gate, so they never reach the store that would pass them on | Propagation over a channel the guard does not sit on |
| **AML.T0051.001 · LLM Prompt Injection: Indirect** | The **LLM01:2026** row above | Novel phrasings with no lexicon hit; the tool-less transform carries that case |
| **AML.T0068 · LLM Prompt Obfuscation** | Invisible characters, HTML comments, a font too small to read, text coloured like the background declared on the same element, base64 and rot13 (decoded, then re-scanned) | White text whose background sits on a parent element or in a stylesheet: a plain-prose instruction hidden that way gives 0 findings |
| **AML.T0077 · LLM Response Rendering** | The **LLM10:2026** row above: markdown-image auto-fetch, links, raw active HTML | Pure beaconing to a bare path on a hostile host (a documented gap) |
| **AML.T0077 · LLM Response Rendering** | The **LLM10:2026** row above: markdown-image auto-fetch, links, raw active HTML, a run of images whose order spells a value | Pure beaconing to a bare path on a hostile host (a documented gap); an image run whose symbols are words rather than short tokens |
| **AML.T0066 · Retrieval Content Crafting** | Only the crafted content that also carries an injection | The crafted content itself — semantic gap |
**Every catalog the guard cites is on its newest edition, with the date it was
@ -328,7 +328,7 @@ a green scan means safe content. The highest-impact items:
two of the three corpora are living, so the cells are not rewritten in place.
Method and before/after: [`docs/rawhtml-census.py`](docs/rawhtml-census.py).
**Full list — 48 items, each with the mechanism, plus the out-of-scope boundary:**
**Full list — 49 items, each with the mechanism, plus the out-of-scope boundary:**
[`docs/LIMITATIONS.md`](docs/LIMITATIONS.md). Several carry field measurements from
consumer corpora, including the false positives the URL-shape rule actually produces.