Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1966,6 +1966,19 @@ private static boolean isOnlyASCII(final String input) {
return true;
}

/*
* Tests whether the code point is one that IDNA nameprep (RFC 3454 Table B.1, "commonly mapped to
* nothing") deletes but that is not a Unicode FORMAT character, so the FORMAT check in
* unicodeToASCII does not catch it: the combining grapheme joiner, the Mongolian TODO soft hyphen
* and free variation selectors, and the variation selectors.
*/
private static boolean isNameprepMappedToNothing(final int codePoint) {
return codePoint == '\u034F' // COMBINING GRAPHEME JOINER
|| codePoint == '\u1806' // MONGOLIAN TODO SOFT HYPHEN
|| codePoint >= '\u180B' && codePoint <= '\u180D' // MONGOLIAN FREE VARIATION SELECTOR ONE..THREE
|| codePoint >= '\uFE00' && codePoint <= '\uFE0F'; // VARIATION SELECTOR-1..16
}

/**
* Converts potentially Unicode input to punycode. If conversion fails, returns the original input.
*
Expand All @@ -1977,13 +1990,16 @@ static String unicodeToASCII(final String input) {
if (isOnlyASCII(input)) { // skip possibly expensive processing
return input;
}
// IDN.toASCII silently drops default-ignorable and format code points (soft hyphen,
// zero-width spaces, the byte order mark, ...) during nameprep, so a host carrying one
// would convert to a clean label and validate. Those code points are not legal in a host
// name, so keep the original here and let the label regex reject it.
// IDN.toASCII silently drops the code points that nameprep (RFC 3454 Table B.1) maps to
// nothing - the soft hyphen, zero-width spaces, the byte order mark, the combining grapheme
// joiner, the Mongolian and variation selectors and so on - so a host carrying one would
// convert to a clean label and validate as a different host. Most are Unicode FORMAT
// characters, but the combining grapheme joiner, the Mongolian selectors and the variation
// selectors are not, so the FORMAT check alone lets them through. Reject both here and let
// the label regex reject anything else.
for (int i = 0; i < input.length();) {
final int codePoint = input.codePointAt(i);
if (Character.getType(codePoint) == Character.FORMAT) {
if (Character.getType(codePoint) == Character.FORMAT || isNameprepMappedToNothing(codePoint)) {
return input;
}
i += Character.charCount(codePoint);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -465,11 +465,24 @@ void testIDNFormatCodePoints() {
// Those code points are not legal in a host name and must be rejected.
assertFalse(validator.isValid("exa\u00ADmple.com"), "soft hyphen shouldn't validate");
assertFalse(validator.isValid("exa\u200Bmple.com"), "zero-width space shouldn't validate");
assertFalse(validator.isValid("exa\u200Cmple.com"), "zero-width non-joiner shouldn't validate");
assertFalse(validator.isValid("exa\u200Dmple.com"), "zero-width joiner shouldn't validate");
assertFalse(validator.isValid("\uFEFFexample.com"), "byte order mark shouldn't validate");
assertTrue(validator.isValid("www.b\u00fccher.ch"), "b\u00fccher.ch should still validate");
}

@Test
void testIDNMappedToNothing() {
// IDN.toASCII also strips the code points that nameprep maps to nothing but that are not
// Unicode FORMAT characters, so the format-code-point guard alone would let them collapse a
// host to a clean label. They must be rejected too.
assertFalse(validator.isValid("exa\u034Fmple.com"), "combining grapheme joiner shouldn't validate");
assertFalse(validator.isValid("exa\uFE0Fmple.com"), "variation selector shouldn't validate");
assertFalse(validator.isValid("exa\u1806mple.com"), "Mongolian TODO soft hyphen shouldn't validate");
assertFalse(validator.isValid("exa\u180Bmple.com"), "Mongolian free variation selector shouldn't validate");
assertTrue(validator.isValid("www.b\u00fccher.ch"), "b\u00fccher.ch should still validate");
}

@Test
void testIDNJava6OrLater() {
// xn--d1abbgf6aiiy.xn--p1ai http://президент.рф
Expand Down
Loading