Multilingual Attacks and Why English-Only Defenses Fail

My prompt-injection scanner only checked for English keywords, which works fine until someone writes the same instruction in another language. Extending it to two dozen languages turned up a dumber problem: a word-boundary check that had quietly never worked against non-Latin scripts.

Magatama watches a pile of alert feeds and log streams so I don't have to. It reads incoming text from monitoring systems and whatever else surfaces on a given day, and decides whether anything needs a model's attention. Somewhere in that pipeline, before any of that text reaches a model, there's a scanner whose whole job is to ask one question: does this look like someone trying to smuggle instructions into the alert?

The first version of that scanner was embarrassingly simple. It looked for English keyword patterns, phrases like "ignore previous instructions," "disregard the above," the greatest hits of prompt injection as documented by every write-up on the internet in the last couple of years. It worked fine in testing, because in testing I wrote the test cases, and I write in English.

Then I actually thought about who'd be feeding text into this thing. Not just me. Alert text, log lines, and feed content can come from anywhere, and anyone trying to sneak a payload past a keyword filter has an obvious move: skip the keywords the filter knows about. Write the same instruction in German, or Japanese, or Arabic, and a scanner built around English phrases has nothing to say about it. No cleverness required, just reading the filter and avoiding its vocabulary.

So I extended the pattern list to cover roughly two dozen languages, including several non-Latin scripts. That part was mostly grinding, translating the same handful of injection phrasings, checking them against native speakers where I could, making sure "ignore the above and do X" actually reads as an instruction in each language and not as nonsense.

The more interesting problem showed up before any of that keyword matching even runs. Text doesn't have to look like garbage to be hostile. You can insert zero-width characters between letters, invisible on screen, that split a flagged word into pieces no pattern will match. You can swap a Latin letter for a lookalike character from another script (Cyrillic, Greek, whatever renders close enough) so the word looks identical to a person and completely different to a string match. You can space letters out, i-g-n-o-r-e style, which does nothing to a human reader and everything to a filter looking for a contiguous word. None of these are exotic tricks. Spammers have used all three for years, just aimed at a different target now.

The fix was a normalization pass that runs before any pattern matching: strip zero-width characters, map homoglyphs back to their plain-Latin equivalents, collapse artificial letter-spacing, then hand the cleaned text to the scanner. Boring code, and it should have been there from the start.

The boundary problem

Here's the detail that got me, in a slightly humbled way. One of the older detection patterns used a word-boundary check, to make sure it matched a whole word rather than a fragment buried inside something else. Word boundaries, in the regex syntax I was using, are defined around what counts as a word character, and that definition leans heavily on the Latin alphabet. Against non-Latin scripts, the boundary logic doesn't behave the way testing against English strings would lead you to assume. In practice, that meant the pattern could fail to match the very text it was supposed to catch, in exactly the languages I'd just added support for.

It wasn't a bug I found through careful reasoning. I was testing something else entirely, ran the scanner against a batch of non-English strings, and noticed a category of expected hits just wasn't showing up. That particular check had been silently inert against those scripts since the day I wrote it, and nothing had ever exercised it against non-Latin input to tell me otherwise.

The lesson isn't complicated, even if it took an embarrassing bug to teach it to me. If detection logic was written and tested exclusively in one language, you don't know what it does against the other six thousand until you check. I hadn't checked. These days every new pattern gets run against a small multilingual sample before I trust it. Low bar. Took a while to get there.