What happened
Six invisible Unicode format characters — U+206A–U+206F (INHIBIT/ACTIVATE SYMMETRIC SWAPPING, INHIBIT/ACTIVATE ARABIC FORM SHAPING, NATIONAL/NOMINAL DIGIT SHAPES) — pass through both sanitizer regexes in packages/core/src/text-sanitize.ts:
BIDI_FORMAT_REGEX (line 60) covers U+2066–2069 but skips its immediate neighbors U+206A–206F
ZERO_WIDTH_REGEX (line 72) covers U+2060–2064 but also skips U+206A–206F
All six are category Cf — invisible, zero-width, deprecated bidi-related formatting characters, squarely in the threat family these classes exist to strip. They are not whitespace, so the \s+ collapse doesn't remove them either. Live-reproduced with the exact pipeline from sanitizeUnicodeText:
input: "alice\u206Abob"
output: "alice\u206Abob" (invisible char survives)
Two names differing only by U+206A render identically but compare unequal. Session names, foreign titles, ids rendered verbatim, and digest payloads all share these classes ("single source of truth", per the file's own comments), so the gap is systemic. The foreign id guard FOREIGN_UNSAFE_CHARS (packages/core/src/foreign-session.ts:156) has the same gap.
No test covers U+206A–206F, so this looks like unintentional drift rather than policy.
How to reproduce
normalizeUserSessionName("alice\u206Abob") → { ok: true, value: "alice\u206Abob" } — an invisible character persists; the same string also passes isSafeForeignId.
Suggested fix
Add \u206A-\u206F to BIDI_FORMAT_REGEX (space replacement keeps word separation consistent with the other bidi controls) and to FOREIGN_UNSAFE_CHARS. A follow-up class decision could also consider variation selectors (U+FE00–FE0F) and tag characters (U+E0000–E007F), both known invisible-content vectors.
What happened
Six invisible Unicode format characters — U+206A–U+206F (INHIBIT/ACTIVATE SYMMETRIC SWAPPING, INHIBIT/ACTIVATE ARABIC FORM SHAPING, NATIONAL/NOMINAL DIGIT SHAPES) — pass through both sanitizer regexes in
packages/core/src/text-sanitize.ts:BIDI_FORMAT_REGEX(line 60) covers U+2066–2069 but skips its immediate neighbors U+206A–206FZERO_WIDTH_REGEX(line 72) covers U+2060–2064 but also skips U+206A–206FAll six are category Cf — invisible, zero-width, deprecated bidi-related formatting characters, squarely in the threat family these classes exist to strip. They are not whitespace, so the
\s+collapse doesn't remove them either. Live-reproduced with the exact pipeline fromsanitizeUnicodeText:Two names differing only by U+206A render identically but compare unequal. Session names, foreign titles, ids rendered verbatim, and digest payloads all share these classes ("single source of truth", per the file's own comments), so the gap is systemic. The foreign id guard
FOREIGN_UNSAFE_CHARS(packages/core/src/foreign-session.ts:156) has the same gap.No test covers U+206A–206F, so this looks like unintentional drift rather than policy.
How to reproduce
normalizeUserSessionName("alice\u206Abob")→{ ok: true, value: "alice\u206Abob" }— an invisible character persists; the same string also passesisSafeForeignId.Suggested fix
Add
\u206A-\u206FtoBIDI_FORMAT_REGEX(space replacement keeps word separation consistent with the other bidi controls) and toFOREIGN_UNSAFE_CHARS. A follow-up class decision could also consider variation selectors (U+FE00–FE0F) and tag characters (U+E0000–E007F), both known invisible-content vectors.