WebKit Bugzilla
Attachment 339973 Details for
Bug 184996
: Add initial support for 'Cross-Origin-Options' HTTP response header
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
WIP patch
184996_Cross_Origin_Options_header_wip.patch (text/plain), 60.81 KB, created by
Chris Dumez
on 2018-05-09 09:54:01 PDT
(
hide
)
Description:
WIP patch
Filename:
MIME Type:
Creator:
Chris Dumez
Created:
2018-05-09 09:54:01 PDT
Size:
60.81 KB
patch
obsolete
>diff --git a/LayoutTests/http/wpt/cross-origin-options/allow-postmessage-expected.txt b/LayoutTests/http/wpt/cross-origin-options/allow-postmessage-expected.txt >new file mode 100644 >index 00000000000..0e9998789a6 >--- /dev/null >+++ b/LayoutTests/http/wpt/cross-origin-options/allow-postmessage-expected.txt >@@ -0,0 +1,5 @@ >+ >+ >+PASS postMessage() on Cross-origin iframe with 'Cross-Origin-Options: allow-postmessage' HTTP header >+PASS postMessage() on Cross-origin popup with 'Cross-Origin-Options: allow-postmessage' HTTP header >+ >diff --git a/LayoutTests/http/wpt/cross-origin-options/allow-postmessage.html b/LayoutTests/http/wpt/cross-origin-options/allow-postmessage.html >new file mode 100644 >index 00000000000..9a78df5e9d3 >--- /dev/null >+++ b/LayoutTests/http/wpt/cross-origin-options/allow-postmessage.html >@@ -0,0 +1,45 @@ >+<!DOCTYPE html> >+<html> >+<head> >+<meta charset="utf-8"> >+<title>Tests that postMessage() works when 'Cross-Origin-Options: allow-postmessage' HTTP header is served</title> >+<script src="/resources/testharness.js"></script> >+<script src="/resources/testharnessreport.js"></script> >+<script src="/common/utils.js"></script> >+<script src="/common/get-host-info.sub.js"></script> >+<script src="resources/utils.js"></script> >+</head> >+<body> >+<script> >+ >+promise_test(function(test) { >+ return withIframe("cross-origin-options-allow-postmessage-pong.py", true /* isCrossOrigin */).then((f) => { >+ return new Promise((resolve) => { >+ window.onmessage = (msg) => { >+ window.onmessage = null; >+ assert_equals(msg.data, "PONG"); >+ assert_equals(msg.source, f.contentWindow); >+ resolve(); >+ }; >+ f.contentWindow.postMessage("PING", "*"); >+ }); >+ }); >+}, "postMessage() on Cross-origin iframe with 'Cross-Origin-Options: allow-postmessage' HTTP header"); >+ >+promise_test(function(test) { >+ return withPopup("cross-origin-options-allow-postmessage-pong.py", true /* isCrossOrigin */).then((result) => { >+ return new Promise((resolve) => { >+ window.onmessage = (msg) => { >+ window.onmessage = null; >+ assert_equals(msg.data, "PONG"); >+ assert_equals(msg.source, result.window); >+ resolve(); >+ }; >+ result.window.postMessage("PING", "*"); >+ }); >+ }); >+}, "postMessage() on Cross-origin popup with 'Cross-Origin-Options: allow-postmessage' HTTP header"); >+ >+</script> >+</body> >+</html> >diff --git a/LayoutTests/http/wpt/cross-origin-options/cross-origin-options-header-expected.txt b/LayoutTests/http/wpt/cross-origin-options/cross-origin-options-header-expected.txt >new file mode 100644 >index 00000000000..fb310842fe4 >--- /dev/null >+++ b/LayoutTests/http/wpt/cross-origin-options/cross-origin-options-header-expected.txt >@@ -0,0 +1,19 @@ >+ >+ >+PASS Cross-origin iframe with 'Cross-Origin-Options: deny' HTTP header >+PASS Cross-origin iframe with 'Cross-Origin-Options: allow-postmessage' HTTP header >+PASS Cross-origin iframe with 'Cross-Origin-Options: allow' HTTP header >+PASS Cross-origin iframe with 'Cross-Origin-Options: invalid' HTTP header >+PASS Same-origin iframe with 'Cross-Origin-Options: deny' HTTP header >+PASS Same-origin iframe with 'Cross-Origin-Options: allow-postmessage' HTTP header >+PASS Same-origin iframe with 'Cross-Origin-Options: allow' HTTP header >+PASS Same-origin iframe with 'Cross-Origin-Options: invalid' HTTP header >+PASS Cross-origin popup with 'Cross-Origin-Options: deny' HTTP header >+PASS Cross-origin popup with 'Cross-Origin-Options: allow-postmessage' HTTP header >+PASS Cross-origin popup with 'Cross-Origin-Options: allow' HTTP header >+PASS Cross-origin popup with 'Cross-Origin-Options: invalid' HTTP header >+PASS Same-origin popup with 'Cross-Origin-Options: deny' HTTP header >+PASS Same-origin popup with 'Cross-Origin-Options: allow-postmessage' HTTP header >+PASS Same-origin popup with 'Cross-Origin-Options: allow' HTTP header >+PASS Same-origin popup with 'Cross-Origin-Options: invalid' HTTP header >+ >diff --git a/LayoutTests/http/wpt/cross-origin-options/cross-origin-options-header.html b/LayoutTests/http/wpt/cross-origin-options/cross-origin-options-header.html >new file mode 100644 >index 00000000000..2215d0fd728 >--- /dev/null >+++ b/LayoutTests/http/wpt/cross-origin-options/cross-origin-options-header.html >@@ -0,0 +1,172 @@ >+<!DOCTYPE html> >+<html> >+<head> >+<meta charset="utf-8"> >+<title>Basic testing for Cross-Origin-Options HTTP header</title> >+<script src="/resources/testharness.js"></script> >+<script src="/resources/testharnessreport.js"></script> >+<script src="/common/utils.js"></script> >+<script src="/common/get-host-info.sub.js"></script> >+<script src="resources/utils.js"></script> >+</head> >+<body> >+<script> >+ >+// Test frame has a subframe so we expect an indexed property with index 0. >+crossOriginPropertyNames.push('0'); >+ >+function checkIframePropertyValues(w) >+{ >+ assert_equals(w.parent, window, "'parent' property value"); >+ assert_equals(w.top, window, "'top' property value"); >+ assert_equals(w.opener, null, "'opener' property value"); >+ assert_equals(w.length, 1, "'length' property value"); >+ assert_not_throwing(function() { w[0]; }, "Subframe access via index"); >+ assert_equals(w['subframe'], w[0], "Subframe access by name"); >+} >+ >+function checkPopupPropertyValues(w) >+{ >+ assert_equals(w.parent, w, "'parent' property value"); >+ assert_equals(w.top, w, "'top' property value"); >+ assert_equals(w.opener, window, "'opener' property value"); >+ assert_equals(w.length, 1, "'length' property value"); >+ assert_not_throwing(function() { w[0]; }, "Subframe access via index"); >+ assert_equals(w['subframe'], w[0], "Subframe access by name"); >+} >+ >+promise_test(function(test) { >+ return withIframe("serve-cross-origin-options-header.py?value=deny", true /* isCrossOrigin */).then((f) => { >+ testCrossOriginOption(f.contentWindow, "deny", true /* isCrossOrigin */); >+ }); >+}, "Cross-origin iframe with 'Cross-Origin-Options: deny' HTTP header"); >+ >+promise_test(function(test) { >+ return withIframe("serve-cross-origin-options-header.py?value=allow-postmessage", true /* isCrossOrigin */).then((f) => { >+ testCrossOriginOption(f.contentWindow, "allow-postmessage", true /* isCrossOrigin */); >+ }); >+}, "Cross-origin iframe with 'Cross-Origin-Options: allow-postmessage' HTTP header"); >+ >+promise_test(function(test) { >+ return withIframe("serve-cross-origin-options-header.py?value=allow", true /* isCrossOrigin */).then((f) => { >+ const w = f.contentWindow; >+ testCrossOriginOption(w, "allow", true /* isCrossOrigin */); >+ >+ checkIframePropertyValues(w); >+ }); >+}, "Cross-origin iframe with 'Cross-Origin-Options: allow' HTTP header"); >+ >+promise_test(function(test) { >+ return withIframe("serve-cross-origin-options-header.py?value=invalid", true /* isCrossOrigin */).then((f) => { >+ const w = f.contentWindow; >+ testCrossOriginOption(w, "allow", true /* isCrossOrigin */); >+ >+ checkIframePropertyValues(w); >+ }); >+}, "Cross-origin iframe with 'Cross-Origin-Options: invalid' HTTP header"); >+ >+promise_test(function(test) { >+ return withIframe("serve-cross-origin-options-header.py?value=deny", false /* isCrossOrigin */).then((f) => { >+ const w = f.contentWindow; >+ testCrossOriginOption(w, "deny", false /* isCrossOrigin */); >+ >+ checkIframePropertyValues(w); >+ }); >+}, "Same-origin iframe with 'Cross-Origin-Options: deny' HTTP header"); >+ >+promise_test(function(test) { >+ return withIframe("serve-cross-origin-options-header.py?value=allow-postmessage", false /* isCrossOrigin */).then((f) => { >+ const w = f.contentWindow; >+ testCrossOriginOption(w, "allow-postmessage", false /* isCrossOrigin */); >+ >+ checkIframePropertyValues(w); >+ }); >+}, "Same-origin iframe with 'Cross-Origin-Options: allow-postmessage' HTTP header"); >+ >+promise_test(function(test) { >+ return withIframe("serve-cross-origin-options-header.py?value=allow", false /* isCrossOrigin */).then((f) => { >+ const w = f.contentWindow; >+ testCrossOriginOption(w, "allow", false /* isCrossOrigin */); >+ >+ checkIframePropertyValues(w); >+ }); >+}, "Same-origin iframe with 'Cross-Origin-Options: allow' HTTP header"); >+ >+promise_test(function(test) { >+ return withIframe("serve-cross-origin-options-header.py?value=invalid", false /* isCrossOrigin */).then((f) => { >+ const w = f.contentWindow; >+ testCrossOriginOption(w, "allow", false /* isCrossOrigin */); >+ >+ checkIframePropertyValues(w); >+ }); >+}, "Same-origin iframe with 'Cross-Origin-Options: invalid' HTTP header"); >+ >+promise_test(function(test) { >+ return withPopup("serve-cross-origin-options-header.py?value=deny", true /* isCrossOrigin */).then((result) => { >+ testCrossOriginOption(result.window, "deny", true /* isCrossOrigin */); >+ }); >+}, "Cross-origin popup with 'Cross-Origin-Options: deny' HTTP header"); >+ >+promise_test(function(test) { >+ return withPopup("serve-cross-origin-options-header.py?value=allow-postmessage", true /* isCrossOrigin */).then((result) => { >+ testCrossOriginOption(result.window, "allow-postmessage", true /* isCrossOrigin */); >+ }); >+}, "Cross-origin popup with 'Cross-Origin-Options: allow-postmessage' HTTP header"); >+ >+promise_test(function(test) { >+ return withPopup("serve-cross-origin-options-header.py?value=allow", true /* isCrossOrigin */).then((result) => { >+ const w = result.window; >+ testCrossOriginOption(w, "allow", true /* isCrossOrigin */); >+ >+ checkPopupPropertyValues(w); >+ }); >+}, "Cross-origin popup with 'Cross-Origin-Options: allow' HTTP header"); >+ >+promise_test(function(test) { >+ return withPopup("serve-cross-origin-options-header.py?value=invalid", true /* isCrossOrigin */).then((result) => { >+ const w = result.window; >+ testCrossOriginOption(w, "allow", true /* isCrossOrigin */); >+ >+ checkPopupPropertyValues(w); >+ }); >+}, "Cross-origin popup with 'Cross-Origin-Options: invalid' HTTP header"); >+ >+promise_test(function(test) { >+ return withPopup("serve-cross-origin-options-header.py?value=deny", false /* isCrossOrigin */).then((result) => { >+ const w = result.window; >+ testCrossOriginOption(w, "deny", false /* isCrossOrigin */); >+ >+ checkPopupPropertyValues(w); >+ }); >+}, "Same-origin popup with 'Cross-Origin-Options: deny' HTTP header"); >+ >+promise_test(function(test) { >+ return withPopup("serve-cross-origin-options-header.py?value=allow-postmessage", false /* isCrossOrigin */).then((result) => { >+ const w = result.window; >+ testCrossOriginOption(w, "allow-postmessage", false /* isCrossOrigin */); >+ >+ checkPopupPropertyValues(w); >+ }); >+}, "Same-origin popup with 'Cross-Origin-Options: allow-postmessage' HTTP header"); >+ >+promise_test(function(test) { >+ return withPopup("serve-cross-origin-options-header.py?value=allow", false /* isCrossOrigin */).then((result) => { >+ const w = result.window; >+ testCrossOriginOption(w, "allow", false /* isCrossOrigin */); >+ >+ checkPopupPropertyValues(w); >+ }); >+}, "Same-origin popup with 'Cross-Origin-Options: allow' HTTP header"); >+ >+promise_test(function(test) { >+ return withPopup("serve-cross-origin-options-header.py?value=invalid", false /* isCrossOrigin */).then((result) => { >+ const w = result.window; >+ testCrossOriginOption(w, "allow", false /* isCrossOrigin */); >+ >+ checkPopupPropertyValues(w); >+ }); >+}, "Same-origin popup with 'Cross-Origin-Options: invalid' HTTP header"); >+ >+</script> >+</body> >+</html> >diff --git a/LayoutTests/http/wpt/cross-origin-options/resources/cross-origin-options-allow-postmessage-pong.py b/LayoutTests/http/wpt/cross-origin-options/resources/cross-origin-options-allow-postmessage-pong.py >new file mode 100644 >index 00000000000..393bcc8d9a2 >--- /dev/null >+++ b/LayoutTests/http/wpt/cross-origin-options/resources/cross-origin-options-allow-postmessage-pong.py >@@ -0,0 +1,16 @@ >+def main(request, response): >+ headers = [("Content-Type", "text/html"), >+ ("Cross-Origin-Options", "allow-postmessage"),] >+ return 200, headers, """<script> >+ addEventListener('message', (msg) => { >+ if (event.data === "PING") >+ event.source.postMessage("PONG", "*"); >+ }); >+ window.addEventListener('load', () => { >+ const ownerWindow = window.opener ? window.opener : window.top; >+ try { >+ ownerWindow.postMessage("READY", "*"); >+ } catch (e) { } >+ }); >+ </script> >+ """ >diff --git a/LayoutTests/http/wpt/cross-origin-options/resources/serve-cross-origin-options-header.py b/LayoutTests/http/wpt/cross-origin-options/resources/serve-cross-origin-options-header.py >new file mode 100644 >index 00000000000..ea90655adc3 >--- /dev/null >+++ b/LayoutTests/http/wpt/cross-origin-options/resources/serve-cross-origin-options-header.py >@@ -0,0 +1,14 @@ >+def main(request, response): >+ headers = [("Content-Type", "text/html"), >+ ("Cross-Origin-Options", request.GET['value']),] >+ return 200, headers, """TEST >+ <iframe name="subframe"></iframe> >+ <script> >+ window.addEventListener('load', () => { >+ const ownerWindow = window.opener ? window.opener : window.top; >+ try { >+ ownerWindow.postMessage("READY", "*"); >+ } catch (e) { } >+ }); >+ </script> >+ """ >diff --git a/LayoutTests/http/wpt/cross-origin-options/resources/utils.js b/LayoutTests/http/wpt/cross-origin-options/resources/utils.js >new file mode 100644 >index 00000000000..4fc2cd0f243 >--- /dev/null >+++ b/LayoutTests/http/wpt/cross-origin-options/resources/utils.js >@@ -0,0 +1,147 @@ >+const RESOURCES_DIR = "/WebKit/cross-origin-options/resources/"; >+ >+function isCrossOriginWindow(w) >+{ >+ try { >+ w.name; >+ } catch (e) { >+ return true; >+ } >+ return false; >+} >+ >+async function waitForCrossOriginLoad(w) >+{ >+ return new Promise((resolve) => { >+ let handle = setInterval(() => { >+ if (isCrossOriginWindow(w)) { >+ clearInterval(handle); >+ try { >+ w.postMessage; >+ window.addEventListener('message', (msg) => { >+ if (msg.source != w || msg.data != "READY") >+ return; >+ resolve(); >+ }); >+ } catch (e) { >+ resolve(); >+ } >+ } >+ }, 5); >+ }); >+} >+ >+async function withIframe(resourceFile, crossOrigin) >+{ >+ return new Promise((resolve) => { >+ let resourceURL = crossOrigin ? get_host_info().HTTP_REMOTE_ORIGIN : get_host_info().HTTP_ORIGIN; >+ resourceURL += RESOURCES_DIR; >+ resourceURL += resourceFile; >+ let frame = document.createElement("iframe"); >+ frame.src = resourceURL; >+ if (crossOrigin) { >+ document.body.appendChild(frame); >+ waitForCrossOriginLoad(frame.contentWindow).then(() => { >+ resolve(frame); >+ }); >+ } else { >+ frame.onload = function() { resolve(frame); }; >+ document.body.appendChild(frame); >+ } >+ }); >+} >+ >+async function withPopup(resourceFile, crossOrigin) >+{ >+ return new Promise((resolve) => { >+ let resourceURL = crossOrigin ? get_host_info().HTTP_REMOTE_ORIGIN : get_host_info().HTTP_ORIGIN; >+ resourceURL += RESOURCES_DIR; >+ resourceURL += resourceFile; >+ >+ let w = open(resourceURL); >+ if (crossOrigin) { >+ waitForCrossOriginLoad(w).then(() => { >+ resolve({ 'window': w }); >+ }); >+ } else { >+ w.onload = function() { resolve({ 'window': w }); }; >+ } >+ }); >+} >+ >+const crossOriginPropertyNames = [ 'blur', 'close', 'closed', 'focus', 'frames', 'length', 'location', 'opener', 'parent', 'postMessage', 'self', 'top', 'window' ]; >+const forbiddenPropertiesCrossOrigin = ["name", "document", "history", "locationbar", "status", "frameElement", "navigator", "alert", "localStorage", "sessionStorage", "event", "foo", "bar"]; >+ >+function assert_not_throwing(f, message) >+{ >+ try { >+ f(); >+ } catch (e) { >+ assert_unreached(message); >+ } >+} >+ >+function checkCrossOriginPropertiesAccess(w) >+{ >+ for (let crossOriginPropertyName of crossOriginPropertyNames) >+ assert_not_throwing(function() { w[crossOriginPropertyName]; }, "Accessing property '" + crossOriginPropertyName +"' on Window should not throw"); >+ >+ assert_false(w.closed, "'closed' property value"); >+ assert_equals(w.frames, w, "'frames' property value"); >+ assert_equals(w.self, w, "'self' property value"); >+ assert_equals(w.window, w, "'window' property value"); >+ >+ assert_not_throwing(function() { w.blur(); }, "Calling blur() on Window should should throw"); >+ assert_not_throwing(function() { w.focus(); }, "Calling focus() on Window should should throw"); >+ assert_not_throwing(function() { w.postMessage('test', '*'); }, "Calling postMessage() on Window should should throw"); >+} >+ >+function testCrossOriginOption(w, headerValue, isCrossOrigin) >+{ >+ if (!isCrossOrigin) { >+ checkCrossOriginPropertiesAccess(w); >+ for (let forbiddenPropertyCrossOrigin of forbiddenPropertiesCrossOrigin) >+ assert_not_throwing(function() { eval("w." + forbiddenPropertyCrossOrigin); }, "Accessing property '" + forbiddenPropertyCrossOrigin + "' on Window should not throw"); >+ assert_not_throwing(function() { w.foo = 1; }, "Setting expando property should not throw"); >+ assert_equals(w.foo, 1, "expando property value"); >+ return; >+ } >+ >+ // Cross-origin case. >+ for (let forbiddenPropertyCrossOrigin of forbiddenPropertiesCrossOrigin) { >+ assert_throws("SecurityError", function() { eval("w." + forbiddenPropertyCrossOrigin); }, "Accessing property '" + forbiddenPropertyCrossOrigin + "' on Window should throw"); >+ let desc = Object.getOwnPropertyDescriptor(window, forbiddenPropertyCrossOrigin); >+ if (desc && desc.value) >+ assert_throws("SecurityError", function() { desc.value.call(w); }, "Calling function '" + forbiddenPropertyCrossOrigin + "' on Window should throw (using getter from other window)"); >+ else if (desc && desc.get) >+ assert_throws("SecurityError", function() { desc.get.call(w); }, "Accessing property '" + forbiddenPropertyCrossOrigin + "' on Window should throw (using getter from other window)"); >+ } >+ assert_throws("SecurityError", function() { w.foo = 1; }, "Setting an expando property should throw"); >+ >+ if (headerValue == "deny" || headerValue == "allow-postmessage") { >+ for (let crossOriginPropertyName of crossOriginPropertyNames) { >+ if (headerValue == "allow-postmessage" && crossOriginPropertyName == "postMessage") { >+ assert_not_throwing(function() { w[crossOriginPropertyName]; }, "Accessing property '" + crossOriginPropertyName +"' on Window should not throw"); >+ } else { >+ assert_throws("SecurityError", function() { w[crossOriginPropertyName]; }, "Accessing '" + crossOriginPropertyName + "' property"); >+ >+ let desc = Object.getOwnPropertyDescriptor(window, crossOriginPropertyName); >+ if (desc && desc.value) >+ assert_throws("SecurityError", function() { desc.value.call(w); }, "Calling function '" + crossOriginPropertyName + "' on Window should throw (using getter from other window)"); >+ else if (desc && desc.get) >+ assert_throws("SecurityError", function() { desc.get.call(w); }, "Accessing property '" + crossOriginPropertyName + "' on Window should throw (using getter from other window)"); >+ } >+ } >+ if (headerValue == "allow-postmessage") { >+ assert_not_throwing(function() { w.postMessage('test', '*'); }, "Calling postMessage() on Window should not throw"); >+ assert_not_throwing(function() { Object.getOwnPropertyDescriptor(window, 'postMessage').value.call(w, 'test', '*'); }, "Calling postMessage() on Window should not throw (using getter from other window)"); >+ } >+ >+ assert_array_equals(Object.getOwnPropertyNames(w).sort(), headerValue == "allow-postmessage" ? ['postMessage'] : [], "Object.getOwnPropertyNames()"); >+ >+ return; >+ } >+ >+ assert_array_equals(Object.getOwnPropertyNames(w).sort(), crossOriginPropertyNames.sort(), "Object.getOwnPropertyNames()"); >+ checkCrossOriginPropertiesAccess(w); >+} >diff --git a/Source/WebCore/bindings/js/JSDOMBindingSecurity.cpp b/Source/WebCore/bindings/js/JSDOMBindingSecurity.cpp >index e2816dbbf88..2b675851160 100644 >--- a/Source/WebCore/bindings/js/JSDOMBindingSecurity.cpp >+++ b/Source/WebCore/bindings/js/JSDOMBindingSecurity.cpp >@@ -25,6 +25,7 @@ > #include "DOMWindow.h" > #include "Document.h" > #include "Frame.h" >+#include "HTTPParsers.h" > #include "JSDOMExceptionHandling.h" > #include "JSDOMWindowBase.h" > #include "SecurityOrigin.h" >@@ -99,4 +100,19 @@ bool BindingSecurity::shouldAllowAccessToNode(JSC::ExecState& state, Node* targe > return !target || canAccessDocument(&state, &target->document(), LogSecurityError); > } > >+bool BindingSecurity::shouldAllowAccessToDOMWindowGivenMinimumCrossOriginOptions(JSC::ExecState* state, DOMWindow& target, CrossOriginOptions minimumCrossOriginOptions, SecurityReportingOption reportingOption) >+{ >+ DOMWindow& source = activeDOMWindow(*state); >+ ASSERT(minimumCrossOriginOptions > CrossOriginOptions::Deny); >+ >+ static_assert(CrossOriginOptions::Deny < CrossOriginOptions::AllowPostMessage && CrossOriginOptions::AllowPostMessage < CrossOriginOptions::Allow, "More restrictive cross-origin options should have lower values"); >+ >+ // Fast path. >+ auto effectiveCrossOriginOptions = std::min(source.crossOriginOptions(), target.crossOriginOptions()); >+ if (effectiveCrossOriginOptions >= minimumCrossOriginOptions) >+ return true; >+ >+ return shouldAllowAccessToDOMWindow(state, target, reportingOption); >+} >+ > } // namespace WebCore >diff --git a/Source/WebCore/bindings/js/JSDOMBindingSecurity.h b/Source/WebCore/bindings/js/JSDOMBindingSecurity.h >index 7b4085a51ba..5c2af998857 100644 >--- a/Source/WebCore/bindings/js/JSDOMBindingSecurity.h >+++ b/Source/WebCore/bindings/js/JSDOMBindingSecurity.h >@@ -36,6 +36,8 @@ class DOMWindow; > class Frame; > class Node; > >+enum class CrossOriginOptions; >+ > void printErrorMessageForFrame(Frame*, const String& message); > > enum SecurityReportingOption { DoNotReportSecurityError, LogSecurityError, ThrowSecurityError }; >@@ -53,6 +55,8 @@ bool shouldAllowAccessToFrame(JSC::ExecState*, Frame*, SecurityReportingOption = > bool shouldAllowAccessToFrame(JSC::ExecState&, Frame&, String& message); > bool shouldAllowAccessToNode(JSC::ExecState&, Node*); > >+bool shouldAllowAccessToDOMWindowGivenMinimumCrossOriginOptions(JSC::ExecState*, DOMWindow&, CrossOriginOptions, SecurityReportingOption = LogSecurityError); >+ > }; > > template<typename T> inline T* BindingSecurity::checkSecurityForNode(JSC::ExecState& state, T& node) >diff --git a/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp b/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp >index 281ea4e7004..5db1d5ccbe1 100644 >--- a/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp >+++ b/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp >@@ -26,6 +26,7 @@ > #include "HTMLCollection.h" > #include "HTMLDocument.h" > #include "HTMLFrameOwnerElement.h" >+#include "HTTPParsers.h" > #include "JSDOMBindingSecurity.h" > #include "JSDOMConvertNullable.h" > #include "JSDOMConvertNumbers.h" >@@ -55,6 +56,12 @@ > namespace WebCore { > using namespace JSC; > >+static CrossOriginOptions effectiveCrossOriginOptionsForAccess(ExecState& state, AbstractDOMWindow& target) >+{ >+ static_assert(CrossOriginOptions::Deny < CrossOriginOptions::AllowPostMessage && CrossOriginOptions::AllowPostMessage < CrossOriginOptions::Allow, "More restrictive cross-origin options should have lower values"); >+ return std::min(activeDOMWindow(state).crossOriginOptions(), target.crossOriginOptions()); >+} >+ > EncodedJSValue JSC_HOST_CALL jsDOMWindowInstanceFunctionShowModalDialog(ExecState*); > > void JSDOMWindow::visitAdditionalChildren(SlotVisitor& visitor) >@@ -80,9 +87,9 @@ static EncodedJSValue jsDOMWindowWebKit(ExecState* exec, EncodedJSValue thisValu > #endif > > template <DOMWindowType windowType> >-bool jsDOMWindowGetOwnPropertySlotRestrictedAccess(JSDOMGlobalObject* thisObject, AbstractFrame* frame, ExecState* exec, PropertyName propertyName, PropertySlot& slot, const String& errorMessage) >+bool jsDOMWindowGetOwnPropertySlotRestrictedAccess(JSDOMGlobalObject* thisObject, AbstractDOMWindow& window, ExecState& state, PropertyName propertyName, PropertySlot& slot, const String& errorMessage) > { >- VM& vm = exec->vm(); >+ VM& vm = state.vm(); > auto scope = DECLARE_THROW_SCOPE(vm); > > auto& builtinNames = static_cast<JSVMClientData*>(vm.clientData)->builtinNames(); >@@ -93,6 +100,21 @@ bool jsDOMWindowGetOwnPropertySlotRestrictedAccess(JSDOMGlobalObject* thisObject > return true; > } > >+ switch (effectiveCrossOriginOptionsForAccess(state, window)) { >+ case CrossOriginOptions::AllowPostMessage: >+ if (propertyName == builtinNames.postMessagePublicName()) { >+ slot.setCustom(thisObject, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum), windowType == DOMWindowType::Remote ? nonCachingStaticFunctionGetter<jsRemoteDOMWindowInstanceFunctionPostMessage, 0> : nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionPostMessage, 2>); >+ return true; >+ } >+ FALLTHROUGH; >+ case CrossOriginOptions::Deny: >+ throwSecurityError(state, scope, errorMessage); >+ slot.setUndefined(); >+ return false; >+ case CrossOriginOptions::Allow: >+ break; >+ } >+ > // These are the functions we allow access to cross-origin (DoNotCheckSecurity in IDL). > // Always provide the original function, on a fresh uncached function object. > if (propertyName == builtinNames.blurPublicName()) { >@@ -136,7 +158,7 @@ bool jsDOMWindowGetOwnPropertySlotRestrictedAccess(JSDOMGlobalObject* thisObject > // For any other entries in the static property table, deny access. (Early return also prevents > // named getter from returning frames with matching names - this seems a little questionable, see > // FIXME comment on prototype search below.) >- throwSecurityError(*exec, scope, errorMessage); >+ throwSecurityError(state, scope, errorMessage); > slot.setUndefined(); > return false; > } >@@ -146,19 +168,20 @@ bool jsDOMWindowGetOwnPropertySlotRestrictedAccess(JSDOMGlobalObject* thisObject > // properties that are in Moz but not IE. Since we have some of these, we have to do it > // the Moz way. > // FIXME: Add support to named attributes on RemoteFrames. >+ auto* frame = window.frame(); > if (frame && is<Frame>(*frame)) { > if (auto* scopedChild = downcast<Frame>(*frame).tree().scopedChild(propertyNameToAtomicString(propertyName))) { >- slot.setValue(thisObject, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::DontEnum, toJS(exec, scopedChild->document()->domWindow())); >+ slot.setValue(thisObject, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::DontEnum, toJS(&state, scopedChild->document()->domWindow())); > return true; > } > } > >- throwSecurityError(*exec, scope, errorMessage); >+ throwSecurityError(state, scope, errorMessage); > slot.setUndefined(); > return false; > } >-template bool jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Local>(JSDOMGlobalObject*, AbstractFrame*, ExecState*, PropertyName, PropertySlot&, const String&); >-template bool jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Remote>(JSDOMGlobalObject*, AbstractFrame*, ExecState*, PropertyName, PropertySlot&, const String&); >+template bool jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Local>(JSDOMGlobalObject*, AbstractDOMWindow&, ExecState&, PropertyName, PropertySlot&, const String&); >+template bool jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Remote>(JSDOMGlobalObject*, AbstractDOMWindow&, ExecState&, PropertyName, PropertySlot&, const String&); > > // Property access sequence is: > // (1) indexed properties, >@@ -172,13 +195,12 @@ bool JSDOMWindow::getOwnPropertySlot(JSObject* object, ExecState* state, Propert > return getOwnPropertySlotByIndex(object, state, index.value(), slot); > > auto* thisObject = jsCast<JSDOMWindow*>(object); >- auto* frame = thisObject->wrapped().frame(); > > // Hand off all cross-domain access to jsDOMWindowGetOwnPropertySlotRestrictedAccess. > String errorMessage; > if (!BindingSecurity::shouldAllowAccessToDOMWindow(*state, thisObject->wrapped(), errorMessage)) >- return jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Local>(thisObject, frame, state, propertyName, slot, errorMessage); >- >+ return jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Local>(thisObject, thisObject->wrapped(), *state, propertyName, slot, errorMessage); >+ > // FIXME: this need more explanation. > // (Particularly, is it correct that this exists here but not in getOwnPropertySlotByIndex?) > slot.setWatchpointSet(thisObject->m_windowCloseWatchpoints); >@@ -186,6 +208,8 @@ bool JSDOMWindow::getOwnPropertySlot(JSObject* object, ExecState* state, Propert > // (2) Regular own properties. > PropertySlot slotCopy = slot; > if (Base::getOwnPropertySlot(thisObject, state, propertyName, slot)) { >+ auto* frame = thisObject->wrapped().frame(); >+ > // Detect when we're getting the property 'showModalDialog', this is disabled, and has its original value. > bool isShowModalDialogAndShouldHide = propertyName == static_cast<JSVMClientData*>(state->vm().clientData)->builtinNames().showModalDialogPublicName() > && (!frame || !DOMWindow::canShowModalDialog(*frame)) >@@ -213,22 +237,39 @@ bool JSDOMWindow::getOwnPropertySlot(JSObject* object, ExecState* state, Propert > bool JSDOMWindow::getOwnPropertySlotByIndex(JSObject* object, ExecState* state, unsigned index, PropertySlot& slot) > { > auto* thisObject = jsCast<JSDOMWindow*>(object); >- auto* frame = thisObject->wrapped().frame(); >+ auto& window = thisObject->wrapped(); >+ auto* frame = window.frame(); > > // Indexed getters take precendence over regular properties, so caching would be invalid. > slot.disableCaching(); > >+ String errorMessage; >+ std::optional<bool> cachedIsCrossOriginAccess; >+ auto isCrossOriginAccess = [&] { >+ if (!cachedIsCrossOriginAccess) >+ cachedIsCrossOriginAccess = !BindingSecurity::shouldAllowAccessToDOMWindow(*state, window, errorMessage); >+ return *cachedIsCrossOriginAccess; >+ }; >+ > // (1) First, indexed properties. >- // These are also allowed cross-orgin, so come before the access check. >- if (frame && index < frame->tree().scopedChildCount()) { >- slot.setValue(thisObject, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly), toJS(state, frame->tree().scopedChild(index)->document()->domWindow())); >- return true; >+ // These are also allowed cross-origin, so come before the access check. >+ switch (effectiveCrossOriginOptionsForAccess(*state, window)) { >+ case CrossOriginOptions::Deny: >+ case CrossOriginOptions::AllowPostMessage: >+ if (isCrossOriginAccess()) >+ break; >+ FALLTHROUGH; >+ case CrossOriginOptions::Allow: >+ if (frame && index < frame->tree().scopedChildCount()) { >+ slot.setValue(thisObject, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly), toJS(state, frame->tree().scopedChild(index)->document()->domWindow())); >+ return true; >+ } >+ break; > } > > // Hand off all cross-domain/frameless access to jsDOMWindowGetOwnPropertySlotRestrictedAccess. >- String errorMessage; >- if (!BindingSecurity::shouldAllowAccessToDOMWindow(*state, thisObject->wrapped(), errorMessage)) >- return jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Local>(thisObject, frame, state, Identifier::from(state, index), slot, errorMessage); >+ if (isCrossOriginAccess()) >+ return jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Local>(thisObject, window, *state, Identifier::from(state, index), slot, errorMessage); > > // (2) Regular own properties. > return Base::getOwnPropertySlotByIndex(thisObject, state, index, slot); >@@ -287,8 +328,10 @@ bool JSDOMWindow::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned > } > > // https://html.spec.whatwg.org/#crossoriginproperties-(-o-) >-static void addCrossOriginWindowPropertyNames(VM& vm, PropertyNameArray& propertyNames) >+static void addCrossOriginWindowPropertyNames(ExecState& state, AbstractDOMWindow& window, PropertyNameArray& propertyNames) > { >+ auto& vm = state.vm(); >+ > static const Identifier* const properties[] = { > &static_cast<JSVMClientData*>(vm.clientData)->builtinNames().blurPublicName(), > &static_cast<JSVMClientData*>(vm.clientData)->builtinNames().closePublicName(), >@@ -304,8 +347,18 @@ static void addCrossOriginWindowPropertyNames(VM& vm, PropertyNameArray& propert > &static_cast<JSVMClientData*>(vm.clientData)->builtinNames().topPublicName(), > &static_cast<JSVMClientData*>(vm.clientData)->builtinNames().windowPublicName() > }; >- for (auto* property : properties) >- propertyNames.add(*property); >+ >+ switch (effectiveCrossOriginOptionsForAccess(state, window)) { >+ case CrossOriginOptions::Allow: >+ for (auto* property : properties) >+ propertyNames.add(*property); >+ break; >+ case CrossOriginOptions::AllowPostMessage: >+ propertyNames.add(static_cast<JSVMClientData*>(vm.clientData)->builtinNames().postMessagePublicName()); >+ break; >+ case CrossOriginOptions::Deny: >+ break; >+ } > } > > static void addScopedChildrenIndexes(ExecState& state, DOMWindow& window, PropertyNameArray& propertyNames) >@@ -318,17 +371,25 @@ static void addScopedChildrenIndexes(ExecState& state, DOMWindow& window, Proper > if (!frame) > return; > >+ switch (effectiveCrossOriginOptionsForAccess(state, window)) { >+ case CrossOriginOptions::Allow: >+ break; >+ case CrossOriginOptions::Deny: >+ case CrossOriginOptions::AllowPostMessage: >+ return; >+ } >+ > unsigned scopedChildCount = frame->tree().scopedChildCount(); > for (unsigned i = 0; i < scopedChildCount; ++i) > propertyNames.add(Identifier::from(&state, i)); > } > > // https://html.spec.whatwg.org/#crossoriginownpropertykeys-(-o-) >-void addCrossOriginWindowOwnPropertyNames(ExecState& state, PropertyNameArray& propertyNames) >+void addCrossOriginWindowOwnPropertyNames(ExecState& state, AbstractDOMWindow& window, PropertyNameArray& propertyNames) > { >- VM& vm = state.vm(); >- addCrossOriginWindowPropertyNames(vm, propertyNames); >+ addCrossOriginWindowPropertyNames(state, window, propertyNames); > >+ auto& vm = state.vm(); > propertyNames.add(vm.propertyNames->toStringTagSymbol); > propertyNames.add(vm.propertyNames->hasInstanceSymbol); > propertyNames.add(vm.propertyNames->isConcatSpreadableSymbol); >@@ -343,7 +404,7 @@ void JSDOMWindow::getOwnPropertyNames(JSObject* object, ExecState* exec, Propert > > if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), DoNotReportSecurityError)) { > if (mode.includeDontEnumProperties()) >- addCrossOriginWindowOwnPropertyNames(*exec, propertyNames); >+ addCrossOriginWindowOwnPropertyNames(*exec, thisObject->wrapped(), propertyNames); > return; > } > Base::getOwnPropertyNames(thisObject, exec, propertyNames, mode); >diff --git a/Source/WebCore/bindings/js/JSDOMWindowCustom.h b/Source/WebCore/bindings/js/JSDOMWindowCustom.h >index cfda3e03866..9fe97422194 100644 >--- a/Source/WebCore/bindings/js/JSDOMWindowCustom.h >+++ b/Source/WebCore/bindings/js/JSDOMWindowCustom.h >@@ -22,6 +22,7 @@ > > namespace WebCore { > >+class AbstractDOMWindow; > class AbstractFrame; > > inline JSDOMWindow* asJSDOMWindow(JSC::JSGlobalObject* globalObject) >@@ -37,8 +38,8 @@ inline const JSDOMWindow* asJSDOMWindow(const JSC::JSGlobalObject* globalObject) > enum class DOMWindowType { Local, Remote }; > > template <DOMWindowType windowType> >-bool jsDOMWindowGetOwnPropertySlotRestrictedAccess(JSDOMGlobalObject*, AbstractFrame*, JSC::ExecState*, JSC::PropertyName, JSC::PropertySlot&, const String&); >+bool jsDOMWindowGetOwnPropertySlotRestrictedAccess(JSDOMGlobalObject*, AbstractDOMWindow&, JSC::ExecState&, JSC::PropertyName, JSC::PropertySlot&, const String&); > >-void addCrossOriginWindowOwnPropertyNames(JSC::ExecState&, JSC::PropertyNameArray&); >+void addCrossOriginWindowOwnPropertyNames(JSC::ExecState&, AbstractDOMWindow&, JSC::PropertyNameArray&); > > } // namespace WebCore >diff --git a/Source/WebCore/bindings/js/JSRemoteDOMWindowCustom.cpp b/Source/WebCore/bindings/js/JSRemoteDOMWindowCustom.cpp >index b7f7ae9851d..8cd42526b9a 100644 >--- a/Source/WebCore/bindings/js/JSRemoteDOMWindowCustom.cpp >+++ b/Source/WebCore/bindings/js/JSRemoteDOMWindowCustom.cpp >@@ -37,22 +37,19 @@ bool JSRemoteDOMWindow::getOwnPropertySlot(JSObject* object, ExecState* state, P > return getOwnPropertySlotByIndex(object, state, index.value(), slot); > > auto* thisObject = jsCast<JSRemoteDOMWindow*>(object); >- auto* frame = thisObject->wrapped().frame(); >- >- return jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Remote>(thisObject, frame, state, propertyName, slot, String()); >+ return jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Remote>(thisObject, thisObject->wrapped(), *state, propertyName, slot, String()); > } > > bool JSRemoteDOMWindow::getOwnPropertySlotByIndex(JSObject* object, ExecState* state, unsigned index, PropertySlot& slot) > { > auto* thisObject = jsCast<JSRemoteDOMWindow*>(object); >- auto* frame = thisObject->wrapped().frame(); > > // Indexed getters take precendence over regular properties, so caching would be invalid. > slot.disableCaching(); > > // FIXME: Add support for indexed properties. > >- return jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Remote>(thisObject, frame, state, Identifier::from(state, index), slot, String()); >+ return jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Remote>(thisObject, thisObject->wrapped(), *state, Identifier::from(state, index), slot, String()); > } > > bool JSRemoteDOMWindow::put(JSCell* cell, ExecState* state, PropertyName propertyName, JSValue value, PutPropertySlot& slot) >@@ -100,12 +97,14 @@ bool JSRemoteDOMWindow::deletePropertyByIndex(JSCell*, ExecState* state, unsigne > return false; > } > >-void JSRemoteDOMWindow::getOwnPropertyNames(JSObject*, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) >+void JSRemoteDOMWindow::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) > { >+ auto* thisObject = jsCast<JSRemoteDOMWindow*>(object); >+ > // FIXME: Add scoped children indexes. > > if (mode.includeDontEnumProperties()) >- addCrossOriginWindowOwnPropertyNames(*exec, propertyNames); >+ addCrossOriginWindowOwnPropertyNames(*exec, thisObject->wrapped(), propertyNames); > } > > bool JSRemoteDOMWindow::defineOwnProperty(JSC::JSObject*, JSC::ExecState* state, JSC::PropertyName, const JSC::PropertyDescriptor&, bool) >diff --git a/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm b/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm >index 30a374c2824..b69c6dc9cbc 100644 >--- a/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm >+++ b/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm >@@ -4706,7 +4706,13 @@ sub GenerateAttributeGetterBodyDefinition > !$attribute->extendedAttributes->{DoNotCheckSecurityOnGetter}) { > AddToImplIncludes("JSDOMBindingSecurity.h", $conditional); > if ($interface->type->name eq "DOMWindow") { >- push(@$outputArray, " if (!BindingSecurity::shouldAllowAccessToDOMWindow(&state, thisObject.wrapped(), ThrowSecurityError))\n"); >+ if ($attribute->extendedAttributes->{DoNotCheckSecurityIf}) { >+ my $crossOriginOptions = GetCrossOriginsOptionsFromExtendedAttributeValue($attribute->extendedAttributes->{DoNotCheckSecurityIf}); >+ AddToImplIncludes("HTTPParsers.h", $conditional); >+ push(@$outputArray, " if (!BindingSecurity::shouldAllowAccessToDOMWindowGivenMinimumCrossOriginOptions(&state, thisObject.wrapped(), $crossOriginOptions, ThrowSecurityError))\n"); >+ } else { >+ push(@$outputArray, " if (!BindingSecurity::shouldAllowAccessToDOMWindow(&state, thisObject.wrapped(), ThrowSecurityError))\n"); >+ } > } else { > push(@$outputArray, " if (!BindingSecurity::shouldAllowAccessToFrame(&state, thisObject.wrapped().frame(), ThrowSecurityError))\n"); > } >@@ -4814,6 +4820,15 @@ sub GenerateAttributeGetterDefinition > push(@$outputArray, "#endif\n\n") if $conditional; > } > >+sub GetCrossOriginsOptionsFromExtendedAttributeValue >+{ >+ my $extendedAttributeValue = shift; >+ >+ return "CrossOriginOptions::Allow" if $extendedAttributeValue eq "CrossOriginOptionsAllow"; >+ return "CrossOriginOptions::AllowPostMessage" if $extendedAttributeValue eq "CrossOriginOptionsAllowPostMessage"; >+ die "Unsupported CrossOriginOptions: " + $extendedAttributeValue; >+} >+ > sub GenerateAttributeSetterBodyDefinition > { > my ($outputArray, $interface, $className, $attribute, $attributeSetterBodyName, $conditional) = @_; >@@ -4836,7 +4851,13 @@ sub GenerateAttributeSetterBodyDefinition > if ($interface->extendedAttributes->{CheckSecurity} && !$attribute->extendedAttributes->{DoNotCheckSecurity} && !$attribute->extendedAttributes->{DoNotCheckSecurityOnSetter}) { > AddToImplIncludes("JSDOMBindingSecurity.h", $conditional); > if ($interface->type->name eq "DOMWindow") { >- push(@$outputArray, " if (!BindingSecurity::shouldAllowAccessToDOMWindow(&state, thisObject.wrapped(), ThrowSecurityError))\n"); >+ if ($attribute->extendedAttributes->{DoNotCheckSecurityIf}) { >+ my $crossOriginOptions = GetCrossOriginsOptionsFromExtendedAttributeValue($attribute->extendedAttributes->{DoNotCheckSecurityIf}); >+ AddToImplIncludes("HTTPParsers.h", $conditional); >+ push(@$outputArray, " if (!BindingSecurity::shouldAllowAccessToDOMWindowGivenMinimumCrossOriginOptions(&state, thisObject.wrapped(), $crossOriginOptions, ThrowSecurityError))\n"); >+ } else { >+ push(@$outputArray, " if (!BindingSecurity::shouldAllowAccessToDOMWindow(&state, thisObject.wrapped(), ThrowSecurityError))\n"); >+ } > } else { > push(@$outputArray, " if (!BindingSecurity::shouldAllowAccessToFrame(&state, thisObject.wrapped().frame(), ThrowSecurityError))\n"); > } >@@ -5057,7 +5078,13 @@ sub GenerateOperationBodyDefinition > > AddToImplIncludes("JSDOMBindingSecurity.h", $conditional); > if ($interface->type->name eq "DOMWindow") { >- push(@$outputArray, " if (!BindingSecurity::shouldAllowAccessToDOMWindow(state, castedThis->wrapped(), ThrowSecurityError))\n"); >+ if ($operation->extendedAttributes->{DoNotCheckSecurityIf}) { >+ my $crossOriginOptions = GetCrossOriginsOptionsFromExtendedAttributeValue($operation->extendedAttributes->{DoNotCheckSecurityIf}); >+ AddToImplIncludes("HTTPParsers.h", $conditional); >+ push(@$outputArray, " if (!BindingSecurity::shouldAllowAccessToDOMWindowGivenMinimumCrossOriginOptions(state, castedThis->wrapped(), $crossOriginOptions, ThrowSecurityError))\n"); >+ } else { >+ push(@$outputArray, " if (!BindingSecurity::shouldAllowAccessToDOMWindow(state, castedThis->wrapped(), ThrowSecurityError))\n"); >+ } > push(@$outputArray, " return JSValue::encode(jsUndefined());\n"); > } else { > push(@$outputArray, " if (!BindingSecurity::shouldAllowAccessToFrame(state, castedThis->wrapped().frame(), ThrowSecurityError))\n"); >diff --git a/Source/WebCore/bindings/scripts/IDLAttributes.json b/Source/WebCore/bindings/scripts/IDLAttributes.json >index c332b111efd..37d61e028ce 100644 >--- a/Source/WebCore/bindings/scripts/IDLAttributes.json >+++ b/Source/WebCore/bindings/scripts/IDLAttributes.json >@@ -161,6 +161,10 @@ > "DoNotCheckSecurity": { > "contextsAllowed": ["attribute", "operation"] > }, >+ "DoNotCheckSecurityIf": { >+ "contextsAllowed": ["attribute", "operation"], >+ "values": ["CrossOriginOptionsAllow", "CrossOriginOptionsAllowPostMessage"] >+ }, > "DoNotCheckSecurityOnGetter": { > "contextsAllowed": ["attribute"] > }, >diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp >index 9c61a95ddfa..393c5c806cf 100644 >--- a/Source/WebCore/dom/Document.cpp >+++ b/Source/WebCore/dom/Document.cpp >@@ -517,6 +517,7 @@ Document::Document(Frame* frame, const URL& url, unsigned documentClasses, unsig > , m_didAssociateFormControlsTimer(*this, &Document::didAssociateFormControlsTimerFired) > , m_cookieCacheExpiryTimer(*this, &Document::invalidateDOMCookieCache) > , m_socketProvider(page() ? &page()->socketProvider() : nullptr) >+ , m_crossOriginOptions { CrossOriginOptions::Allow } > , m_isSynthesized(constructionFlags & Synthesized) > , m_isNonRenderedPlaceholder(constructionFlags & NonRenderedPlaceholder) > , m_orientationNotifier(currentOrientation(frame)) >@@ -7795,4 +7796,11 @@ String Document::signedPublicKeyAndChallengeString(unsigned keySizeIndex, const > return page->chrome().client().signedPublicKeyAndChallengeString(keySizeIndex, challengeString, url); > } > >+void Document::setCrossOriginOptions(CrossOriginOptions value) >+{ >+ m_crossOriginOptions = value; >+ if (auto* window = domWindow()) >+ window->setCrossOriginOptions(value); >+} >+ > } // namespace WebCore >diff --git a/Source/WebCore/dom/Document.h b/Source/WebCore/dom/Document.h >index 0066219d8b0..2c097082c7f 100644 >--- a/Source/WebCore/dom/Document.h >+++ b/Source/WebCore/dom/Document.h >@@ -194,6 +194,7 @@ class XPathResult; > template<typename> class ExceptionOr; > > enum CollectionType; >+enum class CrossOriginOptions; > enum class ShouldOpenExternalURLsPolicy; > > enum class RouteSharingPolicy; >@@ -1429,6 +1430,9 @@ public: > > String signedPublicKeyAndChallengeString(unsigned keySizeIndex, const String& challengeString, const URL&); > >+ CrossOriginOptions crossOriginOptions() const { return m_crossOriginOptions; } >+ void setCrossOriginOptions(CrossOriginOptions value); >+ > protected: > enum ConstructionFlags { Synthesized = 1, NonRenderedPlaceholder = 1 << 1 }; > Document(Frame*, const URL&, unsigned = DefaultDocumentClass, unsigned constructionFlags = 0); >@@ -1814,6 +1818,8 @@ private: > > unsigned m_writeRecursionDepth { 0 }; > >+ CrossOriginOptions m_crossOriginOptions; >+ > InheritedBool m_designMode { inherit }; > MediaProducer::MediaStateFlags m_mediaState { MediaProducer::IsNotPlaying }; > bool m_userHasInteractedWithMediaElement { false }; >diff --git a/Source/WebCore/loader/FrameLoader.cpp b/Source/WebCore/loader/FrameLoader.cpp >index 8ef68f2237a..a14c26c21ab 100644 >--- a/Source/WebCore/loader/FrameLoader.cpp >+++ b/Source/WebCore/loader/FrameLoader.cpp >@@ -742,6 +742,10 @@ void FrameLoader::didBeginDocument(bool dispatch) > if (!headerContentLanguage.isEmpty()) > m_frame.document()->setContentLanguage(headerContentLanguage); > } >+ >+ String crossOriginOptionsHeader = m_documentLoader->response().httpHeaderField(HTTPHeaderName::CrossOriginOptions); >+ if (!crossOriginOptionsHeader.isEmpty()) >+ m_frame.document()->setCrossOriginOptions(parseCrossOriginOptionsHeader(crossOriginOptionsHeader)); > } > > history().restoreDocumentState(); >diff --git a/Source/WebCore/page/AbstractDOMWindow.cpp b/Source/WebCore/page/AbstractDOMWindow.cpp >index ba74519c1ee..582668f753d 100644 >--- a/Source/WebCore/page/AbstractDOMWindow.cpp >+++ b/Source/WebCore/page/AbstractDOMWindow.cpp >@@ -37,8 +37,9 @@ HashMap<GlobalWindowIdentifier, AbstractDOMWindow*>& AbstractDOMWindow::allWindo > return map; > } > >-AbstractDOMWindow::AbstractDOMWindow(GlobalWindowIdentifier&& identifier) >+AbstractDOMWindow::AbstractDOMWindow(GlobalWindowIdentifier&& identifier, CrossOriginOptions crossOriginOptions) > : m_identifier(WTFMove(identifier)) >+ , m_crossOriginOptions(crossOriginOptions) > { > ASSERT(!allWindows().contains(identifier)); > allWindows().add(identifier, this); >diff --git a/Source/WebCore/page/AbstractDOMWindow.h b/Source/WebCore/page/AbstractDOMWindow.h >index 5a5debd532a..e26c2b78ae4 100644 >--- a/Source/WebCore/page/AbstractDOMWindow.h >+++ b/Source/WebCore/page/AbstractDOMWindow.h >@@ -35,6 +35,8 @@ namespace WebCore { > > class AbstractFrame; > >+enum class CrossOriginOptions; >+ > // FIXME: Rename DOMWindow to LocalWindow and AbstractDOMWindow to DOMWindow. > class AbstractDOMWindow : public RefCounted<AbstractDOMWindow>, public EventTargetWithInlineData { > public: >@@ -52,8 +54,11 @@ public: > using RefCounted::ref; > using RefCounted::deref; > >+ CrossOriginOptions crossOriginOptions() { return m_crossOriginOptions; } >+ void setCrossOriginOptions(CrossOriginOptions value) { m_crossOriginOptions = value; } >+ > protected: >- explicit AbstractDOMWindow(GlobalWindowIdentifier&&); >+ AbstractDOMWindow(GlobalWindowIdentifier&&, CrossOriginOptions); > > EventTargetInterface eventTargetInterface() const final { return DOMWindowEventTargetInterfaceType; } > void refEventTarget() final { ref(); } >@@ -61,6 +66,7 @@ protected: > > private: > GlobalWindowIdentifier m_identifier; >+ CrossOriginOptions m_crossOriginOptions; > }; > > } // namespace WebCore >diff --git a/Source/WebCore/page/DOMWindow.cpp b/Source/WebCore/page/DOMWindow.cpp >index e402a99dd7b..dfd15d8aac9 100644 >--- a/Source/WebCore/page/DOMWindow.cpp >+++ b/Source/WebCore/page/DOMWindow.cpp >@@ -402,7 +402,7 @@ void DOMWindow::setCanShowModalDialogOverride(bool allow) > } > > DOMWindow::DOMWindow(Document& document) >- : AbstractDOMWindow(GlobalWindowIdentifier { Process::identifier(), generateObjectIdentifier<WindowIdentifierType>() }) >+ : AbstractDOMWindow(GlobalWindowIdentifier { Process::identifier(), generateObjectIdentifier<WindowIdentifierType>() }, document.crossOriginOptions()) > , ContextDestructionObserver(&document) > , FrameDestructionObserver(document.frame()) > { >diff --git a/Source/WebCore/page/DOMWindow.idl b/Source/WebCore/page/DOMWindow.idl >index e696178eac6..cd412e998c3 100644 >--- a/Source/WebCore/page/DOMWindow.idl >+++ b/Source/WebCore/page/DOMWindow.idl >@@ -49,11 +49,11 @@ typedef USVString CSSOMString; > PrimaryGlobal, > ] interface DOMWindow : EventTarget { > // The current browsing context. >- [DoNotCheckSecurity, Unforgeable, ImplementedAs=self] readonly attribute WindowProxy window; >- [Replaceable, DoNotCheckSecurityOnGetter] readonly attribute WindowProxy self; >+ [DoNotCheckSecurityIf=CrossOriginOptionsAllow, Unforgeable, ImplementedAs=self] readonly attribute WindowProxy window; >+ [Replaceable, DoNotCheckSecurityIf=CrossOriginOptionsAllow] readonly attribute WindowProxy self; > [Unforgeable] readonly attribute Document document; > attribute DOMString name; >- [DoNotCheckSecurity, PutForwards=href, Unforgeable] readonly attribute Location? location; // FIXME: Should not be nullable. >+ [DoNotCheckSecurityIf=CrossOriginOptionsAllow, PutForwards=href, Unforgeable] readonly attribute Location? location; // FIXME: Should not be nullable. > readonly attribute History history; > [EnabledAtRuntime=CustomElements, ImplementedAs=ensureCustomElementRegistry] readonly attribute CustomElementRegistry customElements; > [Replaceable] readonly attribute BarProp locationbar; >@@ -63,18 +63,18 @@ typedef USVString CSSOMString; > [Replaceable] readonly attribute BarProp statusbar; > [Replaceable] readonly attribute BarProp toolbar; > attribute DOMString status; >- [DoNotCheckSecurity, CallWith=IncumbentDocument, ForwardDeclareInHeader] void close(); >- [DoNotCheckSecurity, ForwardDeclareInHeader] readonly attribute boolean closed; >+ [DoNotCheckSecurityIf=CrossOriginOptionsAllow, CallWith=IncumbentDocument, ForwardDeclareInHeader] void close(); >+ [DoNotCheckSecurityIf=CrossOriginOptionsAllow, ForwardDeclareInHeader] readonly attribute boolean closed; > void stop(); >- [DoNotCheckSecurity, CallWith=IncumbentWindow, ForwardDeclareInHeader] void focus(); >- [DoNotCheckSecurity, ForwardDeclareInHeader] void blur(); >+ [DoNotCheckSecurityIf=CrossOriginOptionsAllow, CallWith=IncumbentWindow, ForwardDeclareInHeader] void focus(); >+ [DoNotCheckSecurityIf=CrossOriginOptionsAllow, ForwardDeclareInHeader] void blur(); > > // Other browsing contexts. >- [Replaceable, DoNotCheckSecurityOnGetter, ImplementedAs=self] readonly attribute WindowProxy frames; >- [Replaceable, DoNotCheckSecurityOnGetter] readonly attribute unsigned long length; >- [DoNotCheckSecurityOnGetter, Unforgeable] readonly attribute WindowProxy? top; >- [DoNotCheckSecurityOnGetter, CustomSetter] attribute WindowProxy? opener; >- [Replaceable, DoNotCheckSecurityOnGetter] readonly attribute WindowProxy? parent; >+ [Replaceable, DoNotCheckSecurityIf=CrossOriginOptionsAllow, ImplementedAs=self] readonly attribute WindowProxy frames; >+ [Replaceable, DoNotCheckSecurityIf=CrossOriginOptionsAllow] readonly attribute unsigned long length; >+ [DoNotCheckSecurityIf=CrossOriginOptionsAllow, Unforgeable] readonly attribute WindowProxy? top; >+ [DoNotCheckSecurityIf=CrossOriginOptionsAllow, CustomSetter] attribute WindowProxy? opener; >+ [Replaceable, DoNotCheckSecurityIf=CrossOriginOptionsAllow] readonly attribute WindowProxy? parent; > [CheckSecurityForNode] readonly attribute Element? frameElement; > [CallWith=ActiveWindow&FirstWindow] WindowProxy? open(optional USVString url = "about:blank", optional DOMString target = "_blank", optional [TreatNullAs=EmptyString] DOMString features = ""); > >@@ -92,7 +92,7 @@ typedef USVString CSSOMString; > long requestAnimationFrame(RequestAnimationFrameCallback callback); // FIXME: Should return an unsigned long. > void cancelAnimationFrame(long handle); // FIXME: handle should be an unsigned long. > >- [CallWith=ScriptState&IncumbentWindow, DoNotCheckSecurity, ForwardDeclareInHeader, MayThrowException] void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer = []); >+ [CallWith=ScriptState&IncumbentWindow, DoNotCheckSecurityIf=CrossOriginOptionsAllowPostMessage, ForwardDeclareInHeader, MayThrowException] void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer = []); > > // Obsolete members, still part of the HTML specification (https://html.spec.whatwg.org/#Window-partial). > void captureEvents(); // Not implemented. Also not in modern standards. Empty function may help compatibility with legacy content. >diff --git a/Source/WebCore/page/Frame.h b/Source/WebCore/page/Frame.h >index 156ed12333e..25a34f8a3dd 100644 >--- a/Source/WebCore/page/Frame.h >+++ b/Source/WebCore/page/Frame.h >@@ -136,7 +136,7 @@ public: > > WEBCORE_EXPORT ~Frame(); > >- DOMWindow* window() const; >+ WEBCORE_EXPORT DOMWindow* window() const; > > void addDestructionObserver(FrameDestructionObserver*); > void removeDestructionObserver(FrameDestructionObserver*); >diff --git a/Source/WebCore/page/RemoteDOMWindow.cpp b/Source/WebCore/page/RemoteDOMWindow.cpp >index 92f77936dad..03a925dec62 100644 >--- a/Source/WebCore/page/RemoteDOMWindow.cpp >+++ b/Source/WebCore/page/RemoteDOMWindow.cpp >@@ -32,8 +32,8 @@ > > namespace WebCore { > >-RemoteDOMWindow::RemoteDOMWindow(Ref<RemoteFrame>&& frame, GlobalWindowIdentifier&& identifier) >- : AbstractDOMWindow(WTFMove(identifier)) >+RemoteDOMWindow::RemoteDOMWindow(Ref<RemoteFrame>&& frame, GlobalWindowIdentifier&& identifier, CrossOriginOptions crossOriginOptions) >+ : AbstractDOMWindow(WTFMove(identifier), crossOriginOptions) > , m_frame(WTFMove(frame)) > { > m_frame->setWindow(this); >diff --git a/Source/WebCore/page/RemoteDOMWindow.h b/Source/WebCore/page/RemoteDOMWindow.h >index cb8611c5d03..ede2a75d17c 100644 >--- a/Source/WebCore/page/RemoteDOMWindow.h >+++ b/Source/WebCore/page/RemoteDOMWindow.h >@@ -44,9 +44,9 @@ class Location; > > class RemoteDOMWindow final : public AbstractDOMWindow { > public: >- static Ref<RemoteDOMWindow> create(Ref<RemoteFrame>&& frame, GlobalWindowIdentifier&& identifier) >+ static Ref<RemoteDOMWindow> create(Ref<RemoteFrame>&& frame, GlobalWindowIdentifier&& identifier, CrossOriginOptions crossOriginOptions) > { >- return adoptRef(*new RemoteDOMWindow(WTFMove(frame), WTFMove(identifier))); >+ return adoptRef(*new RemoteDOMWindow(WTFMove(frame), WTFMove(identifier), crossOriginOptions)); > } > > ~RemoteDOMWindow() final; >@@ -68,7 +68,7 @@ public: > void postMessage(JSC::ExecState&, DOMWindow& incumbentWindow, JSC::JSValue message, const String& targetOrigin, Vector<JSC::Strong<JSC::JSObject>>&&); > > private: >- WEBCORE_EXPORT RemoteDOMWindow(Ref<RemoteFrame>&&, GlobalWindowIdentifier&&); >+ WEBCORE_EXPORT RemoteDOMWindow(Ref<RemoteFrame>&&, GlobalWindowIdentifier&&, CrossOriginOptions); > > bool isRemoteDOMWindow() const final { return true; } > bool isLocalDOMWindow() const final { return false; } >diff --git a/Source/WebCore/platform/network/HTTPHeaderNames.in b/Source/WebCore/platform/network/HTTPHeaderNames.in >index 3e192856e9e..9d2b396c56e 100644 >--- a/Source/WebCore/platform/network/HTTPHeaderNames.in >+++ b/Source/WebCore/platform/network/HTTPHeaderNames.in >@@ -50,6 +50,7 @@ Content-Type > Content-Range > Cookie > Cookie2 >+Cross-Origin-Options > Date > DNT > Default-Style >diff --git a/Source/WebCore/platform/network/HTTPParsers.cpp b/Source/WebCore/platform/network/HTTPParsers.cpp >index c8f45e5aed1..cf1432204e6 100644 >--- a/Source/WebCore/platform/network/HTTPParsers.cpp >+++ b/Source/WebCore/platform/network/HTTPParsers.cpp >@@ -913,4 +913,19 @@ FromOriginDisposition parseFromOriginHeader(const String& header) > return FromOriginDisposition::Invalid; > } > >+CrossOriginOptions parseCrossOriginOptionsHeader(StringView header) >+{ >+ auto strippedHeader = stripLeadingAndTrailingHTTPSpaces(header); >+ if (strippedHeader.isEmpty()) >+ return CrossOriginOptions::Allow; >+ >+ if (equalLettersIgnoringASCIICase(strippedHeader, "deny")) >+ return CrossOriginOptions::Deny; >+ >+ if (equalLettersIgnoringASCIICase(strippedHeader, "allow-postmessage")) >+ return CrossOriginOptions::AllowPostMessage; >+ >+ return CrossOriginOptions::Allow; >+} >+ > } >diff --git a/Source/WebCore/platform/network/HTTPParsers.h b/Source/WebCore/platform/network/HTTPParsers.h >index 90bcd18a0ee..d089a346984 100644 >--- a/Source/WebCore/platform/network/HTTPParsers.h >+++ b/Source/WebCore/platform/network/HTTPParsers.h >@@ -71,6 +71,13 @@ enum class FromOriginDisposition { > Invalid > }; > >+// Should be sorted from most restrictive to most permissive. >+enum class CrossOriginOptions { >+ Deny, >+ AllowPostMessage, >+ Allow, >+}; >+ > bool isValidReasonPhrase(const String&); > bool isValidHTTPHeaderValue(const String&); > bool isValidAcceptHeaderValue(const String&); >@@ -111,6 +118,7 @@ bool isCrossOriginSafeRequestHeader(HTTPHeaderName, const String&); > String normalizeHTTPMethod(const String&); > > WEBCORE_EXPORT FromOriginDisposition parseFromOriginHeader(const String&); >+CrossOriginOptions parseCrossOriginOptionsHeader(StringView); > > inline bool isHTTPSpace(UChar character) > { >diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.cpp b/Source/WebKit/WebProcess/WebPage/WebPage.cpp >index 21e6e2fbdbd..2b5379d9bc9 100644 >--- a/Source/WebKit/WebProcess/WebPage/WebPage.cpp >+++ b/Source/WebKit/WebProcess/WebPage/WebPage.cpp >@@ -5886,8 +5886,13 @@ void WebPage::frameBecameRemote(uint64_t frameID, GlobalFrameIdentifier&& remote > if (frame->page() != this) > return; > >+ auto* coreFrame = frame->coreFrame(); >+ auto* previousWindow = coreFrame->window(); >+ if (!previousWindow) >+ return; >+ > auto remoteFrame = RemoteFrame::create(WTFMove(remoteFrameIdentifier)); >- auto remoteWindow = RemoteDOMWindow::create(remoteFrame.copyRef(), WTFMove(remoteWindowIdentifier)); >+ auto remoteWindow = RemoteDOMWindow::create(remoteFrame.copyRef(), WTFMove(remoteWindowIdentifier), previousWindow->crossOriginOptions()); > UNUSED_PARAM(remoteWindow); > > remoteFrame->setOpener(frame->coreFrame()->loader().opener()); >@@ -5896,7 +5901,6 @@ void WebPage::frameBecameRemote(uint64_t frameID, GlobalFrameIdentifier&& remote > remoteFrame->windowProxy().setJSWindowProxies(WTFMove(jsWindowProxies)); > remoteFrame->windowProxy().setDOMWindow(remoteWindow.ptr()); > >- auto* coreFrame = frame->coreFrame(); > coreFrame->setView(nullptr); > coreFrame->willDetachPage(); > coreFrame->detachFromPage();
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 184996
:
338788
|
338803
|
338811
|
338812
|
338820
|
338825
|
338840
|
339215
|
339223
|
339225
|
339226
|
339242
|
339897
|
339922
|
339923
|
339973
|
339978
|
339980
|
339995
|
339998
|
340000
|
340007
|
340010
|
340022
|
340031
|
340035
|
340039
|
340041
|
340042
|
340046
|
340047
|
340049