fix(window): make WindowProxy prototypes immutable

This commit is contained in:
ldm0
2026-09-08 14:43:00 +08:00
committed by Donough Liu
parent 3330e01843
commit ebc4c0ea44
4 changed files with 75 additions and 1 deletions
@@ -2924,7 +2924,6 @@ html/browsers/the-window-object/window-indexed-properties-strict.html
html/browsers/the-window-object/window-indexed-properties.html
html/browsers/the-window-object/window-open-noopener.html?indexed
html/browsers/the-window-object/window-open-noreferrer.html
html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-same-origin.html
html/browsers/windows/browsing-context-names/choose-default-001.html
html/browsers/windows/browsing-context.html
html/browsers/windows/document-domain-removed-iframe.html
@@ -5248,6 +5248,7 @@ html/browsers/the-window-object/window-open-noopener.html?_top
html/browsers/the-window-object/window-prototype-chain.html
html/browsers/the-windowproxy-exotic-object/windowproxy-define-own-property-unforgeable-same-origin.html
html/browsers/the-windowproxy-exotic-object/windowproxy-prevent-extensions.html
html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-same-origin.html
html/browsers/windows/browsing-context-names/choose-_blank-001.html
html/browsers/windows/browsing-context-names/choose-default-002.html
html/browsers/windows/browsing-context-window.html
@@ -42,6 +42,14 @@ impl ContextBootstrapAssets {
.id_by_name("Window")
.ok_or_else(|| anyhow!("missing constructor template metadata `Window`"))?;
let window_template = registry.get_or_build_template(scope, window_id)?;
// WindowProxy's [[SetPrototypeOf]] only succeeds when the requested
// prototype is already its current prototype. Set the invariant on
// Window's instance template before deriving the same- and
// cross-origin global templates so it remains attached to V8's global
// proxy rather than only to the inner global object.
window_template
.instance_template(scope)
.set_immutable_proto();
// A cross-origin WindowProxy shell must retain Window's V8 wrapper
// identity so that detaching and reusing it for the committed child
// realm discards the facade's temporary own properties. It must not,
@@ -8827,6 +8827,72 @@ fn window_named_properties_respect_later_prototype_properties_and_descriptor_fla
);
}
#[test]
fn main_and_child_window_proxies_have_immutable_prototypes() {
let mut vm = new_storage_test_vm("https://window-proxy-prototype.test/");
let result = vm
.eval(
r#"
(() => {
"use strict";
const frame = document.createElement("iframe");
(document.body || document.documentElement || document).appendChild(frame);
const probe = target => {
const original = Object.getPrototypeOf(target);
const replacement = {};
const outcome = callback => {
try {
callback();
return "returned";
} catch (error) {
return error && error.name;
}
};
const objectDifferent = outcome(() => {
Object.setPrototypeOf(target, replacement);
});
const dunderDifferent = outcome(() => {
target.__proto__ = replacement;
});
const reflectDifferent = Reflect.setPrototypeOf(target, replacement);
const unchanged = Object.getPrototypeOf(target) === original;
const objectSame = Object.setPrototypeOf(target, original) === target;
const dunderSame = outcome(() => {
target.__proto__ = original;
});
const reflectSame = Reflect.setPrototypeOf(target, original);
return {
objectDifferent,
dunderDifferent,
reflectDifferent,
unchanged,
objectSame,
dunderSame,
reflectSame,
};
};
const observations = {
main: probe(window),
child: probe(frame.contentWindow),
};
frame.remove();
return JSON.stringify(observations);
})()
"#,
)
.expect("WindowProxy immutable prototype probe should evaluate");
assert_eq!(
result,
r#"{"main":{"objectDifferent":"TypeError","dunderDifferent":"TypeError","reflectDifferent":false,"unchanged":true,"objectSame":true,"dunderSame":"returned","reflectSame":true},"child":{"objectDifferent":"TypeError","dunderDifferent":"TypeError","reflectDifferent":false,"unchanged":true,"objectSame":true,"dunderSame":"returned","reflectSame":true}}"#
);
}
#[test]
fn window_internal_child_context_identity_is_not_read_from_web_properties() {
let mut vm = new_storage_test_vm("https://window-private-identity.test/");