Bug 1724233 - Make sure to run ConvertUTF8toACE before ConvertToDisplayIDN r=necko-reviewers,dragana

Differential Revision: https://phabricator.services.mozilla.com/D122097
This commit is contained in:
Valentin Gosu
2021-09-16 11:27:34 +00:00
parent 048f855279
commit 0debc12e15
11 changed files with 55 additions and 248 deletions

View File

@@ -4,6 +4,7 @@
https://bugzilla.mozilla.org/show_bug.cgi?id=448166
-->
<head>
<meta charset="utf-8" />
<title>Test for Bug 448166</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
@@ -23,8 +24,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=448166
/** Test for Bug 448166 **/
isnot($("test").href, "http://www.mozilla.org/",
"Should notice unpaired surrogate");
is($("test").href, "http://www.xn--mozilla-2e14b.org/",
"Should replace unpaired surrogate with replacement char");
is($("test").href, "http://www.moz<EFBFBD>illa.org",
"URL parser fails. Href returns original input string");
SimpleTest.doesThrow(() => { new URL($("test").href);}, "URL parser rejects input");
is($("control").href, "http://www.mozilla.org/",
"Just making sure .href works");

View File

@@ -599,43 +599,51 @@ nsresult nsStandardURL::NormalizeIPv4(const nsACString& host,
return NS_OK;
}
nsresult nsStandardURL::NormalizeIDN(const nsACString& host,
nsCString& result) {
// If host is ACE, then convert to UTF-8. Else, if host is already UTF-8,
// then make sure it is normalized per IDN.
// this function returns true if normalization succeeds.
nsresult nsStandardURL::NormalizeIDN(const nsCString& host, nsCString& result) {
result.Truncate();
mDisplayHost.Truncate();
nsresult rv;
if (!gIDN) {
return NS_ERROR_UNEXPECTED;
}
bool isAscii;
nsAutoCString normalized;
rv = gIDN->ConvertToDisplayIDN(host, &isAscii, normalized);
// If the input is ASCII, and not ACE encoded, then there's no processing
// needed. This is needed because we want to allow ascii labels longer than
// 64 characters for some schemes.
bool isACE = false;
if (IsAscii(host) && NS_SUCCEEDED(gIDN->IsACE(host, &isACE)) && !isACE) {
mCheckedIfHostA = true;
result = host;
return NS_OK;
}
// Even if it's already ACE, we must still call ConvertUTF8toACE in order
// for the input normalization to take place.
rv = gIDN->ConvertUTF8toACE(host, result);
if (NS_FAILED(rv)) {
return rv;
}
// The result is ASCII. No need to convert to ACE.
if (isAscii) {
result = normalized;
// If the ASCII representation doesn't contain the xn-- token then we don't
// need to call ConvertToDisplayIDN as that would not change anything.
if (!StringBeginsWith(result, "xn--"_ns) &&
result.Find(".xn--"_ns) == kNotFound) {
mCheckedIfHostA = true;
mDisplayHost.Truncate();
return NS_OK;
}
rv = gIDN->ConvertUTF8toACE(normalized, result);
bool isAscii = true;
nsAutoCString displayHost;
rv = gIDN->ConvertToDisplayIDN(result, &isAscii, displayHost);
if (NS_FAILED(rv)) {
return rv;
}
mCheckedIfHostA = true;
mDisplayHost = normalized;
if (!isAscii) {
mDisplayHost = displayHost;
}
return NS_OK;
}

View File

@@ -184,7 +184,7 @@ class nsStandardURL : public nsIFileURL,
bool ValidIPv6orHostname(const char* host, uint32_t length);
static bool IsValidOfBase(unsigned char c, const uint32_t base);
nsresult NormalizeIDN(const nsACString& host, nsCString& result);
nsresult NormalizeIDN(const nsCString& host, nsCString& result);
nsresult CheckIfHostIsAscii();
void CoalescePath(netCoalesceFlags coalesceFlag, char* path);

View File

@@ -212,7 +212,13 @@ nsresult nsIDNService::IDNA2008StringPrep(const nsAString& input,
return NS_OK;
}
if (info.errors != 0) {
uint32_t ignoredErrors = 0;
if (flag == eStringPrepForDNS) {
ignoredErrors = UIDNA_ERROR_LEADING_HYPHEN | UIDNA_ERROR_TRAILING_HYPHEN |
UIDNA_ERROR_HYPHEN_3_4;
}
if ((info.errors & ~ignoredErrors) != 0) {
if (flag == eStringPrepForDNS) {
output.Truncate();
}
@@ -325,16 +331,25 @@ nsresult nsIDNService::ACEtoUTF8(const nsACString& input, nsACString& _retval,
}
NS_IMETHODIMP nsIDNService::IsACE(const nsACString& input, bool* _retval) {
const char* data = input.BeginReading();
uint32_t dataLen = input.Length();
// look for the ACE prefix in the input string. it may occur
// at the beginning of any segment in the domain name. for
// example: "www.xn--ENCODED.com"
const char* p = PL_strncasestr(data, kACEPrefix, dataLen);
if (!IsAscii(input)) {
*_retval = false;
return NS_OK;
}
*_retval = p && (p == data || *(p - 1) == '.');
auto stringContains = [](const nsACString& haystack,
const nsACString& needle) {
return std::search(haystack.BeginReading(), haystack.EndReading(),
needle.BeginReading(),
needle.EndReading()) != haystack.EndReading();
};
*_retval = StringBeginsWith(input, "xn--"_ns) ||
(!input.IsEmpty() && input[0] != '.' &&
stringContains(input, ".xn--"_ns));
return NS_OK;
}

View File

@@ -24,7 +24,7 @@ function run_test() {
.setSpec("http://%80.com")
.finalize();
},
/NS_ERROR_UNEXPECTED/,
/NS_ERROR_MALFORMED_URI/,
"illegal UTF character"
);

View File

@@ -1164,7 +1164,7 @@ add_test(
() => {
stringToURL("https://b%9a/");
},
/NS_ERROR_UNEXPECTED/,
/NS_ERROR_MALFORMED_URI/,
"bad URI"
);

View File

@@ -146,12 +146,6 @@
[Parsing: <file:..> against <http://www.example.com/test>]
expected: FAIL
[Parsing: <http://﷐zyx.com> against <http://other.com/>]
expected: FAIL
[Parsing: <http://%ef%b7%90zyx.com> against <http://other.com/>]
expected: FAIL
[Parsing: <http://192.168.0.257> against <http://other.com/>]
expected: FAIL
@@ -281,12 +275,6 @@
[Parsing: <file://[example\]/> against <about:blank>]
expected: FAIL
[Parsing: <https://<2F>> against <about:blank>]
expected: FAIL
[Parsing: <https://%EF%BF%BD> against <about:blank>]
expected: FAIL
[Parsing: <sc://\x00/> against <about:blank>]
expected: FAIL
@@ -498,12 +486,6 @@
[Parsing: <x> against <sc://ñ>]
expected: FAIL
[Parsing: <http://﷐zyx.com> against <http://other.com/>]
expected: FAIL
[Parsing: <https://<2F>> against <about:blank>]
expected: FAIL
[Parsing: <data:test# »> against <about:blank>]
expected: FAIL

View File

@@ -143,12 +143,6 @@
[Parsing: <http://www.@pple.com> against <about:blank>]
expected: FAIL
[Parsing: <http://﷐zyx.com> against <http://other.com/>]
expected: FAIL
[Parsing: <http://%ef%b7%90zyx.com> against <http://other.com/>]
expected: FAIL
[Parsing: <http://192.168.0.257> against <http://other.com/>]
expected: FAIL
@@ -281,12 +275,6 @@
[Parsing: <file://[example\]/> against <about:blank>]
expected: FAIL
[Parsing: <https://<2F>> against <about:blank>]
expected: FAIL
[Parsing: <https://%EF%BF%BD> against <about:blank>]
expected: FAIL
[Parsing: <sc://\x00/> against <about:blank>]
expected: FAIL
@@ -498,12 +486,6 @@
[Parsing: <x> against <sc://ñ>]
expected: FAIL
[Parsing: <http://﷐zyx.com> against <http://other.com/>]
expected: FAIL
[Parsing: <https://<2F>> against <about:blank>]
expected: FAIL
[Parsing: <data:test# »> against <about:blank>]
expected: FAIL
@@ -531,12 +513,6 @@
[Parsing: <x> against <sc://ñ>]
expected: FAIL
[Parsing: <http://﷐zyx.com> against <http://other.com/>]
expected: FAIL
[Parsing: <https://<2F>> against <about:blank>]
expected: FAIL
[Parsing: <data:test# »> against <about:blank>]
expected: FAIL

View File

@@ -48,27 +48,9 @@
[Location's href: http://@:www.example.com should throw]
expected: FAIL
[URL's href: https://<2F> should throw]
expected: FAIL
[XHR: https://<2F> should throw]
expected: FAIL
[sendBeacon(): https://<2F> should throw]
expected: FAIL
[Location's href: https://<2F> should throw]
expected: FAIL
[URL's href: https://%EF%BF%BD should throw]
expected: FAIL
[XHR: https://%EF%BF%BD should throw]
expected: FAIL
[sendBeacon(): https://%EF%BF%BD should throw]
expected: FAIL
[Location's href: https://%EF%BF%BD should throw]
expected: FAIL
@@ -159,27 +141,15 @@
[Location's href: sc://\]/ should throw]
expected: FAIL
[XHR: ftp://example.com%80/ should throw]
expected: FAIL
[Location's href: ftp://example.com%80/ should throw]
expected: FAIL
[XHR: ftp://example.com%A0/ should throw]
expected: FAIL
[Location's href: ftp://example.com%A0/ should throw]
expected: FAIL
[XHR: https://example.com%80/ should throw]
expected: FAIL
[Location's href: https://example.com%80/ should throw]
expected: FAIL
[XHR: https://example.com%A0/ should throw]
expected: FAIL
[Location's href: https://example.com%A0/ should throw]
expected: FAIL
@@ -231,12 +201,6 @@
[window.open(): file://[example\]/ should throw]
expected: FAIL
[window.open(): https://<2F> should throw]
expected: FAIL
[window.open(): https://%EF%BF%BD should throw]
expected: FAIL
[window.open(): sc://\x00/ should throw]
expected: FAIL
@@ -312,12 +276,6 @@
[URL's constructor's base argument: file://[example\]/ should throw]
expected: FAIL
[URL's constructor's base argument: https://<2F> should throw]
expected: FAIL
[URL's constructor's base argument: https://%EF%BF%BD should throw]
expected: FAIL
[URL's constructor's base argument: sc://\x00/ should throw]
expected: FAIL
@@ -354,42 +312,12 @@
[URL's constructor's base argument: non-special://[:80/ should throw]
expected: FAIL
[sendBeacon(): https://<2F> should throw]
expected: FAIL
[window.open(): https://<2F> should throw]
expected: FAIL
[Location's href: https://<2F> should throw]
expected: FAIL
[XHR: https://<2F> should throw]
expected: FAIL
[URL's constructor's base argument: https://<2F> should throw]
expected: FAIL
[URL's href: https://<2F> should throw]
expected: FAIL
[sendBeacon(): https://<2F> should throw]
expected: FAIL
[window.open(): https://<2F> should throw]
expected: FAIL
[Location's href: https://<2F> should throw]
expected: FAIL
[XHR: https://<2F> should throw]
expected: FAIL
[URL's constructor's base argument: https://<2F> should throw]
expected: FAIL
[URL's href: https://<2F> should throw]
expected: FAIL
[URL's constructor's base argument: file://xn--/p should throw]
expected: FAIL

View File

@@ -80,33 +80,6 @@
[xn--a.β (using <area>.hostname)]
expected: FAIL
[.example (using URL)]
expected: FAIL
[.example (using URL.host)]
expected: FAIL
[.example (using URL.hostname)]
expected: FAIL
[.example (using <a>)]
expected: FAIL
[.example (using <a>.host)]
expected: FAIL
[.example (using <a>.hostname)]
expected: FAIL
[.example (using <area>)]
expected: FAIL
[.example (using <area>.host)]
expected: FAIL
[.example (using <area>.hostname)]
expected: FAIL
[xn--1ug.example (using URL)]
expected: FAIL
@@ -134,33 +107,6 @@
[xn--1ug.example (using <area>.hostname)]
expected: FAIL
[يa (using URL)]
expected: FAIL
[يa (using URL.host)]
expected: FAIL
[يa (using URL.hostname)]
expected: FAIL
[يa (using <a>)]
expected: FAIL
[يa (using <a>.host)]
expected: FAIL
[يa (using <a>.hostname)]
expected: FAIL
[يa (using <area>)]
expected: FAIL
[يa (using <area>.host)]
expected: FAIL
[يa (using <area>.hostname)]
expected: FAIL
[xn--a-yoc (using URL)]
expected: FAIL
@@ -188,33 +134,6 @@
[xn--a-yoc (using <area>.hostname)]
expected: FAIL
[<5B>.com (using URL)]
expected: FAIL
[<5B>.com (using URL.host)]
expected: FAIL
[<5B>.com (using URL.hostname)]
expected: FAIL
[<5B>.com (using <a>)]
expected: FAIL
[<5B>.com (using <a>.host)]
expected: FAIL
[<5B>.com (using <a>.hostname)]
expected: FAIL
[<5B>.com (using <area>)]
expected: FAIL
[<5B>.com (using <area>.host)]
expected: FAIL
[<5B>.com (using <area>.hostname)]
expected: FAIL
[xn--zn7c.com (using URL)]
expected: FAIL
@@ -349,4 +268,3 @@
[xn-- (using URL.host)]
expected: FAIL

View File

@@ -80,18 +80,6 @@
[Parsing: <file:..> against <http://www.example.com/test>]
expected: FAIL
[Parsing: <http://﷐zyx.com> against <http://other.com/>]
expected: FAIL
[Parsing: <http://%ef%b7%90zyx.com> against <http://other.com/>]
expected: FAIL
[Parsing: <https://<2F>> against <about:blank>]
expected: FAIL
[Parsing: <https://%EF%BF%BD> against <about:blank>]
expected: FAIL
[Parsing: <sc://faß.ExAmPlE/> against <about:blank>]
expected: FAIL
@@ -688,18 +676,6 @@
[Parsing: <file:..> against <http://www.example.com/test>]
expected: FAIL
[Parsing: <http://﷐zyx.com> against <http://other.com/>]
expected: FAIL
[Parsing: <http://%ef%b7%90zyx.com> against <http://other.com/>]
expected: FAIL
[Parsing: <https://<2F>> against <about:blank>]
expected: FAIL
[Parsing: <https://%EF%BF%BD> against <about:blank>]
expected: FAIL
[Parsing: <sc://faß.ExAmPlE/> against <about:blank>]
expected: FAIL