forked from WebKit/WebKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
https://bugs.webkit.org/show_bug.cgi?id=275147 Reviewed by Alex Christensen. The `noopener-allow-popups` COOP value would enable a document to ensure it can't be scripted by other same-origin documents that have opened it. Some origins can contain different applications with different levels of security requirements. In those cases, it can be beneficial to prevent scripts running in one application from being able to open and script pages of another same-origin application. The noopener-allow-popups Cross-Origin-Opener-Policy value severs the opener relationship between the document loaded with this policy and its opener. At the same time, this document can open further documents (as the "allow-popups" in the name suggests) and maintain its opener relationship with them, assuming that their COOP policy allows it. This implements whatwg/html#10394 * LayoutTests/imported/w3c/web-platform-tests/html/cross-origin-opener-policy/reporting/resources/reporting-common.js: (const.coopHeaders): A helper to create headers in a more succinct way. * LayoutTests/imported/w3c/web-platform-tests/html/cross-origin-opener-policy/reporting/tentative/access-to-noopener-page-from-no-coop-ro.https-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/html/cross-origin-opener-policy/reporting/tentative/access-to-noopener-page-from-no-coop-ro.https.html: Added. * LayoutTests/imported/w3c/web-platform-tests/html/cross-origin-opener-policy/resources/noopener-helper.js: Added. (getExecutorPath): (const.test_noopener_opening_popup): The logic for the noopener tests. (async const): * LayoutTests/imported/w3c/web-platform-tests/html/cross-origin-opener-policy/tentative/noopener/coop-noopener-allow-popups.https-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/html/cross-origin-opener-policy/tentative/noopener/coop-noopener-allow-popups.https.html: Added. * Source/WebCore/loader/CrossOriginOpenerPolicy.cpp: (WebCore::crossOriginOpenerPolicyToString): Add the "noopener-allow-popups" string. (WebCore::crossOriginOpenerPolicyValueToEffectivePolicyString): Add the "noopener-allow-popups" string. (WebCore::matchingCOOP): Implement the related HTML algorithm. (WebCore::coopValuesRequireBrowsingContextGroupSwitch): Implement the switching logic related to noopener-allow-popups. (WebCore::obtainCrossOriginOpenerPolicy): Parse the "noopener-allow-popups" value. * Source/WebCore/loader/CrossOriginOpenerPolicy.h: Add the noopener-allow-popups enum value. * Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in: Add the noopener-allow-popups enum value. Canonical link: https://commits.webkit.org/284866@main
- Loading branch information
1 parent
8b10763
commit 7688a5f
Showing
9 changed files
with
373 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 4 additions & 0 deletions
4
...ner-policy/reporting/tentative/access-to-noopener-page-from-no-coop-ro.https-expected.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
|
||
FAIL access-to-coop-page-from-opener, noopener-allow-popups assert_equals: expected (string) "coop" but got (undefined) undefined | ||
FAIL access-to-coop-page-from-opener, noopener-allow-popups + redirect assert_equals: expected (string) "coop" but got (undefined) undefined | ||
|
69 changes: 69 additions & 0 deletions
69
...igin-opener-policy/reporting/tentative/access-to-noopener-page-from-no-coop-ro.https.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
<!DOCTYPE html> | ||
<title> | ||
COOP reports are sent when the openee used COOP-RO+COEP and then its | ||
same-origin opener tries to access it. | ||
</title> | ||
<meta name=timeout content=long> | ||
<script src="/resources/testharness.js"></script> | ||
<script src="/resources/testharnessreport.js"></script> | ||
<script src=/common/get-host-info.sub.js></script> | ||
<script src="/common/utils.js"></script> | ||
<script src="/common/dispatcher/dispatcher.js"></script> | ||
<script src="/html/cross-origin-opener-policy/reporting/resources/reporting-common.js"></script> | ||
<script src="/html/cross-origin-opener-policy/reporting/resources/try-access.js"></script> | ||
<script> | ||
|
||
const directory = "/html/cross-origin-opener-policy"; | ||
const redirect_path = directory + "/resources/redirect.py?"; | ||
const same_origin = get_host_info().HTTPS_ORIGIN; | ||
|
||
let runTest = (openee_redirect, name) => promise_test(async t => { | ||
const report_token = token(); | ||
const openee_token = token(); | ||
const opener_token = token(); // The current test window. | ||
|
||
const opener_url = location.href; | ||
|
||
const reportTo = reportToHeaders(report_token); | ||
const openee_url = same_origin + executor_path + reportTo.header + | ||
reportTo.coopReportOnlyNoopenerAllowPopupsHeader + coep_header + | ||
`&uuid=${openee_token}`; | ||
const openee_redirect_url = same_origin + redirect_path + openee_url | ||
const openee_requested_url = openee_redirect ? openee_redirect_url | ||
: openee_url; | ||
|
||
|
||
const openee = window.open(openee_requested_url); | ||
t.add_cleanup(() => send(openee_token, "window.close()")) | ||
|
||
// 1. Make sure the new document to be loaded. | ||
send(openee_token, ` | ||
send("${opener_token}", "Ready"); | ||
`); | ||
let reply = await receive(opener_token); | ||
assert_equals(reply, "Ready"); | ||
|
||
// 2. Try to access the openee. A report is sent, because of COOP-RO+COEP. | ||
tryAccess(openee); | ||
|
||
// 3. Check a report is sent to the openee. | ||
let report = | ||
await receiveReport(report_token, "access-to-coop-page-from-opener"); | ||
assert_equals(report.type, "coop"); | ||
assert_equals(report.url, openee_url.replace(/"/g, '%22')); | ||
assert_equals(report.body.disposition, "reporting"); | ||
assert_equals(report.body.effectivePolicy, "noopener-allow-popups"); | ||
assert_equals(report.body.property, "blur"); | ||
assert_source_location_missing(report); | ||
assert_equals(report.body.openerURL, opener_url); | ||
assert_equals(report.body.openeeURL, undefined); | ||
assert_equals(report.body.otherDocumentURL, undefined); | ||
assert_equals(report.body.referrer, opener_url); | ||
assert_equals(report.body.initialPopupURL, undefined); | ||
}, name); | ||
|
||
runTest(false, "access-to-coop-page-from-opener, noopener-allow-popups"); | ||
runTest(true , "access-to-coop-page-from-opener, noopener-allow-popups + redirect"); | ||
|
||
</script> | ||
|
159 changes: 159 additions & 0 deletions
159
...orted/w3c/web-platform-tests/html/cross-origin-opener-policy/resources/noopener-helper.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
const executor_path = '/common/dispatcher/executor.html?pipe='; | ||
const coop_header = policy => { | ||
return `|header(Cross-Origin-Opener-Policy,${policy})`; | ||
}; | ||
|
||
function getExecutorPath(uuid, origin, coop_header) { | ||
return origin.origin + executor_path + coop_header + `&uuid=${uuid}`; | ||
} | ||
|
||
const test_noopener_opening_popup = | ||
(opener_coop, openee_coop, origin, opener_expectation) => { | ||
promise_test(async t => { | ||
// Set up dispatcher communications. | ||
const popup_token = token(); | ||
const reply_token = token(); | ||
const popup_reply_token = token(); | ||
const popup_openee_token = token(); | ||
|
||
const popup_url = getExecutorPath( | ||
popup_token, SAME_ORIGIN, coop_header(opener_coop)); | ||
|
||
// We open a popup and then ping it, it will respond after loading. | ||
const popup = window.open(popup_url); | ||
t.add_cleanup(() => send(popup_token, 'window.close()')); | ||
send(popup_token, `send('${reply_token}', 'Popup loaded');`); | ||
assert_equals(await receive(reply_token), 'Popup loaded'); | ||
|
||
if (opener_coop == 'noopener-allow-popups') { | ||
// Assert that we can't script the popup. | ||
assert_true(popup.closed, 'Opener popup.closed'); | ||
} | ||
|
||
// Ensure that the popup has no access to its opener. | ||
send(popup_token, ` | ||
let openerDOMAccessAllowed = false; | ||
try { | ||
openerDOMAccessAllowed = !!self.opener.document.URL; | ||
} catch(ex) { | ||
} | ||
const payload = { | ||
opener: !!self.opener, | ||
openerDOMAccess: openerDOMAccessAllowed | ||
}; | ||
send('${reply_token}', JSON.stringify(payload)); | ||
`); | ||
let payload = JSON.parse(await receive(reply_token)); | ||
if (opener_coop == 'noopener-allow-popups') { | ||
assert_false(payload.opener, 'popup opener'); | ||
assert_false(payload.openerDOMAccess, 'popup DOM access'); | ||
} | ||
|
||
// Open another popup from inside the popup, and wait for it to | ||
// load. | ||
const popup_openee_url = getExecutorPath( | ||
popup_openee_token, origin, coop_header(openee_coop)); | ||
send(popup_token, ` | ||
window.openee = open("${popup_openee_url}"); | ||
await receive('${popup_reply_token}'); | ||
const payload = { | ||
openee: !!window.openee, | ||
closed: window.openee.closed | ||
}; | ||
send('${reply_token}', JSON.stringify(payload)); | ||
`); | ||
t.add_cleanup(() => send(popup_token, 'window.openee && window.openee.close()')); | ||
// Notify the popup that its openee was loaded. | ||
send(popup_openee_token, `send('${popup_reply_token}', 'popup openee opened');`); | ||
|
||
// Assert that the popup has access to its openee. | ||
payload = JSON.parse(await receive(reply_token)); | ||
assert_true(payload.openee, 'popup openee'); | ||
|
||
// Assert that the openee has access to its popup opener. | ||
send(popup_openee_token, ` | ||
let openerDOMAccessAllowed = false; | ||
try { | ||
openerDOMAccessAllowed = !!self.opener.document.URL; | ||
} catch(ex) { | ||
} | ||
const payload = { | ||
opener: !!self.opener, | ||
openerDOMAccess: openerDOMAccessAllowed | ||
}; | ||
send('${reply_token}', JSON.stringify(payload)); | ||
`); | ||
payload = JSON.parse(await receive(reply_token)); | ||
if (opener_expectation) { | ||
assert_true(payload.opener, 'Opener is not null'); | ||
assert_true(payload.openerDOMAccess, 'No DOM access'); | ||
} else { | ||
assert_false(payload.opener, 'Opener is null'); | ||
assert_false(payload.openerDOMAccess, 'No DOM access'); | ||
} | ||
}, | ||
'noopener-allow-popups ensures that the opener cannot script the openee,' + | ||
' but further popups with no COOP can access their opener: ' + | ||
opener_coop + '/' + openee_coop + ':' + origin == SAME_ORIGIN); | ||
}; | ||
|
||
const test_noopener_navigating_away = (popup_coop) => { | ||
promise_test( | ||
async t => { | ||
// Set up dispatcher communications. | ||
const popup_token = token(); | ||
const reply_token = token(); | ||
const popup_reply_token = token(); | ||
const popup_second_token = token(); | ||
|
||
const popup_url = | ||
getExecutorPath(popup_token, SAME_ORIGIN, coop_header(popup_coop)); | ||
|
||
// We open a popup and then ping it, it will respond after loading. | ||
const popup = window.open(popup_url); | ||
send(popup_token, `send('${reply_token}', 'Popup loaded');`); | ||
assert_equals(await receive(reply_token), 'Popup loaded'); | ||
t.add_cleanup(() => send(popup_token, 'window.close()')); | ||
|
||
// Assert that we can script the popup. | ||
assert_not_equals(popup.window, null); | ||
assert_false(popup.closed, 'popup closed'); | ||
|
||
// Ensure that the popup has no access to its opener. | ||
send(popup_token, ` | ||
let openerDOMAccessAllowed = false; | ||
try { | ||
openerDOMAccessAllowed = !!self.opener.document.URL; | ||
} catch(ex) { | ||
} | ||
const payload = { | ||
opener: !!self.opener, | ||
openerDOMAccess: openerDOMAccessAllowed | ||
}; | ||
send('${reply_token}', JSON.stringify(payload)); | ||
`); | ||
let payload = JSON.parse(await receive(reply_token)); | ||
assert_true(payload.opener, 'popup opener'); | ||
assert_true(payload.openerDOMAccess, 'popup DOM access'); | ||
|
||
// Navigate the popup | ||
const popup_second_url = getExecutorPath( | ||
popup_second_token, SAME_ORIGIN, | ||
coop_header('noopener-allow-popups')); | ||
|
||
send(popup_token, ` | ||
window.location = '${popup_second_url}'; | ||
`); | ||
|
||
// Notify the popup that its openee was loaded. | ||
send( | ||
popup_second_token, | ||
`send('${reply_token}', 'popup navigated away');`); | ||
assert_equals(await receive(reply_token), 'popup navigated away'); | ||
|
||
//assert_equals(popup.window, null); | ||
assert_true(popup.closed, 'popup.closed'); | ||
}, | ||
'noopener-allow-popups ensures that the opener cannot script the openee,' + | ||
' even after a navigation: ' + popup_coop); | ||
}; |
31 changes: 31 additions & 0 deletions
31
...oss-origin-opener-policy/tentative/noopener/coop-noopener-allow-popups.https-expected.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
|
||
PASS | ||
Cross-Origin-Opener-Policy: noopener-allow-popups means that the opener | ||
has no access to the openee. | ||
|
||
PASS | ||
Cross-Origin-Opener-Policy: noopener-allow-popups means that the opener | ||
has no access to the openee. | ||
1 | ||
PASS | ||
Cross-Origin-Opener-Policy: noopener-allow-popups means that the opener | ||
has no access to the openee. | ||
2 | ||
PASS | ||
Cross-Origin-Opener-Policy: noopener-allow-popups means that the opener | ||
has no access to the openee. | ||
3 | ||
PASS | ||
Cross-Origin-Opener-Policy: noopener-allow-popups means that the opener | ||
has no access to the openee. | ||
4 | ||
PASS | ||
Cross-Origin-Opener-Policy: noopener-allow-popups means that the opener | ||
has no access to the openee. | ||
5 | ||
PASS | ||
Cross-Origin-Opener-Policy: noopener-allow-popups means that the opener | ||
has no access to the openee. | ||
6 | ||
PASS noopener-allow-popups ensures that the opener cannot script the openee, even after a navigation: unsafe-none | ||
|
45 changes: 45 additions & 0 deletions
45
.../html/cross-origin-opener-policy/tentative/noopener/coop-noopener-allow-popups.https.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<!doctype html> | ||
<title> | ||
Cross-Origin-Opener-Policy: noopener-allow-popups means that the opener | ||
has no access to the openee. | ||
</title> | ||
<script src=/resources/testharness.js></script> | ||
<script src=/resources/testharnessreport.js></script> | ||
<script src="/common/dispatcher/dispatcher.js"></script> | ||
<script src="/common/get-host-info.sub.js"></script> | ||
<script src="/common/utils.js"></script> | ||
<script src="../../resources/common.js"></script> | ||
<script src="../../resources/noopener-helper.js"></script> | ||
<script> | ||
|
||
|
||
test_noopener_opening_popup("noopener-allow-popups", | ||
"unsafe-none", | ||
SAME_ORIGIN, | ||
/*opener_expectation=*/true); | ||
test_noopener_opening_popup("noopener-allow-popups", | ||
"noopener-allow-popups", | ||
SAME_ORIGIN, | ||
/*opener_expectation=*/false); | ||
test_noopener_opening_popup("noopener-allow-popups", | ||
"same-origin", | ||
SAME_ORIGIN, | ||
/*opener_expectation=*/false); | ||
test_noopener_opening_popup("noopener-allow-popups", | ||
"same-origin-allow-popups", | ||
SAME_ORIGIN, | ||
/*opener_expectation=*/false); | ||
test_noopener_opening_popup("noopener-allow-popups", | ||
"same-origin-allow-popups", | ||
CROSS_ORIGIN, | ||
/*opener_expectation=*/false); | ||
test_noopener_opening_popup("same-origin-allow-popups", | ||
"noopener-allow-popups", | ||
SAME_ORIGIN, | ||
/*opener_expectation=*/false); | ||
test_noopener_opening_popup("same-origin", | ||
"noopener-allow-popups", | ||
SAME_ORIGIN, | ||
/*opener_expectation=*/false); | ||
test_noopener_navigating_away("unsafe-none"); | ||
</script> |
Oops, something went wrong.