Diff
Modified: trunk/LayoutTests/ChangeLog (221977 => 221978)
--- trunk/LayoutTests/ChangeLog 2017-09-13 17:50:48 UTC (rev 221977)
+++ trunk/LayoutTests/ChangeLog 2017-09-13 18:06:24 UTC (rev 221978)
@@ -1,3 +1,17 @@
+2017-09-13 Daniel Bates <[email protected]>
+
+ Make history.pushState()/replaceState() more closely aligned to the HTML standard
+ https://bugs.webkit.org/show_bug.cgi?id=176730
+ <rdar://problem/33839265>
+
+ Reviewed by Alex Christensen.
+
+ * http/tests/security/history-pushState-replaceState-from-sandboxed-iframe-expected.txt: Added.
+ * http/tests/security/history-pushState-replaceState-from-sandboxed-iframe.html: Added.
+ * http/tests/security/history-username-password-expected.txt:
+ * http/tests/security/history-username-password.html:
+ * http/tests/security/resources/history-pushState-replaceState-from-sandboxed-iframe.html: Added.
+
2017-09-13 John Wilander <[email protected]>
Introduce Storage Access API (document parts) as an experimental feature
Added: trunk/LayoutTests/http/tests/security/history-pushState-replaceState-from-sandboxed-iframe-expected.txt (0 => 221978)
--- trunk/LayoutTests/http/tests/security/history-pushState-replaceState-from-sandboxed-iframe-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/security/history-pushState-replaceState-from-sandboxed-iframe-expected.txt 2017-09-13 18:06:24 UTC (rev 221978)
@@ -0,0 +1,22 @@
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+Tests history.replaceState(), history.pushState() from a sandboxed iframe
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS window.history.replaceState(null, "New title", location.href) did not throw exception.
+PASS window.history.pushState(null, "New title", location.href) did not throw exception.
+PASS window.history.replaceState(null, "New title", completeURL("")) threw exception SecurityError: Blocked attempt to use history.replaceState() to change session history URL from http://127.0.0.1:8000/security/resources/history-pushState-replaceState-from-sandboxed-iframe.html to http://127.0.0.1:8000/. Paths and fragments must match for a sandboxed document..
+PASS window.history.pushState(null, "New title", completeURL("")) threw exception SecurityError: Blocked attempt to use history.pushState() to change session history URL from http://127.0.0.1:8000/security/resources/history-pushState-replaceState-from-sandboxed-iframe.html to http://127.0.0.1:8000/. Paths and fragments must match for a sandboxed document..
+PASS window.history.replaceState(null, "New title", completeURL("dummy")) threw exception SecurityError: Blocked attempt to use history.replaceState() to change session history URL from http://127.0.0.1:8000/security/resources/history-pushState-replaceState-from-sandboxed-iframe.html to http://127.0.0.1:8000/dummy. Paths and fragments must match for a sandboxed document..
+PASS window.history.pushState(null, "New title", completeURL("dummy")) threw exception SecurityError: Blocked attempt to use history.pushState() to change session history URL from http://127.0.0.1:8000/security/resources/history-pushState-replaceState-from-sandboxed-iframe.html to http://127.0.0.1:8000/dummy. Paths and fragments must match for a sandboxed document..
+PASS window.history.replaceState(null, "New title", completeURL("", "dummy")) threw exception SecurityError: Blocked attempt to use history.replaceState() to change session history URL from http://127.0.0.1:8000/security/resources/history-pushState-replaceState-from-sandboxed-iframe.html to http://127.0.0.1:8000/?dummy. Paths and fragments must match for a sandboxed document..
+PASS window.history.pushState(null, "New title", completeURL("", "dummy")) threw exception SecurityError: Blocked attempt to use history.pushState() to change session history URL from http://127.0.0.1:8000/security/resources/history-pushState-replaceState-from-sandboxed-iframe.html to http://127.0.0.1:8000/?dummy. Paths and fragments must match for a sandboxed document..
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/http/tests/security/history-pushState-replaceState-from-sandboxed-iframe.html (0 => 221978)
--- trunk/LayoutTests/http/tests/security/history-pushState-replaceState-from-sandboxed-iframe.html (rev 0)
+++ trunk/LayoutTests/http/tests/security/history-pushState-replaceState-from-sandboxed-iframe.html 2017-09-13 18:06:24 UTC (rev 221978)
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner) {
+ testRunner.dumpAsText();
+ testRunner.dumpChildFramesAsText();
+}
+</script>
+</head>
+<body>
+<iframe src="" sandbox="allow-scripts" width="100%" height="700"></iframe>
+</body>
+</html>
Modified: trunk/LayoutTests/http/tests/security/history-username-password-expected.txt (221977 => 221978)
--- trunk/LayoutTests/http/tests/security/history-username-password-expected.txt 2017-09-13 17:50:48 UTC (rev 221977)
+++ trunk/LayoutTests/http/tests/security/history-username-password-expected.txt 2017-09-13 18:06:24 UTC (rev 221978)
@@ -1,14 +1,18 @@
Click to test in new window
-SecurityError: Attempt to use history.replaceState() to change session history URL to http://[email protected]:8000/ is insecure; Username/passwords aren't allowed in state object URLs
-SecurityError: Attempt to use history.replaceState() to change session history URL to http://:[email protected]:8000/ is insecure; Username/passwords aren't allowed in state object URLs
-SecurityError: Attempt to use history.replaceState() to change session history URL to http://www.webkit:[email protected]:8000/ is insecure; Username/passwords aren't allowed in state object URLs
-SecurityError: Attempt to use history.pushState() to add URL http://[email protected]:8000/ to session history is insecure; Username/passwords aren't allowed in state object URLs
-SecurityError: Attempt to use history.pushState() to add URL http://:[email protected]:8000/ to session history is insecure; Username/passwords aren't allowed in state object URLs
-SecurityError: Attempt to use history.pushState() to add URL http://www.webkit:[email protected]:8000/ to session history is insecure; Username/passwords aren't allowed in state object URLs
-SecurityError: Attempt to use history.replaceState() to change session history URL to http://[email protected]:8000/ is insecure; Username/passwords aren't allowed in state object URLs
-SecurityError: Attempt to use history.replaceState() to change session history URL to http://:[email protected]:8000/ is insecure; Username/passwords aren't allowed in state object URLs
-SecurityError: Attempt to use history.replaceState() to change session history URL to http://www.webkit:[email protected]:8000/ is insecure; Username/passwords aren't allowed in state object URLs
-SecurityError: Attempt to use history.pushState() to add URL http://[email protected]:8000/ to session history is insecure; Username/passwords aren't allowed in state object URLs
-SecurityError: Attempt to use history.pushState() to add URL http://:[email protected]:8000/ to session history is insecure; Username/passwords aren't allowed in state object URLs
-SecurityError: Attempt to use history.pushState() to add URL http://www.webkit:[email protected]:8000/ to session history is insecure; Username/passwords aren't allowed in state object URLs
+SecurityError: Blocked attempt to use history.replaceState() to change session history URL from http://127.0.0.1:8000/security/history-username-password.html to http://[email protected]:8000/. Protocols, domains, ports, usernames, and passwords must match.
+SecurityError: Blocked attempt to use history.replaceState() to change session history URL from http://127.0.0.1:8000/security/history-username-password.html to http://:[email protected]:8000/. Protocols, domains, ports, usernames, and passwords must match.
+SecurityError: Blocked attempt to use history.replaceState() to change session history URL from http://127.0.0.1:8000/security/history-username-password.html to http://www.webkit:[email protected]:8000/. Protocols, domains, ports, usernames, and passwords must match.
+SecurityError: Blocked attempt to use history.replaceState() to change session history URL from http://127.0.0.1:8000/security/history-username-password.html to blob:http://www.webkit:[email protected]:8000. Protocols, domains, ports, usernames, and passwords must match.
+SecurityError: Blocked attempt to use history.pushState() to change session history URL from http://127.0.0.1:8000/security/history-username-password.html to http://[email protected]:8000/. Protocols, domains, ports, usernames, and passwords must match.
+SecurityError: Blocked attempt to use history.pushState() to change session history URL from http://127.0.0.1:8000/security/history-username-password.html to http://:[email protected]:8000/. Protocols, domains, ports, usernames, and passwords must match.
+SecurityError: Blocked attempt to use history.pushState() to change session history URL from http://127.0.0.1:8000/security/history-username-password.html to http://www.webkit:[email protected]:8000/. Protocols, domains, ports, usernames, and passwords must match.
+SecurityError: Blocked attempt to use history.pushState() to change session history URL from http://127.0.0.1:8000/security/history-username-password.html to blob:http://www.webkit:[email protected]:8000. Protocols, domains, ports, usernames, and passwords must match.
+SecurityError: Blocked attempt to use history.replaceState() to change session history URL from about:blank to http://[email protected]:8000/. Protocols, domains, ports, usernames, and passwords must match.
+SecurityError: Blocked attempt to use history.replaceState() to change session history URL from about:blank to http://:[email protected]:8000/. Protocols, domains, ports, usernames, and passwords must match.
+SecurityError: Blocked attempt to use history.replaceState() to change session history URL from about:blank to http://www.webkit:[email protected]:8000/. Protocols, domains, ports, usernames, and passwords must match.
+SecurityError: Blocked attempt to use history.replaceState() to change session history URL from about:blank to blob:http://www.webkit:[email protected]:8000. Protocols, domains, ports, usernames, and passwords must match.
+SecurityError: Blocked attempt to use history.pushState() to change session history URL from about:blank to http://[email protected]:8000/. Protocols, domains, ports, usernames, and passwords must match.
+SecurityError: Blocked attempt to use history.pushState() to change session history URL from about:blank to http://:[email protected]:8000/. Protocols, domains, ports, usernames, and passwords must match.
+SecurityError: Blocked attempt to use history.pushState() to change session history URL from about:blank to http://www.webkit:[email protected]:8000/. Protocols, domains, ports, usernames, and passwords must match.
+SecurityError: Blocked attempt to use history.pushState() to change session history URL from about:blank to blob:http://www.webkit:[email protected]:8000. Protocols, domains, ports, usernames, and passwords must match.
Modified: trunk/LayoutTests/http/tests/security/history-username-password.html (221977 => 221978)
--- trunk/LayoutTests/http/tests/security/history-username-password.html 2017-09-13 17:50:48 UTC (rev 221977)
+++ trunk/LayoutTests/http/tests/security/history-username-password.html 2017-09-13 18:06:24 UTC (rev 221978)
@@ -34,6 +34,13 @@
}
try {
+ historyToTest.replaceState(null, "Phishy Title", "blob:" + location.protocol + "//www.webkit:org" + "@" + location.host);
+ log("replaceState with username and password worked, shouldn't have.");
+ } catch(e) {
+ log(e);
+ }
+
+ try {
historyToTest.pushState(null, "Phishy Title", location.protocol + "//www.webkit.org" + "@" + location.host);
log("pushState with username worked, shouldn't have.");
} catch(e) {
@@ -53,6 +60,13 @@
} catch(e) {
log(e);
}
+
+ try {
+ historyToTest.pushState(null, "Phishy Title", "blob:" + location.protocol + "//www.webkit:org" + "@" + location.host);
+ log("pushState with username and password worked, shouldn't have.");
+ } catch(e) {
+ log(e);
+ }
}
function clicked()
Added: trunk/LayoutTests/http/tests/security/resources/history-pushState-replaceState-from-sandboxed-iframe.html (0 => 221978)
--- trunk/LayoutTests/http/tests/security/resources/history-pushState-replaceState-from-sandboxed-iframe.html (rev 0)
+++ trunk/LayoutTests/http/tests/security/resources/history-pushState-replaceState-from-sandboxed-iframe.html 2017-09-13 18:06:24 UTC (rev 221978)
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<script src=""
+<body>
+<script>
+function completeURL(path = "", fragment = "")
+{
+ var url = ""
+ if (path)
+ url += path;
+ if (fragment)
+ url += "?" + fragment;
+ return url;
+}
+
+description("Tests history.replaceState(), history.pushState() from a sandboxed iframe");
+
+shouldNotThrow('window.history.replaceState(null, "New title", location.href)');
+shouldNotThrow('window.history.pushState(null, "New title", location.href)');
+
+shouldThrow('window.history.replaceState(null, "New title", completeURL(""))', "'SecurityError: Blocked attempt to use history.replaceState() to change session history URL from http://127.0.0.1:8000/security/resources/history-pushState-replaceState-from-sandboxed-iframe.html to http://127.0.0.1:8000/. Paths and fragments must match for a sandboxed document.'");
+
+shouldThrow('window.history.pushState(null, "New title", completeURL(""))', "'SecurityError: Blocked attempt to use history.pushState() to change session history URL from http://127.0.0.1:8000/security/resources/history-pushState-replaceState-from-sandboxed-iframe.html to http://127.0.0.1:8000/. Paths and fragments must match for a sandboxed document.'");
+
+shouldThrow('window.history.replaceState(null, "New title", completeURL("dummy"))', "'SecurityError: Blocked attempt to use history.replaceState() to change session history URL from http://127.0.0.1:8000/security/resources/history-pushState-replaceState-from-sandboxed-iframe.html to http://127.0.0.1:8000/dummy. Paths and fragments must match for a sandboxed document.'");
+
+shouldThrow('window.history.pushState(null, "New title", completeURL("dummy"))', "'SecurityError: Blocked attempt to use history.pushState() to change session history URL from http://127.0.0.1:8000/security/resources/history-pushState-replaceState-from-sandboxed-iframe.html to http://127.0.0.1:8000/dummy. Paths and fragments must match for a sandboxed document.'");
+
+shouldThrow('window.history.replaceState(null, "New title", completeURL("", "dummy"))', "'SecurityError: Blocked attempt to use history.replaceState() to change session history URL from http://127.0.0.1:8000/security/resources/history-pushState-replaceState-from-sandboxed-iframe.html to http://127.0.0.1:8000/?dummy. Paths and fragments must match for a sandboxed document.'");
+
+shouldThrow('window.history.pushState(null, "New title", completeURL("", "dummy"))', "'SecurityError: Blocked attempt to use history.pushState() to change session history URL from http://127.0.0.1:8000/security/resources/history-pushState-replaceState-from-sandboxed-iframe.html to http://127.0.0.1:8000/?dummy. Paths and fragments must match for a sandboxed document.'");
+</script>
+</body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (221977 => 221978)
--- trunk/Source/WebCore/ChangeLog 2017-09-13 17:50:48 UTC (rev 221977)
+++ trunk/Source/WebCore/ChangeLog 2017-09-13 18:06:24 UTC (rev 221978)
@@ -1,3 +1,25 @@
+2017-09-13 Daniel Bates <[email protected]>
+
+ Make history.pushState()/replaceState() more closely aligned to the HTML standard
+ https://bugs.webkit.org/show_bug.cgi?id=176730
+ <rdar://problem/33839265>
+
+ Reviewed by Alex Christensen.
+
+ Update history.pushState()/replaceState() to more closely align with the algorithm
+ specified in <https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate-2> (9 September 2017).
+
+ Test: http/tests/security/history-pushState-replaceState-from-sandboxed-iframe.html
+
+ * page/History.cpp:
+ (WebCore::History::stateObjectAdded):
+ * page/SecurityOrigin.cpp:
+ (WebCore::SecurityOrigin::extractInnerURL): Use URL constructor that takes a base URL as opposed
+ to using the special ParsedURLString-variant because the latter can only be used to parse a string
+ returned from URL::string(). And the extracted inner URL does not meet this criterion. Using the
+ ParsedURLString-variant of the URL constructor with a string that is not the result of URL::string()
+ will cause an assertion failure in a debug build.
+
2017-09-13 John Wilander <[email protected]>
Introduce Storage Access API (document parts) as an experimental feature
Modified: trunk/Source/WebCore/page/History.cpp (221977 => 221978)
--- trunk/Source/WebCore/page/History.cpp 2017-09-13 17:50:48 UTC (rev 221977)
+++ trunk/Source/WebCore/page/History.cpp 2017-09-13 18:06:24 UTC (rev 221978)
@@ -174,15 +174,20 @@
return { };
URL fullURL = urlForState(urlString);
- if (!fullURL.isValid() || !m_frame->document()->securityOrigin().canRequest(fullURL))
+ if (!fullURL.isValid())
return Exception { SecurityError };
- if (fullURL.hasUsername() || fullURL.hasPassword()) {
- if (stateObjectType == StateObjectType::Replace)
- return Exception { SecurityError, "Attempt to use history.replaceState() to change session history URL to " + fullURL.string() + " is insecure; Username/passwords aren't allowed in state object URLs" };
- return Exception { SecurityError, "Attempt to use history.pushState() to add URL " + fullURL.string() + " to session history is insecure; Username/passwords aren't allowed in state object URLs" };
- }
+ const URL& documentURL = m_frame->document()->url();
+ auto createBlockedURLSecurityErrorWithMessageSuffix = [&] (const char* suffix) {
+ const char* functionName = stateObjectType == StateObjectType::Replace ? "history.replaceState()" : "history.pushState()";
+ return Exception { SecurityError, makeString("Blocked attempt to use ", functionName, " to change session history URL from ", documentURL.stringCenterEllipsizedToLength(), " to ", fullURL.stringCenterEllipsizedToLength(), ". ", suffix) };
+ };
+ if (!protocolHostAndPortAreEqual(fullURL, documentURL) || fullURL.user() != documentURL.user() || fullURL.pass() != documentURL.pass())
+ return createBlockedURLSecurityErrorWithMessageSuffix("Protocols, domains, ports, usernames, and passwords must match.");
+ if (!m_frame->document()->securityOrigin().canRequest(fullURL) && (fullURL.path() != documentURL.path() || fullURL.query() != documentURL.query()))
+ return createBlockedURLSecurityErrorWithMessageSuffix("Paths and fragments must match for a sandboxed document.");
+
Document* mainDocument = m_frame->page()->mainFrame().document();
History* mainHistory = nullptr;
if (mainDocument) {
Modified: trunk/Source/WebCore/page/SecurityOrigin.cpp (221977 => 221978)
--- trunk/Source/WebCore/page/SecurityOrigin.cpp 2017-09-13 17:50:48 UTC (rev 221977)
+++ trunk/Source/WebCore/page/SecurityOrigin.cpp 2017-09-13 18:06:24 UTC (rev 221978)
@@ -66,7 +66,7 @@
{
// FIXME: Update this callsite to use the innerURL member function when
// we finish implementing it.
- return URL(ParsedURLString, decodeURLEscapeSequences(url.path()));
+ return { URL(), decodeURLEscapeSequences(url.path()) };
}
static RefPtr<SecurityOrigin> getCachedOrigin(const URL& url)