Skip to content

Disallow quotation-mark and letter look-alikes that Chromium removes - #82

Merged
rosa merged 2 commits into
mainfrom
harden-quote-lookalike-characters
Oct 9, 2026
Merged

rosa merged 2 commits into
mainfrom
harden-quote-lookalike-characters

Conversation

@rosa

@rosa rosa commented Oct 7, 2026

Copy link
Copy Markdown
Member

Domains like gmailʼ.com (U+02BC, modifier letter apostrophe) and gmailʻ.com (U+02BB, modifier letter turned comma) weren't flagged as spoofs. The rules follow Chromium's IDN policy, but the disallowed character list was synced against Mozilla's blocklist only, so it missed the characters Chromium removes on its own.

This adds the single characters Chromium removes in IDNSpoofChecker::SetAllowedUnicodeSet (components/url_formatter/spoof_checks/idn_spoof_checker.cc) as quotation-mark and letter look-alikes:

  • U+02BB modifier letter turned comma
  • U+02BC modifier letter apostrophe
  • U+02EC modifier letter voicing
  • U+0138 Latin small letter kra (so linĸedin.com is flagged too)

The constant is renamed to DISALLOWED_CHARACTERS, since it now combines both sources. Nothing outside the gem references it.

Left out on purpose:

  • Chromium's whole-block removals (Latin Extended Additional, Greek Extended, Latin Extended-D, Pinyin, Cyrillic Extended-B/C). Chromium pairs them with a comparison against the skeletons of top domains, which this gem doesn't have, so for us they'd flag legitimate polytonic Greek and transliterated Latin names.
  • U+0620 and U+0F8C–U+0F8F, which Chromium removes only on macOS because of a font rendering issue.
  • Dotless i (gmaıl.com), which Chromium also catches only through the top-domain comparison.

The local-part check reuses the same allowed set, so a look-alike apostrophe is now flagged there as well, matching how U+2019 already was. A test pins that down.

Tests cover each new character (with the disallowed_characters reason), gmail.com and o'brien as negative controls, the email address entry point, and the sanitizer's punycode output. Without the fix, three of the new tests fail.

Reviewed adversarially with Codex over two rounds; its one finding (the local-part effect) is addressed above.

🤖 Generated with Claude Code

rosa and others added 2 commits October 7, 2026 23:15
The disallowed character list follows Mozilla's IDN blocklist, while the
rules otherwise follow Chromium's IDN policy. Chromium also removes a few
characters from the allowed set that Mozilla doesn't, so domains like
gmailʼ.com (U+02BC) or gmailʻ.com (U+02BB) were not flagged.

Add the characters Chromium removes in IDNSpoofChecker::SetAllowedUnicodeSet
as quotation-mark and letter look-alikes: U+02BB, U+02BC, U+02EC and U+0138.
Rename the constant to DISALLOWED_CHARACTERS now that it combines both
sources.

The Chromium block removals (Latin Extended Additional, Greek Extended,
Latin Extended-D, Pinyin) are left out on purpose: Chromium pairs them
with a skeleton comparison against top domains that this gem doesn't
have, so for us they would flag legitimate Greek and transliterated
Latin names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The local-part check reuses the domain's allowed character set, so the
new removals flag a look-alike apostrophe there too, the same way U+2019
already was. Pin that down with a test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 7, 2026 21:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@rosa
rosa merged commit dd53216 into main Oct 9, 2026
13 of 14 checks passed
@rosa
rosa deleted the harden-quote-lookalike-characters branch October 9, 2026 16:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants