category: combining

general_categories: Ll Mn

string: café

trap: len() counts codepoints, not what a reader sees: truncating here can leave a combining mark attached to the wrong letter, or standing alone.


string: naè̈


general_categories: Cf Nd Pd Sk So

string: 👨🏻💻-0

trap: one visible glyph, several codepoints joined by U+200D: a per-character loop, a reverse, or a hard character limit splits it into pieces that render as strangers.


general_categories: Cf Ll So Zs

string: 👩🍳 admin


category: astral

general_categories: Ll Nd Pd So

trap: beyond the BMP: one codepoint is two UTF-16 code units, so JavaScript's .length, a UTF-16 column limit and Python's len() disagree about the same string.


general_categories: Ll Lu Nd Pd


general_categories: Ll Mn Nd Pd So Zs

string: ❤ login-0

trap: an invisible U+FE0F/U+FE0E decides text or emoji presentation; strip it and the glyph changes, keep it and the length is one more than it looks.


string: ☑ report-1


general_categories: Cf Ll Po

trap: invisible direction overrides reorder what is displayed without changing what is stored, so a filename or a name can read differently than it compares.


general_categories: Cf Ll Lo

string: שלוםsecure


general_categories: Cf Ll

string: invoice

trap: invisible codepoints survive trim(), slip through many alphanumeric checks, and make two identical-looking identifiers unequal.


string: admin


category: homoglyph

general_categories: Ll Nd Pd

trap: Cyrillic and Greek letters that look like ASCII. NFKC does NOT fold them, so normalising is not a defence against a lookalike.


general_categories: Ll Nd Zs

string: invoice ﬁ0

trap: compatibility characters whose NFKC form has a different length, so validating before normalising and storing after gives two different strings.


string: admin ﬂ1


trap: case conversion is not length-preserving and not always reversible: upper and lower round-trips change these strings.


general_categories: Ll Lu

string: ẞadmin


category: whitespace

general_categories: Ll Po Zs

string: invoice: report

trap: spaces and line terminators that are not U+0020 or U+000A: splitlines() breaks on some of them, strip() does not remove others.


general_categories: Cc Ll Po