fix: add Unicode normalization and homoglyph detection to tool name validation

Closed
MA
3mo ago
+170-0

Problem

Tool name validation accepts Unicode homoglyphs — characters that look visually identical to ASCII but are encoded differently. This allows tool names that appear legitimate but are actually distinct identifiers, enabling spoofing attacks in agentic workflows.

Attack Scenario

  1. Attacker registers tool verify_սser (Armenian ս U+057D instead of Latin s)
  2. Tool appears as verify_user in UI, logs, and agent context
  3. User or orchestrator believes they are calling the legitimate verify_user tool
  4. Attacker's malicious verify_սser executes instead
  5. Result: permission escalation, credential theft, data exfiltration

Affected Character Classes (pre-fix)

CategoryExampleUnicode
Cyrillic Аlooks like Latin AU+0410
Cyrillic Оlooks like Latin OU+041E
Greek Αlooks like Latin AU+0391
Fullwidth alooks like ASCII aU+FF41
RTL Override ‮reverses displayed textU+202E

Solution

Added two complementary checks to validate_tool_name():

  1. NFKC normalization check — if unicodedata.normalize('NFKC', name) != name, the name contains confusable or fullwidth characters and is rejected
  2. Bidirectional formatting character check — explicitly rejects all 12 Unicode bidi control characters (U+200E, U+200F, U+202A–U+202E, U+2066–U+2069, U+061C) that can reverse or reorder displayed text

Both checks run before the existing regex, so malicious names fail fast with a descriptive error message.

Changes

src/mcp/shared/tool_name_validation.py

  • Added import unicodedata
  • Added BIDIRECTIONAL_FORMATTING_CHARS constant (12 Unicode control characters)
  • Added bidi character detection loop in validate_tool_name()
  • Added NFKC normalization check in validate_tool_name()

tests/shared/test_tool_name_validation.py

  • TestUnicodeHomoglyphDetection — 9 tests covering Cyrillic, Greek, Fullwidth, RTL/LTR overrides
  • TestUnicodeNormalizationBypass — 2 tests for decomposed and NFKC normalization forms
  • TestUnicodeBoundaryConditions — 1 test for zero-width characters
  • TestValidAsciiStillWorks — 5 regression tests confirming valid ASCII names are unaffected

Total: 16 new test cases, 0 breaking changes

Verification

pytest tests/shared/test_tool_name_validation.py -v

All existing tests continue to pass. New tests confirm:

  • Homoglyphs are rejected with a descriptive normalization warning
  • Bidi formatting characters are rejected with a directional warning
  • Valid ASCII tool names (verify_user, tool-name, DATA_EXPORT_v2.1) are unaffected

References