Commit 5edd419b authored by Pâris MEULEMAN's avatar Pâris MEULEMAN Committed by Commit Bot

[Security] Inherit COEP for newly created window

The spec states about COEP
"If creator is not null, set document’s embedder policy to creator
embedder policy.". This is done in this patch.

Note: This is required for COOP, because it cares about COEP in the
case of "same-origin" and needs it to be available on the initial empty
document of a new popup.

Also adds a WPT about a potential COEP exploit using WindowProxy to
circumvent COEP restrictions on fetching.

Bug: 922191
Change-Id: Ie3097618be825c5031449d997970e33fad3d4f8e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2007747
Commit-Queue: Pâris Meuleman <pmeuleman@chromium.org>
Reviewed-by: default avatarArthur Sonzogni <arthursonzogni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#734058}
parent 25d32f13
......@@ -4445,13 +4445,15 @@ void RenderFrameHostImpl::CreateNewWindow(
dom_storage_context, params->session_storage_namespace_id);
}
// On popup creation, if the opener and the openers's top-level document
// are same origin, then the popup's initial empty document inherits its COOP
// policy from the opener's top-level document.
// See https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e#model
network::mojom::CrossOriginOpenerPolicy popup_coop =
network::mojom::CrossOriginOpenerPolicy::kUnsafeNone;
network::mojom::CrossOriginEmbedderPolicy popup_coep =
network::mojom::CrossOriginEmbedderPolicy::kNone;
if (base::FeatureList::IsEnabled(network::features::kCrossOriginIsolation)) {
// On popup creation, if the opener and the openers's top-level document
// are same origin, then the popup's initial empty document inherits its
// COOP policy from the opener's top-level document. See
// https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e#model
RenderFrameHostImpl* top_level_opener = this;
while (top_level_opener->GetParent()) {
top_level_opener = top_level_opener->GetParent();
......@@ -4471,6 +4473,11 @@ void RenderFrameHostImpl::CreateNewWindow(
}
}
// The initial empty document in the popup inherits the COEP of its opener (if
// any).
if (!params->opener_suppressed)
popup_coep = cross_origin_embedder_policy();
// If the opener is suppressed or script access is disallowed, we should
// open the window in a new BrowsingInstance, and thus a new process. That
// means the current renderer process will not be able to route messages to
......@@ -4509,6 +4516,7 @@ void RenderFrameHostImpl::CreateNewWindow(
main_frame->SetOriginAndNetworkIsolationKeyOfNewFrame(
GetLastCommittedOrigin());
main_frame->cross_origin_opener_policy_ = popup_coop;
main_frame->cross_origin_embedder_policy_ = popup_coep;
if (main_frame->waiting_for_init_) {
// Need to check |waiting_for_init_| as some paths inside CreateNewWindow
......
......@@ -131,4 +131,26 @@ promise_test(async t => {
promise_test(t => {
return promise_rejects(t, new TypeError(), fetch(get_host_info().HTTPS_REMOTE_ORIGIN+"/common/blank.html", {mode: "no-cors"}));
}, `"require-corp" top-level: fetch() to response without CORP should fail`);
promise_test(t => {
const w = window.open();
return promise_rejects(t, new TypeError(), w.fetch(get_host_info().HTTPS_REMOTE_ORIGIN+"/common/blank.html", {mode: "no-cors"}));
}, `"require-corp" top-level: fetch() to response without CORP through a WindowProxy should fail`);
async_test(t => {
let w = window.open();
const frame = w.document.createElement("iframe");
t.add_cleanup(() => w.close());
t.step_timeout(() => {
// Make sure the iframe didn't load. See
// https://github.com/whatwg/html/issues/125 for why a timeout is
// used here. Long term all network error handling should be similar
// and have a reliable event.
assert_equals(frame.contentDocument, null);
t.done();
}, 500);
frame.src = "/common/blank.html";
document.body.append(frame);
assert_equals(frame.contentDocument.body.localName, "body");
}, `"require-corp" top-level: navigating an iframe to a page without CORP, through a WindowProxy, should fail`);
</script>
......@@ -8,5 +8,7 @@ PASS "require-corp" top-level (as noopener popup): navigating to "none" should s
PASS "require-corp" top-level (as popup with opener set to null): navigating to "none" should succeed
PASS "require-corp" top-level: fetch() to CORP: cross-origin response should succeed
FAIL "require-corp" top-level: fetch() to response without CORP should fail assert_unreached: Should have rejected: undefined Reached unreachable code
FAIL "require-corp" top-level: fetch() to response without CORP through a WindowProxy should fail assert_unreached: Should have rejected: undefined Reached unreachable code
FAIL "require-corp" top-level: navigating an iframe to a page without CORP, through a WindowProxy, should fail assert_equals: expected null but got Document node with 1 child
Harness: the test ran to completion.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment