You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Because near-membrane proxies project blue-realm (host) objects into the red realm (sandbox) via distortionCallback, the integrity of distortions depends on the assumption that the sandbox cannot remove or replace the distorted property on the original target. Prior to this fix, untrusted code could exploit the delete operator to breach distortion boundaries:
Distortion bypass via property deletion — Sandbox code could delete ShadowRoot.prototype.host (or any property whose getter/setter/value is distorted). Because callableDeleteProperty on the blue side unconditionally called ReflectDeleteProperty(target, key), the deletion succeeded on the real blue-realm object. Subsequent property accesses would either resolve to undefined or fall through to a prototype-chain definition that was not distorted, leaking the original blue-realm behavior.
Technical Implementation
The fix adds a guard in callableDeleteProperty inside createHooksCallback (in membrane.ts). When the protectDistortions option is enabled, the callable retrieves the target property's descriptor via ReflectGetOwnPropertyDescriptor, extracts its get, set, and value components, and checks each against distortionCallback. If any component returns a value different from the original (indicating it has been distorted), the callable throws via pushErrorAcrossBoundary(new TypeErrorCtor('Cannot delete property with a distortion.')) before ReflectDeleteProperty is ever reached.
Option threading: A new protectDistortions?: boolean option was added to HooksOptions, VirtualEnvironmentOptions, BrowserEnvironmentOptions, and NodeEnvironmentOptions. It flows from createVirtualEnvironment → VirtualEnvironment constructor → blue connector hooks options → createHooksCallback closure.
Error propagation: The error is wrapped with pushErrorAcrossBoundary so it crosses the membrane correctly and surfaces as a TypeError inside the sandbox.
Feature gating: The guard is gated behind protectDistortions (defaults to false), so existing consumers are unaffected.
Scope: The guard runs in callableDeleteProperty on the blue side, which is called from the red side's passthruDeletePropertyTrap for live proxy targets. Static proxies do not cross the boundary for delete operations and are not covered by this guard.
Test Coverage
Four tests in test/distortions/protect-distortions.spec.js:
Test
What it validates
Throw on distorted getter delete
delete ShadowRoot.prototype.host throws TypeError when the getter is distorted and protectDistortions: true
Allow non-distorted delete
delete obj.x on a plain object succeeds normally
Preserve distortion after failed delete
After the throw, elm.shadowRoot.host still returns the distorted value (null)
No throw when disabled
Same delete succeeds silently when protectDistortions is not set
Tests use afterEach to restore ShadowRoot.prototype.host via Object.defineProperty to prevent cross-test interference from the "when disabled" case which actually deletes the property.
Security Implications
Before: Sandbox code could delete any property on a blue-realm object, including those with distorted getters/setters/values, bypassing the distortion and leaking the original blue-realm definition.
After: With protectDistortions: true, deletion of distorted properties throws a TypeError, preserving distortion integrity.
The option defaults to false for backward compatibility and must be explicitly enabled by the consumer alongside liveTargetCallback (which ensures delete operations cross the boundary).
The reason will be displayed to describe this comment to others. Learn more.
What about Object.defineProperty? The same bypass works by replacing the distorted descriptor instead of deleting it, right?
I'm confused as to what damage deleting a distorted property can do? Like it's their sandbox, they should be allowed to delete and add and stuff right?
What about Object.defineProperty? The same bypass works by replacing the distorted descriptor instead of deleting it, right?
I explained this in the parking lot discussion, it's not a bypass because the attacking code wouldn't have any access to the original window.top API reference to begin with.
I'm confused as to what damage deleting a distorted property can do? Like it's their sandbox, they should be allowed to delete and add and stuff right?
Deleting the distorted property tricks near-membrane into doing the thing it does for undistorted things: return the undistorted value from the blue realm.
This file contains hidden or 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
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Security Vulnerability Addressed
Because near-membrane proxies project blue-realm (host) objects into the red realm (sandbox) via
distortionCallback, the integrity of distortions depends on the assumption that the sandbox cannot remove or replace the distorted property on the original target. Prior to this fix, untrusted code could exploit thedeleteoperator to breach distortion boundaries:delete ShadowRoot.prototype.host(or any property whose getter/setter/value is distorted). BecausecallableDeletePropertyon the blue side unconditionally calledReflectDeleteProperty(target, key), the deletion succeeded on the real blue-realm object. Subsequent property accesses would either resolve toundefinedor fall through to a prototype-chain definition that was not distorted, leaking the original blue-realm behavior.Technical Implementation
The fix adds a guard in
callableDeletePropertyinsidecreateHooksCallback(inmembrane.ts). When theprotectDistortionsoption is enabled, the callable retrieves the target property's descriptor viaReflectGetOwnPropertyDescriptor, extracts itsget,set, andvaluecomponents, and checks each againstdistortionCallback. If any component returns a value different from the original (indicating it has been distorted), the callable throws viapushErrorAcrossBoundary(new TypeErrorCtor('Cannot delete property with a distortion.'))beforeReflectDeletePropertyis ever reached.protectDistortions?: booleanoption was added toHooksOptions,VirtualEnvironmentOptions,BrowserEnvironmentOptions, andNodeEnvironmentOptions. It flows fromcreateVirtualEnvironment→VirtualEnvironmentconstructor → blue connector hooks options →createHooksCallbackclosure.pushErrorAcrossBoundaryso it crosses the membrane correctly and surfaces as aTypeErrorinside the sandbox.protectDistortions(defaults tofalse), so existing consumers are unaffected.callableDeletePropertyon the blue side, which is called from the red side'spassthruDeletePropertyTrapfor live proxy targets. Static proxies do not cross the boundary for delete operations and are not covered by this guard.Test Coverage
Four tests in
test/distortions/protect-distortions.spec.js:delete ShadowRoot.prototype.hostthrowsTypeErrorwhen the getter is distorted andprotectDistortions: truedelete obj.xon a plain object succeeds normallyelm.shadowRoot.hoststill returns the distorted value (null)protectDistortionsis not setTests use
afterEachto restoreShadowRoot.prototype.hostviaObject.definePropertyto prevent cross-test interference from the "when disabled" case which actually deletes the property.Security Implications
protectDistortions: true, deletion of distorted properties throws aTypeError, preserving distortion integrity.falsefor backward compatibility and must be explicitly enabled by the consumer alongsideliveTargetCallback(which ensures delete operations cross the boundary).