From 3a73b22199a61a711bdeed49f58720529851a467 Mon Sep 17 00:00:00 2001 From: Ari Chivukula Date: Wed, 11 Sep 2024 17:26:49 -0700 Subject: [PATCH] [Partitioned Popins] Limit cross-origin popin opener access This CL mirrors the COOP restrict-properties work to prevent the use of the opener proxy for/by a popin for any actions other than postMessage() or closed. The difference between https://chromium-review.googlesource.com/c/chromium/src/+/5800429 and this CL is that enforcement is limited here to cross-origin cases. There will be enforcement of same-origin cases in a future CL, but we will not enforce it as a security boundary (independent process) for now. All of this work is behind an experimental flag "PartitionedPopins" so will not be enabled by default. Explainer: https://explainers-by-googlers.github.io/partitioned-popins/ I2P: https://groups.google.com/a/chromium.org/g/blink-dev/c/ApU_zUmpQ2g/ Bug: 340606651 Change-Id: I5a852fc2f598e311142a25a434656592fe9185a3 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5854082 Auto-Submit: Ari Chivukula Reviewed-by: Daniel Cheng Reviewed-by: Rakina Zata Amni Commit-Queue: Rakina Zata Amni Cr-Commit-Position: refs/heads/main@{#1354277} --- ....proxy-cross.tentative.sub.https.window.js | 37 ++++++++++++ ...opins.proxy-same.tentative.https.window.js | 39 +++++++++++++ .../partitioned-popins.proxy-popin.html | 24 ++++++++ partitioned-popins/resources/proxy-helpers.js | 58 +++++++++++++++++++ 4 files changed, 158 insertions(+) create mode 100644 partitioned-popins/partitioned-popins.proxy-cross.tentative.sub.https.window.js create mode 100644 partitioned-popins/partitioned-popins.proxy-same.tentative.https.window.js create mode 100644 partitioned-popins/resources/partitioned-popins.proxy-popin.html create mode 100644 partitioned-popins/resources/proxy-helpers.js diff --git a/partitioned-popins/partitioned-popins.proxy-cross.tentative.sub.https.window.js b/partitioned-popins/partitioned-popins.proxy-cross.tentative.sub.https.window.js new file mode 100644 index 00000000000000..dd369736419b61 --- /dev/null +++ b/partitioned-popins/partitioned-popins.proxy-cross.tentative.sub.https.window.js @@ -0,0 +1,37 @@ +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/partitioned-popins/resources/proxy-helpers.js + +'use strict'; + +// Spec: https://explainers-by-googlers.github.io/partitioned-popins/ +// Step 1 (window) Set up listener to resolve messages as they come in. +// Step 2 (window) Open cross-site popin. +// Step 3 (popin) Set up listener to resolve messages as they come in. +// Step 4 (popin) Test and report usable methods against window. +// Step 5 (window) Test and compare usable methods against popin. +// Step 6 (popin) Cleanup. +// Step 7 (window) Cleanup. + +async_test(t => { + let popin_proxy; + + // Step 1 + window.addEventListener("message", t.step_func(e => { + switch (e.data.type) { + case 'ready': + // Step 5 + assert_equals(e.data.message, "Closed,Then,"); + assert_equals(getUsableMethods(popin_proxy), "Closed,Then,"); + popin_proxy.postMessage({type: "cleanup"}, "*"); + break; + case 'cleanup': + // Step 7 + t.done(); + break; + } + })); + + // Step 2 + popin_proxy = window.open("https://{{hosts[alt][]}}:{{ports[https][0]}}/partitioned-popins/resources/partitioned-popins.proxy-popin.html", '_blank', 'popin'); +}, "Verify cross-site Partitioned Popins proxies only have access to postMessage and closed methods."); diff --git a/partitioned-popins/partitioned-popins.proxy-same.tentative.https.window.js b/partitioned-popins/partitioned-popins.proxy-same.tentative.https.window.js new file mode 100644 index 00000000000000..1bda23eb7b6c6a --- /dev/null +++ b/partitioned-popins/partitioned-popins.proxy-same.tentative.https.window.js @@ -0,0 +1,39 @@ +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/partitioned-popins/resources/proxy-helpers.js + +'use strict'; + +// Spec: https://explainers-by-googlers.github.io/partitioned-popins/ +// Step 1 (window) Set up listener to resolve messages as they come in. +// Step 2 (window) Open same-origin popin. +// Step 3 (popin) Set up listener to resolve messages as they come in. +// Step 4 (popin) Test and report usable methods against window. +// Step 5 (window) Test and compare usable methods against popin. +// Step 6 (popin) Cleanup. +// Step 7 (window) Cleanup. + +// TODO(crbug.com/340606651): Remove expectations file and secure same-origin popins. + +async_test(t => { + let popin_proxy; + + // Step 1 + window.addEventListener("message", t.step_func(e => { + switch (e.data.type) { + case 'ready': + // Step 5 + assert_equals(e.data.message, "Closed,Then,"); + assert_equals(getUsableMethods(popin_proxy), "Closed,Then,"); + popin_proxy.postMessage({type: "cleanup"}, "*"); + break; + case 'cleanup': + // Step 7 + t.done(); + break; + } + })); + + // Step 2 + popin_proxy = window.open("/partitioned-popins/resources/partitioned-popins.proxy-popin.html", '_blank', 'popin'); +}, "Verify same-origin Partitioned Popins proxies only have access to postMessage and closed methods."); diff --git a/partitioned-popins/resources/partitioned-popins.proxy-popin.html b/partitioned-popins/resources/partitioned-popins.proxy-popin.html new file mode 100644 index 00000000000000..d1b7b869338af2 --- /dev/null +++ b/partitioned-popins/resources/partitioned-popins.proxy-popin.html @@ -0,0 +1,24 @@ + + + + + + diff --git a/partitioned-popins/resources/proxy-helpers.js b/partitioned-popins/resources/proxy-helpers.js new file mode 100644 index 00000000000000..46359df0dc7601 --- /dev/null +++ b/partitioned-popins/resources/proxy-helpers.js @@ -0,0 +1,58 @@ +'use strict'; + +function customMethod() { +} + +let customAttribute = ""; + +function getUsableMethods(proxy) { + let message = ""; + try { + proxy.closed; + message += "Closed," + } catch (_) {} + try { + proxy.blur(); + message += "Blur," + } catch (_) {} + try { + proxy.onblur; + message += "OnBlur," + } catch (_) {} + try { + proxy.opener; + message += "Opener," + } catch (_) {} + try { + proxy.length; + message += "Length," + } catch (_) {} + try { + proxy.name = "foo"; + message += "Name," + } catch (_) {} + try { + proxy[0]; + message += "AnonymousIndex," + } catch (_) {} + try { + proxy['test']; + message += "AnonymousName," + } catch (_) {} + try { + proxy.customMethod(); + message += "CustomMethod," + } catch (_) {} + try { + proxy.customAttribute; + message += "CustomAttributeGet," + } catch (_) {} + try { + proxy.customAttribute = ""; + message += "CustomAttributeSet," + } catch (_) {} + if (proxy.then == undefined) { + message += "Then," + } + return message; +}