fix(window): reject indexed writes and definitions

This commit is contained in:
ldm0
2026-09-09 06:52:23 +08:00
parent f78ea0b74e
commit f66a4e7c54
4 changed files with 94 additions and 3 deletions
@@ -24,9 +24,10 @@ pub(super) use helpers::{
window_child_context_handle, window_has_discarded_child_browsing_context,
};
pub(super) use interceptors::{
window_indexed_property_deleter, window_indexed_property_descriptor,
window_indexed_property_enumerator, window_indexed_property_getter,
window_indexed_property_query, window_named_properties_indexed_property_getter,
window_indexed_property_definer, window_indexed_property_deleter,
window_indexed_property_descriptor, window_indexed_property_enumerator,
window_indexed_property_getter, window_indexed_property_query, window_indexed_property_setter,
window_named_properties_indexed_property_getter,
window_named_properties_indexed_property_query, window_named_property_getter,
window_named_property_query,
};
@@ -64,6 +64,17 @@ pub(in crate::context_bootstrap) fn window_indexed_property_getter<'s>(
v8::Intercepted::kYes
}
pub(in crate::context_bootstrap) fn window_indexed_property_setter(
_scope: &mut v8::PinScope<'_, '_>,
_index: u32,
_value: v8::Local<'_, v8::Value>,
_args: v8::PropertyCallbackArguments<'_>,
mut rv: v8::ReturnValue<'_, v8::Boolean>,
) -> v8::Intercepted {
rv.set_bool(false);
v8::Intercepted::kYes
}
pub(in crate::context_bootstrap) fn window_indexed_property_query<'s>(
scope: &mut v8::PinScope<'s, '_>,
index: u32,
@@ -77,6 +88,17 @@ pub(in crate::context_bootstrap) fn window_indexed_property_query<'s>(
v8::Intercepted::kYes
}
pub(in crate::context_bootstrap) fn window_indexed_property_definer(
_scope: &mut v8::PinScope<'_, '_>,
_index: u32,
_descriptor: &v8::PropertyDescriptor,
_args: v8::PropertyCallbackArguments<'_>,
mut rv: v8::ReturnValue<'_, v8::Boolean>,
) -> v8::Intercepted {
rv.set_bool(false);
v8::Intercepted::kYes
}
pub(in crate::context_bootstrap) fn window_indexed_property_deleter<'s>(
scope: &mut v8::PinScope<'s, '_>,
index: u32,
@@ -372,9 +372,11 @@ pub(crate) fn install_window_own_template_bindings<'s>(
window_template.set_indexed_property_handler(
v8::IndexedPropertyHandlerConfiguration::new()
.getter(window_indexed_property_getter)
.setter(window_indexed_property_setter)
.query(window_indexed_property_query)
.deleter(window_indexed_property_deleter)
.enumerator(window_indexed_property_enumerator)
.definer(window_indexed_property_definer)
.descriptor(window_indexed_property_descriptor),
);
// Window is a [Global] WebIDL interface. Blink installs its members on the
@@ -15241,6 +15241,72 @@ fn main_window_indexed_child_deletion_is_live_and_not_cached() {
);
}
#[test]
fn main_window_indexed_set_and_define_reject_every_array_index() {
let mut vm = new_storage_test_vm("https://window-indexed-write.test/");
let result = vm
.eval(
r#"
(() => {
const frame = document.createElement('iframe');
(document.body || document.documentElement || document).appendChild(frame);
const child = frame.contentWindow;
const strictSetThrows = index => {
'use strict';
try {
window[index] = 'strict';
return false;
} catch (error) {
return error instanceof TypeError;
}
};
const defineThrows = (index, descriptor) => {
try {
Object.defineProperty(window, index, descriptor);
return false;
} catch (error) {
return error instanceof TypeError;
}
};
window[0] = 'sloppy';
window[1] = 'sloppy';
window[4294967294] = 'sloppy';
const result = {
existingChildPreserved: window[0] === child,
existingStrictSetThrows: strictSetThrows(0),
existingReflectSet: Reflect.set(window, 0, 'reflect'),
existingReflectDefine: Reflect.defineProperty(window, 0, { value: 'reflect' }),
existingDefineThrows: defineThrows(0, { get: () => 'getter' }),
missingRemainsAbsent: window[1] === undefined,
missingStrictSetThrows: strictSetThrows(1),
missingReflectSet: Reflect.set(window, 1, 'reflect'),
missingReflectDefine: Reflect.defineProperty(window, 1, { value: 'reflect' }),
missingDefineThrows: defineThrows(1, { value: 'defined' }),
maxIndexRemainsAbsent: window[4294967294] === undefined,
maxIndexStrictSetThrows: strictSetThrows(4294967294),
maxIndexReflectSet: Reflect.set(window, 4294967294, 'reflect'),
maxIndexReflectDefine: Reflect.defineProperty(window, 4294967294, { value: 'reflect' })
};
window[4294967295] = 1;
result.nonIndexSet = window[4294967295];
result.nonIndexReflectSet = Reflect.set(window, 4294967295, 2);
result.nonIndexAfterReflectSet = window[4294967295];
result.nonIndexReflectDefine = Reflect.defineProperty(window, 4294967295, { value: 3 });
result.nonIndexAfterDefine = window[4294967295];
return JSON.stringify(result);
})()
"#,
)
.expect("Window indexed write and define probe should evaluate");
assert_eq!(
result,
r#"{"existingChildPreserved":true,"existingStrictSetThrows":true,"existingReflectSet":false,"existingReflectDefine":false,"existingDefineThrows":true,"missingRemainsAbsent":true,"missingStrictSetThrows":true,"missingReflectSet":false,"missingReflectDefine":false,"missingDefineThrows":true,"maxIndexRemainsAbsent":true,"maxIndexStrictSetThrows":true,"maxIndexReflectSet":false,"maxIndexReflectDefine":false,"nonIndexSet":1,"nonIndexReflectSet":true,"nonIndexAfterReflectSet":2,"nonIndexReflectDefine":true,"nonIndexAfterDefine":3}"#
);
}
#[tokio::test]
async fn main_window_indexed_child_descriptor_matches_window_semantics() {
let mut vm = new_storage_test_vm("https://window-indexed-descriptor.test/");