docs
stormdoor.dev
FINDING

prompt.unicode_smuggling

Invisible Unicode characters that decode to a readable message addressed to the model.

SEVERITY
high
LEADS TO
block
GATE
/v1/prompts
CHECK
prompt.unicode_smuggling
Jump to what to do

Source: rules.

What it looks at#

Runs of characters that render as nothing but carry data, decoded and tested for readable text (valid UTF-8, mostly printable, at least four letters):

  • unicode_tag — Tag-block characters (U+E0000–U+E007F) shadowing ASCII. The most widely demonstrated “ASCII smuggling” channel.
  • variation_selector — a run of two or more variation selectors (U+FE00–FE0F, U+E0100–E01EF), each encoding one byte. A single selector after an emoji or a CJK ideograph is normal and ignored.
  • zero_width — a run of 16+ zero-width characters drawn from two symbols, read as a bit stream.
  • braille — a run of Braille pattern characters read as a byte stream.

Legitimate uses are left alone: the subdivision-flag emoji (a Tag sequence after U+1F3F4), ZWJ inside emoji and Arabic/Indic text, variation selectors on emoji.

Why it matters#

A human reviewer sees an empty-looking string; a model may read the whole message. This is the channel used to hide instructions in documents, web pages and tool output that an agent is about to read. Unlike prompt.hidden_text, which says “there are invisible characters here”, this says the invisible characters are an actual message.

What to do#

High severity — on its own this pushes the verdict to block. Text that is hidden and readable is hard to produce by accident. The response never echoes what the characters decoded to (design rule 1); location is the span of the hidden run in your original text, and data.matches[].rule names the channel. When the decoded message also matches an instruction phrase list, this single finding covers the run — it is not reported twice.

OTHER FINDING CODES FROM /V1/PROMPTS