1
0
Fork 0

feat(output): report hidden markup — tiny font, invisible colour, comments outside code

Session 5 of the 2026 threat plan (attack row 3): A04 and A15 were red.

- html-obfuscation:tiny-font (HIGH): font below 4px. ShadowLeak's 1px passed
  because the commons zero-font row only matches a leading 0.
- html-obfuscation:invisible-colour (HIGH): text within 8 per channel of the
  background in the same style attribute, or transparent. White alone is not
  a signal (email buttons); measured residual in LIMITATIONS.
- output:html-comment-present (MEDIUM): a comment in the artifact outside
  fenced code and inline code. 253/254 comments in the reference corpus sit
  in code.

Local to output.py; the vendored lexicon stays byte-equal with commons.
Guard rule before/after: FP 106/198, 196/2918, 108/389 identical; OKF 6/53
and 47/31 identical. Matrix 139/139, ReDoS 161 patterns 0 flagged, suite
1142 passed + 6 xfailed. Row 3: 12 caught, 1 gap, 6 open.
This commit is contained in:
Kjell Tore Guttormsen 2026-10-31 17:01:39 +01:00
commit 0ebe8abdbd
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
11 changed files with 524 additions and 15 deletions

View file

@ -202,6 +202,36 @@ items; this is the full list, each with the mechanism.
is disproportionate for a dev showcase, and the OCR/stego it would smuggle is
already out of scope). One known gap: the numeric `-`/`+` CSV false positive (a
typed XLSX numeric cell does not trip it).
- **Hidden styling is judged from the element's own `style` attribute, and white
text alone is not a signal.** `html-obfuscation:tiny-font` reports a font below
4px (`HIDDEN_FONT_MAX_PX`; pt, em, rem and % converted at 16px per em), and
`html-obfuscation:invisible-colour` reports text within 8 per channel of the
background declared in the *same* attribute, or transparent. A background on a
parent element, a `<style>` class, or a colour reached by inheritance is not
seen. White text is everyday HTML email (white on a dark button, with the
button colour on the `<td>`), so it is not flagged by itself. Measured
2026-10-31: an instruction in plain prose styled only `color:#ffffff` gives
**0 findings**. ShadowLeak's form is caught because it also used a 1px font.
A size written with a leading `0` is left to the vendored commons row
`html-obfuscation:zero-font`, so one element is not reported twice. That row
also matches readable sizes such as `font-size:0.9em` and misses `.2em`; the
regex belongs to commons and has been reported there. No inline font-size
below 8px and no same-colour pair occurs in the 3 505 benign documents or the
78 OKF concepts, but those corpora carry almost no inline styling (5 documents),
so that 0 says little about HTML-heavy sources.
- **A comment shown as code is not reported at the persist gate, and that
exemption assumes markdown.** `output:html-comment-present` (MEDIUM, like
`sanitize:html-comment` on the input side) counts each `<!-- -->` outside a
fenced code block or an inline code span, because a renderer shows those as
code. Measured on the reference corpus: 253 of its 254 comments sit in code
(XML policy samples), and the one that does not is an attack example written
inline in a red-team document. In an HTML artifact a backtick is not code, so
a comment wrapped in backticks there is hidden yet not reported. A code span
across lines is not recognised (the comment is reported), and neither is an
indented code block (a comment there is reported although it renders). The
input side is unchanged: it strips every comment, code samples included.
- **Lexicon findings are deduplicated by pattern id** — `count=1` and the first
offset are reported, so a class matched across several channels collapses to one
finding at its first location: a deliberate readability tradeoff.
@ -602,7 +632,7 @@ items; this is the full list, each with the mechanism.
missed; on one preset it is held for review instead of halted.
- **The ReDoS sweep has a measured sensitivity floor, not a clean bill of
health.** All 154 compiled patterns across all eleven regex-bearing modules are
health.** All 161 compiled patterns across all eleven regex-bearing modules are
swept arm by arm — payloads synthesised per run from each pattern's own
skeleton, so `[`, `[system]` and `[system](` are each probed separately rather
than relying on generic units, and each pattern is timed in the call mode the
@ -778,7 +808,8 @@ fails the test, forcing this doc to be updated:
emoji, on ordinary first-party content. The joiner is now judged by context
instead: exempt only when **both** neighbours are emoji-context codepoints,
measured to cover 122/122 of the codepoints adjacent to a ZWJ across Unicode
18.0's 1614 RGI sequences (re-measured 2026-10-31; 17.0 gave the same). A joiner placed *between two emoji* is therefore
18.0's 1614 RGI sequences (re-measured 2026-10-31; 17.0 gave the same). A
joiner placed *between two emoji* is therefore
invisible to the carrier check and could carry a covert channel — it costs an
emoji per bit and cannot split a word, which is the shape the word-splitting
attack needs, so the narrowing is deliberate rather than complete. *(Until

View file

@ -259,6 +259,14 @@ tas i økt 2:*
- **Økt 5, skjult markup:** A04 (tekst i 1px og hvitt; `zero-font` krever en
størrelse som starter på 0) og A15 utgang (HTML-kommentar i modellens output gir 0
funn).
*Gjort 31.10:* A04 og A15 (begge ankomster) fanges, så rad 3 har nå **12 fanget,
1 hull og 6 røde** (A05, A08, A09, A12, A16, A19). `html-obfuscation:tiny-font`
(under 4px) og `html-obfuscation:invisible-colour` (samme farge som bakgrunnen i
samme attributt, eller gjennomsiktig) bor i `output.py`, fordi leksikonet er
byte-likt med commons. Hvit tekst alene er bevisst ikke et signal (e-postknapper).
Det er målt at ren prosa i hvitt gir 0 funn, og det er ført i LIMITATIONS.
`output:html-comment-present` (MEDIUM) teller bare kommentarer utenfor kode.
I referansekorpuset står 253 av 254 kommentarer i kode.
- **Økt 6, rekkefølge-bæring:** A05 utgang. Hvert bilde bærer ingen data, men
rekkefølgen gjør det. Det graderes LOW og gir WARN.
- **Økt 7, instruksjoner i ren prosa:** A08, A09, A12, A16 og A19. Ingen av dem har