From 00cac65af1895c643ae60a13c912bdf2e1cee291 Mon Sep 17 00:00:00 2001 From: Darshan Sen Date: Sat, 10 Jul 2021 18:37:47 +0530 Subject: [PATCH] url: prevent pathname setter from erasing path of path-only URLs This change prevents the pathname setter from erasing the path of path-only URLs as that would make them cannot-be-a-base URLs. The changes in all files except `src/node_url.cc` have been done by running: ```console git node wpt url ``` Fixes: https://github.com/nodejs/node/issues/39059 Signed-off-by: Darshan Sen PR-URL: https://github.com/nodejs/node/pull/39060 Reviewed-By: Anna Henningsen Reviewed-By: Khaidi Chu Reviewed-By: James M Snell Reviewed-By: Tiancheng "Timothy" Gu --- src/node_url.cc | 3 + test/fixtures/wpt/README.md | 2 +- .../wpt/url/resources/setters_tests.json | 103 ++++++++ .../wpt/url/resources/urltestdata.json | 224 +++++++++++++++++- test/fixtures/wpt/versions.json | 2 +- 5 files changed, 331 insertions(+), 3 deletions(-) diff --git a/src/node_url.cc b/src/node_url.cc index 4343b672c6a982..d7549e3bc05562 100644 --- a/src/node_url.cc +++ b/src/node_url.cc @@ -1477,6 +1477,9 @@ void URL::Parse(const char* input, if (ch != '/') { continue; } + } else if (has_state_override && !(url->flags & URL_FLAGS_HAS_HOST)) { + url->flags |= URL_FLAGS_HAS_PATH; + url->path.emplace_back(""); } break; case kPath: diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md index 60d3aeb98c17da..6019abd8aa8769 100644 --- a/test/fixtures/wpt/README.md +++ b/test/fixtures/wpt/README.md @@ -22,7 +22,7 @@ Last update: - interfaces: https://github.com/web-platform-tests/wpt/tree/fcb671ed8b/interfaces - resources: https://github.com/web-platform-tests/wpt/tree/972ca5b669/resources - streams: https://github.com/web-platform-tests/wpt/tree/8f60d94439/streams -- url: https://github.com/web-platform-tests/wpt/tree/1fcb39223d/url +- url: https://github.com/web-platform-tests/wpt/tree/77d54aa9e0/url [Web Platform Tests]: https://github.com/web-platform-tests/wpt [`git node wpt`]: https://github.com/nodejs/node-core-utils/blob/main/docs/git-node.md#git-node-wpt diff --git a/test/fixtures/wpt/url/resources/setters_tests.json b/test/fixtures/wpt/url/resources/setters_tests.json index 56bcae464a6f54..b709ef5234a4fd 100644 --- a/test/fixtures/wpt/url/resources/setters_tests.json +++ b/test/fixtures/wpt/url/resources/setters_tests.json @@ -990,6 +990,26 @@ "hostname": "test", "port": "12" } + }, + { + "comment": "Leading / is not stripped", + "href": "http://example.com/", + "new_value": "///bad.com", + "expected": { + "href": "http://example.com/", + "host": "example.com", + "hostname": "example.com" + } + }, + { + "comment": "Leading / is not stripped", + "href": "sc://example.com/", + "new_value": "///bad.com", + "expected": { + "href": "sc:///", + "host": "", + "hostname": "" + } } ], "hostname": [ @@ -1345,6 +1365,26 @@ "hostname": "", "pathname": "//p" } + }, + { + "comment": "Leading / is not stripped", + "href": "http://example.com/", + "new_value": "///bad.com", + "expected": { + "href": "http://example.com/", + "host": "example.com", + "hostname": "example.com" + } + }, + { + "comment": "Leading / is not stripped", + "href": "sc://example.com/", + "new_value": "///bad.com", + "expected": { + "href": "sc:///", + "host": "", + "hostname": "" + } } ], "port": [ @@ -1571,6 +1611,51 @@ "pathname": "me@example.net" } }, + { + "comment": "Special URLs cannot have their paths erased", + "href": "file:///some/path", + "new_value": "", + "expected": { + "href": "file:///", + "pathname": "/" + } + }, + { + "comment": "Non-special URLs can have their paths erased", + "href": "foo://somehost/some/path", + "new_value": "", + "expected": { + "href": "foo://somehost", + "pathname": "" + } + }, + { + "comment": "Non-special URLs with an empty host can have their paths erased", + "href": "foo:///some/path", + "new_value": "", + "expected": { + "href": "foo://", + "pathname": "" + } + }, + { + "comment": "Path-only URLs cannot have their paths erased", + "href": "foo:/some/path", + "new_value": "", + "expected": { + "href": "foo:/", + "pathname": "/" + } + }, + { + "comment": "Path-only URLs always have an initial slash", + "href": "foo:/some/path", + "new_value": "test", + "expected": { + "href": "foo:/test", + "pathname": "/test" + } + }, { "href": "unix:/run/foo.socket?timeout=10", "new_value": "/var/log/../run/bar.socket", @@ -1667,6 +1752,24 @@ "pathname": "/%23" } }, + { + "comment": "? doesn't mess up encoding", + "href": "http://example.net", + "new_value": "/?é", + "expected": { + "href": "http://example.net/%3F%C3%A9", + "pathname": "/%3F%C3%A9" + } + }, + { + "comment": "# doesn't mess up encoding", + "href": "http://example.net", + "new_value": "/#é", + "expected": { + "href": "http://example.net/%23%C3%A9", + "pathname": "/%23%C3%A9" + } + }, { "comment": "File URLs and (back)slashes", "href": "file://monkey/", diff --git a/test/fixtures/wpt/url/resources/urltestdata.json b/test/fixtures/wpt/url/resources/urltestdata.json index 96c42d2284ebf7..a56b30caf98cb1 100644 --- a/test/fixtures/wpt/url/resources/urltestdata.json +++ b/test/fixtures/wpt/url/resources/urltestdata.json @@ -539,6 +539,36 @@ "search": "", "hash": "" }, + { + "input": "\\x", + "base": "http://example.org/foo/bar", + "href": "http://example.org/x", + "origin": "http://example.org", + "protocol": "http:", + "username": "", + "password": "", + "host": "example.org", + "hostname": "example.org", + "port": "", + "pathname": "/x", + "search": "", + "hash": "" + }, + { + "input": "\\\\x\\hello", + "base": "http://example.org/foo/bar", + "href": "http://x/hello", + "origin": "http://x", + "protocol": "http:", + "username": "", + "password": "", + "host": "x", + "hostname": "x", + "port": "", + "pathname": "/hello", + "search": "", + "hash": "" + }, { "input": "::", "base": "http://example.org/foo/bar", @@ -4698,6 +4728,140 @@ "base": "about:blank", "failure": true }, + { + "input": "foo://ho\u0000st/", + "base": "about:blank", + "failure": true + }, + { + "input": "foo://ho|st/", + "base": "about:blank", + "failure": true + }, + "Forbidden host codepoints: tabs and newlines are removed during preprocessing", + { + "input": "foo://ho\u0009st/", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href":"foo://host/", + "password": "", + "pathname": "/", + "port":"", + "protocol": "foo:", + "search": "", + "username": "" + }, + { + "input": "foo://ho\u000Ast/", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href":"foo://host/", + "password": "", + "pathname": "/", + "port":"", + "protocol": "foo:", + "search": "", + "username": "" + }, + { + "input": "foo://ho\u000Dst/", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href":"foo://host/", + "password": "", + "pathname": "/", + "port":"", + "protocol": "foo:", + "search": "", + "username": "" + }, + "Encoded forbidden host codepoints in special URLs", + { + "input": "http://ho%00st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%09st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%0Ast/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%0Dst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%20st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%23st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%2Fst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%3Ast/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%3Cst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%3Est/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%3Fst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%40st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%5Bst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%5Cst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%5Dst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%7Cst/", + "base": "about:blank", + "failure": true + }, "Allowed host code points", { "input": "http://\u001F!\"$&'()*+,-.;=_`{}~/", @@ -7676,7 +7840,8 @@ "search": "", "username": "joe" }, - { "input": "foo://!\"$%&'()*+,-.;=_`{}~/", + { + "input": "foo://!\"$%&'()*+,-.;=_`{}~/", "base": "about:blank", "hash": "", "host": "!\"$%&'()*+,-.;=_`{}~", @@ -7794,5 +7959,62 @@ "protocol": "wss:", "search": "", "username": "" + }, + "Ensure that input schemes are not ignored when resolving non-special URLs", + { + "input": "abc:rootless", + "base": "abc://host/path", + "hash": "", + "host": "", + "hostname": "", + "href":"abc:rootless", + "password": "", + "pathname": "rootless", + "port":"", + "protocol": "abc:", + "search": "", + "username": "" + }, + { + "input": "abc:rootless", + "base": "abc:/path", + "hash": "", + "host": "", + "hostname": "", + "href":"abc:rootless", + "password": "", + "pathname": "rootless", + "port":"", + "protocol": "abc:", + "search": "", + "username": "" + }, + { + "input": "abc:rootless", + "base": "abc:path", + "hash": "", + "host": "", + "hostname": "", + "href":"abc:rootless", + "password": "", + "pathname": "rootless", + "port":"", + "protocol": "abc:", + "search": "", + "username": "" + }, + { + "input": "abc:/rooted", + "base": "abc://host/path", + "hash": "", + "host": "", + "hostname": "", + "href":"abc:/rooted", + "password": "", + "pathname": "/rooted", + "port":"", + "protocol": "abc:", + "search": "", + "username": "" } ] diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json index 6567782a1d47c9..fb10c7d403d730 100644 --- a/test/fixtures/wpt/versions.json +++ b/test/fixtures/wpt/versions.json @@ -48,7 +48,7 @@ "path": "streams" }, "url": { - "commit": "1fcb39223d3009fbb46c1b254755d6cc75e290f1", + "commit": "77d54aa9e0405f737987b59331f3584e3e1c26f9", "path": "url" } } \ No newline at end of file