Api manipulation method values - #2703
Conversation
[Beta] Generated file diffTime updated: Wed, 20 May 2026 16:25:05 GMT Android
File has changed Apple
File has changed Chrome-mv3File has changed FirefoxFile has changed Integration
File has changed Windows
File has changed |
|
@claude Can you validate that if an existing value function descriptor exists, that we still can remotely override it with a new function descriptor (aka not with define: true)? |
|
This PR requires a manual review and approval from a member of one of the following teams:
|
|
@cursoragent Can you validate that if an existing value function descriptor exists, that we still can remotely override it with a new function descriptor (aka not with define: true)? |
|
Validated and yes — an existing value function descriptor can be remotely overridden via a value-descriptor change without I added two stronger unit tests in
Both new tests pass alongside the existing 7 ( |
Adds two unit tests for apiManipulation: - Confirms an existing DOM-style value function descriptor (set via Object.defineProperty with writable/configurable/enumerable) can be remotely overridden via a value descriptor change *without* define: true, and that the original descriptor attributes (writable/configurable/enumerable) are preserved through the merge in wrapProperty(). - Confirms that setting define: true on a change for a property that already exists still falls through to wrapProperty(), so the new function replaces the existing one rather than being skipped. Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
Build Branch
Static preview entry points
QR codes (mobile preview)
Integration commandsnpm (Android / Extension): Swift Package Manager (Apple): .package(url: "https://github.com/duckduckgo/content-scope-scripts.git", branch: "pr-releases/jkt/auto/api-manipulation-method-values-60ce")git submodule (Windows): git -C submodules/content-scope-scripts fetch origin pr-releases/jkt/auto/api-manipulation-method-values-60ce
git -C submodules/content-scope-scripts checkout origin/pr-releases/jkt/auto/api-manipulation-method-values-60cePin to exact commitnpm (Android / Extension): Swift Package Manager (Apple): .package(url: "https://github.com/duckduckgo/content-scope-scripts.git", revision: "b2221a8ba0fd645cd75c4cadf3bdd00261febdcd")git submodule (Windows): git -C submodules/content-scope-scripts fetch origin pr-releases/jkt/auto/api-manipulation-method-values-60ce
git -C submodules/content-scope-scripts checkout b2221a8ba0fd645cd75c4cadf3bdd00261febdcd |
When apiManipulation replaces an existing method-valued descriptor with a
configured function, mask the replacement against the original DOM method
so that toString(), toString.toString(), .name and .length still resemble
the native method. Without this, third-party scripts can trivially detect
the override via fn.toString() ('function noop() {}') or fn.name.
Implementation: in wrapApiDescriptor, when the new descriptor's value is
a function and an existing own value descriptor with a function value
exists, wrap the configured replacement with maskMethodReplacement().
That helper creates a fresh wrapper function (so we never mutate shared
singletons such as functionMap.noop), copies the original function's
name and length onto it, and applies wrapToString() against the original
to preserve toString output. The resulting value is then defined via
ContentFeature.defineProperty in the normal way, which adds its debug
flag and a second wrapToString layer; the two layers compose correctly
because the inner mask is wrapToString rather than wrapFunction (the
latter creates a non-configurable own toString that violates the Proxy
get-trap invariant once the outer wrapToString is added on top).
Adds unit tests asserting:
- toString() returns the original method's source rather than the
replacement's body
- toString.toString() still resolves to Function.prototype.toString
- .name and .length match the original method
- When the original is a real native function (Object.prototype
.hasOwnProperty), the masked toString() contains '[native code]'
- The configured replacement is still what executes at call time
Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
The masking helper added in the previous commit invoked the configured replacement via the page-visible `Reflect.apply` and set `name`/`length` on the wrapper via the page-visible `Object.defineProperty`. Both are re-readable by hostile pages after content scope initialisation: - `Reflect.apply` is reassignable on `globalThis.Reflect`, letting a page observe or alter every invocation of the protected method. - `Object.defineProperty` is reassignable on `globalThis.Object`, letting a page block or poison the name/length masking. Switch the helper to: - `ReflectApply` — new bound primitive exported from captured-globals, matching the existing `ReflectDeleteProperty` pattern. - `objectDefineProperty` — already-exported captured `Object.defineProperty`. Adds a unit test that swaps in spies for `globalThis.Reflect.apply` and `globalThis.Object.defineProperty` after feature construction and asserts: 1. `Object.defineProperty` is not called during `wrapApiDescriptor` (the helper used captured `objectDefineProperty`). 2. Masked `name`/`length`/`toString()` remain intact under tampering. 3. End-to-end invocation still routes to the configured replacement. Note: `ContentFeature.defineProperty`'s outer debug-wrapper still reads `Reflect.apply` from the page; that is pre-existing and out of scope for this helper. The captured `ReflectApply` here guarantees the helper's own inner call (helper → configured replacement) cannot be intercepted. Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
There was a problem hiding this comment.
Stale comment
Web Compatibility Assessment
injected/src/features/api-manipulation.js:59-61- warning: The runtime now accepts descriptor changes withvalue, but the bundled@duckduckgo/privacy-configurationschema still definesDescriptorAPIChangeas requiringgetterValueand disallowing additionalvalueproperties. I validated a minimalapiManipulationconfig with a methodvaluedescriptor againstcreateValidator('CurrentGenericConfig'), and it fails withadditionalProperty: valueplus missinggetterValue. That makes the new method-value path undeployable via the normal schema-validated remote-config flow, or forces any rollout outside the expected typed config contract.Security Assessment
injected/src/features/api-manipulation.js:59-61- warning: This is a configuration-trust gap rather than a page-exploitable primitive: runtime validation only checks that exactly one ofgetterValue/valueexists, while the schema that should validate the nestedConfigSettingshape has not been updated in this repo. If a config bypassed the current schema, malformedvaluesettings would be filtered only byprocessAttr()defaults instead of the privacy-config schema boundary.- No new messaging, origin-validation, network, iframe, or message-bridge security issues identified beyond the existing visible review comments.
Risk Level
Critical Risk - the PR changes
captured-globals.jsand expands config-driven replacement of DOM method-valued descriptors across injected platforms.Recommendations
- Update/bump the
@duckduckgo/privacy-configurationschema soDescriptorAPIChangepermits exactly one ofgetterValueorvalue, both typed asCSSConfigSetting.- Add a schema-validation fixture or unit test containing a method
valuedescriptor so this delivery path stays covered.- Keep the existing method identity/captured-global tests;
npm run test-unit --workspace=injected -- --filter='ApiManipulation'passes locally.Sent by Cursor Automation: Web compat and sec
laghee
left a comment
There was a problem hiding this comment.
We could add some logging in here in the future to prevent potential dev confusion (e.g., Cursor claims accidentally wrapping an instance instead of the prototype would silently fail, as do invalid API changes), but otherwise LGTM
Adds two capabilities to the descriptor-change path so configs can target
real-world media API surfaces that the existing wrapApiDescriptor cannot:
1. Shadow-define inherited methods. The previous define: true guard was
!(key in api), which is alse for any property reachable via the
prototype chain (e.g. MediaDevices.prototype.addEventListener, inherited
from EventTarget.prototype). Switch the guard to
!Object.prototype.hasOwnProperty.call(api, key) so a config can define
an own property on MediaDevices.prototype that shadows the inherited
one without modifying EventTarget.prototype (which would affect every
other EventTarget consumer - window, document, elements, etc.).
2. setterValue field on descriptor changes. Accessor properties such as
MediaDevices.prototype.ondevicechange (an EventHandler IDL attribute)
are a getter+setter pair. The existing getterValue path produces only
{ get }, so wrapProperty's {...origDescriptor, ...descriptor} merge
preserves the original setter; assigning mediaDevices.ondevicechange = fn
still registers a real listener. Adding setterValue to the descriptor
shape lets configs override the setter half too, invoking the configured
function via captured ReflectApply on each assignment. getterValue
and setterValue may be supplied independently or together.
Validation is updated to accept the accessor shape when only setterValue
is present, and to reject mixing accessor (getterValue/setterValue) with
value (�alue) shapes in a single change.
Adds unit tests covering:
- shadow-define an inherited method with define: true
- no-op behaviour when define: true is omitted and key is inherited
- setter override alongside getter
- setter-only override (original getter preserved)
- validation for setterValue-only changes
- validation rejecting setterValue + value mixing
Motivation: the original Claude camera-prompt-on-login report turned out
to be triggered by Claude subscribing to mediaDevices.devicechange during
its bootstrap (via both �ddEventListener('devicechange', ...) and
mediaDevices.ondevicechange = ...). With these two additions a single
apiManipulation config can suppress every JS-side device-enumeration trigger
without touching EventTarget.prototype or shipping a separate JS shim.
0658319 to
c7be658
Compare
Retain define:true for properties absent from the prototype chain only. When an API exists (own or inherited), apply value/getterValue/setterValue overrides—including shadow-defining inherited methods such as MediaDevices.prototype.addEventListener without requiring define.
Restore unchanged define semantics from c7be658. Value/getterValue/setterValue and method masking remain; inherited APIs still require define: true to shadow-define.
There was a problem hiding this comment.
Stale comment
Web Compatibility Assessment
injected/src/features/api-manipulation.js:182-189- error:findPropertyDescriptor()uses page-visibleObject.getPrototypeOfwhile walking inherited DOM descriptors.apiManipulation.init()runs after page script can tamper globals, so a page/polyfill can make inherited descriptor changes throw, return the wrong prototype, or loop, leaving the configured API surface unmodified on that site.injected/src/features/api-manipulation.js:207-217- error:maskMethodReplacement()still uses a fresh normal function as the replacement target. That preservestoString(),.name, and.length, but changes native DOM method constructability and exposes an ownprototype, detectable viaObject.prototype.hasOwnProperty.call(fn, 'prototype')ornew fn().injected/src/features/api-manipulation.js:110-130- warning: Runtime validation acceptsvalueandsetterValue, but the bundled@duckduckgo/privacy-configurationschema still only definesgetterValuefor descriptor changes. These new config shapes remain outside the normal schema-validated remote-config contract.Security Assessment
injected/src/features/api-manipulation.js:182-189- error: The uncapturedObject.getPrototypeOfread is a hostile-page bypass for the new inherited method/accessor shadowing path. A page that reassignsObject.getPrototypeOfbefore config init can prevent protections such asMediaDevices.prototype.addEventListenersuppression from installing, or can hang the main thread by returning the same object repeatedly. Use a captured/bound primitive fromcaptured-globals.js(for exampleReflectGetPrototypeOf = Reflect.getPrototypeOf.bind(Reflect)) for the traversal.injected/src/features/api-manipulation.js:110-130- warning: The runtime/schema mismatch is a configuration-trust gap: accepted descriptor shapes no longer match the schema boundary that should validate nestedConfigSettingvalues.- No new messaging, origin-validation, network, iframe,
postMessage, or message-bridge issues identified.Risk Level
Critical Risk - this PR changes
captured-globals.jsand expands config-driven DOM descriptor replacement across all injected platforms, including new prototype-chain traversal in a hostile page context.Recommendations
- Capture and use a bound prototype traversal primitive in
captured-globals.js, then add a unit test that tampersObject.getPrototypeOfafter feature construction and verifies inherited descriptor overrides still apply.- Preserve method constructability/prototype shape by using the original function as the proxy target with an
applytrap that invokes the configured replacement via capturedReflectApply; add tests for ownprototypeparity andnew fn()behavior.- Update/bump
@duckduckgo/privacy-configurationsoDescriptorAPIChangepermits the final mutually exclusive data (value) and accessor (getterValue/setterValue) shapes, with schema fixtures covering both.Validation:
npm run test-unit --workspace=injected -- --filter='ApiManipulation'passed (20 specs, 0 failures). A one-off hostile-global check confirmed the current inherited-descriptor path throws whenObject.getPrototypeOfis tampered.Sent by Cursor Automation: Web compat and sec
Revert expanded define documentation and hasOwnProperty shadow-define behavior. Remove inherited shadow-define tests. define:true remains for adding properties that are absent from the target object.
There was a problem hiding this comment.
Stale comment
Web Compatibility Assessment
injected/src/features/api-manipulation.js:141-142- error: Thedefine: trueinherited-shadow path creates the own descriptor from the configured replacement only, then returns before inspecting the inherited native descriptor. For inherited methods likeMediaDevices.prototype.addEventListener, the replacement is not masked againstEventTarget.prototype.addEventListenerand descriptor flags default instead of preserving the inherited method shape. For inherited accessor properties withsetterValueonly, this also shadows the inherited getter so reads becomeundefined. This violates descriptor/API surface fidelity for the inherited APIs this path is meant to support.injected/src/features/api-manipulation.js:190-195- error:maskMethodReplacement()uses a fresh normal function as the callable target. That preservestoString(),.name, and.length, but it changes native DOM method constructability and exposes an ownprototypeon methods that are usually non-constructable. Page scripts can detect the override viaObject.prototype.hasOwnProperty.call(fn, 'prototype')ornew fn().injected/src/features/api-manipulation.js:59-66- warning: Runtime validation acceptsvalueandsetterValue, but the bundled@duckduckgo/privacy-configurationschema still defines descriptor changes asgetterValue-only. The new config shapes remain outside the normal schema-validated remote-config contract.Security Assessment
injected/src/features/api-manipulation.js:169- warning: The new method/setter replacement path is installed throughthis.wrapProperty(), which routes intoContentFeature.defineProperty()'s outer debug proxy. That proxy still invokes the replacement through mutableReflect.applyon the capturedReflectobject, so a hostile page that reassignsReflect.applybefore config init or before invocation can intercept/suppress the end-to-end configured replacement despite the innerReflectApplyhelper.injected/src/features/api-manipulation.js:59-66- warning: The schema/runtime mismatch is a configuration-trust gap rather than a direct page-exploitable primitive: accepted descriptor shapes no longer match the schema boundary that should validate nestedConfigSettingvalues.- No new messaging, origin-validation, network, iframe,
postMessage, or message-bridge issues identified.Risk Level
Critical Risk - this changes
captured-globals.jsand expands config-driven DOM descriptor replacement/definition across injected platforms.Recommendations
- For inherited
define: trueshadows, derive from the inherited descriptor using captured/bound prototype traversal, then overlay configuredvalue/get/set, preserving complementary accessor halves, flags, and masking against the inherited native method/setter.- Preserve method constructability/prototype shape by proxying the original function target with an
applytrap that calls the configured replacement via capturedReflectApply, instead of wrapping a fresh normal function.- Use a bound
ReflectApplyin the outerContentFeature.defineProperty()debug proxy, or otherwise ensure api-manipulation method/setter replacements do not re-enter page-mutableReflect.applyat call time.- Update/bump
@duckduckgo/privacy-configurationsoDescriptorAPIChangepermits the final mutually exclusive data (value) and accessor (getterValue/setterValue) shapes, with schema fixtures for both.Validation:
npm run test-unit --workspace=injected -- --filter='ApiManipulation'passed (19 specs, 0 failures).Sent by Cursor Automation: Web compat and sec
…nfig Bump @duckduckgo/privacy-configuration to 1779290401870 (merged PR #5215 schema: setterValue/value descriptors with original define comment). Restore inherited-property shadow-defining without define: true so MediaDevices.prototype.addEventListener/removeEventListener overrides match the deployed remote-config shape. Add unit coverage for the #5215 apiChanges payload and schema validation.
There was a problem hiding this comment.
Stale comment
Web Compatibility Assessment
injected/src/features/api-manipulation.js:182-190- error:findPropertyDescriptor()walks inherited DOM descriptors with page-visibleObject.getPrototypeOf. BecauseapiManipulation.init()runs after page script can mutate globals, a site/polyfill can make inherited descriptor overrides throw, return the wrong prototype, or loop. This can leave the intendedMediaDevices.prototype.addEventListener-style API surface unmodified on affected pages.injected/src/features/api-manipulation.js:207-217- error:maskMethodReplacement()still uses a fresh normal function as the callable target. That preservestoString(),.name, and.length, but changes native DOM method constructability and exposes an ownprototypeon methods that are usually non-constructable. A probe withObject.prototype.hasOwnPropertyshowed the replacement has an ownprototypeand is constructable.injected/src/features/api-manipulation.js:50-66- warning: Descriptor validation reads optional fields through the prototype chain ('enumerable' in change,change.value,change.setterValue, etc.). If a page or legacy library pollutesObject.prototype, valid config descriptors can be misclassified before installation. For example,Object.prototype.value = ...makes a validgetterValuedescriptor fail validation.Security Assessment
injected/src/features/api-manipulation.js:182-190- error: The uncapturedObject.getPrototypeOfread is a hostile-page bypass for the new inherited descriptor path. A page that reassigns it before config init can prevent protections from installing or hang traversal. Use a captured/bound primitive fromcaptured-globals.js.injected/src/features/api-manipulation.js:50-66- warning: Prototype-polluted inherited config fields let page code influence the runtime validation boundary for remote-config changes. Use capturedhasOwnProperty.call(change, key)before reading optional descriptor fields.injected/src/features/api-manipulation.js:167-172- warning: The final installed value/setter still routes throughContentFeature.defineProperty()'s debug proxy, which calls mutableReflect.applyat invocation time. That leaves the end-to-end method/setter replacement path interceptable if the page tampersReflect.apply, despite the inner helper using capturedReflectApply.- No new messaging, origin-validation, network, iframe,
postMessage, or message-bridge issues identified. The schema deployability issue from earlier reviews appears addressed by the privacy-configuration bump and fixture.Risk Level
Critical Risk - the PR changes
captured-globals.jsand expands config-driven DOM descriptor replacement/definition across injected platforms, including inherited prototype traversal in hostile page contexts.Recommendations
- Add a captured/bound prototype traversal primitive, e.g.
ReflectGetPrototypeOf = Reflect.getPrototypeOf.bind(Reflect), use it infindPropertyDescriptor(), and add a tampered-Object.getPrototypeOftest.- Base descriptor validation on own properties only via captured
hasOwnProperty.call(...); add anObject.prototype.valuepollution regression test.- Preserve method constructability/prototype shape by proxying the original function target with an
applytrap that invokes the configured replacement via capturedReflectApply.- Use captured
ReflectApplyin the outerContentFeature.defineProperty()debug proxy, or avoid re-wrapping already-masked api-manipulation method/setter replacements through page-mutableReflect.apply.Validation:
npm run test-unit --workspace=injected -- --filter='ApiManipulation'passes (21 specs, 0 failures).Sent by Cursor Automation: Web compat and sec
…apes Extract mergePropertyDescriptors from wrapProperty so shadow-defining inherited APIs uses the same value/accessor compatibility rules. Skip the override (instead of throwing) when remote config shape disagrees with the live prototype descriptor kind. Clarify define vs inherited shadow-define JSDoc. Add unit coverage for descriptor-kind mismatches on inherited properties.
There was a problem hiding this comment.
Stale comment
Web Compatibility Assessment
injected/src/features/api-manipulation.js:187-194- error:findPropertyDescriptor()walks inherited DOM descriptors via page-visibleObject.getPrototypeOf.apiManipulation.init()runs after page script can mutate globals, so a site/polyfill can make inherited descriptor overrides throw, return the wrong prototype, or loop. That can leaveMediaDevices.prototype.addEventListener-style API patches uninstalled on affected pages.injected/src/features/api-manipulation.js:212-222- error:maskMethodReplacement()uses a fresh normal function as the callable target. It maskstoString(),.name, and.length, but changes native method constructability and adds an ownprototypefor DOM methods that are normally non-constructable. This remains detectable viaObject.prototype.hasOwnProperty.call(fn, 'prototype')ornew fn().injected/src/features/api-manipulation.js:49-66- warning: Descriptor validation reads optional config fields through the prototype chain ('enumerable' in change,change.value,change.setterValue, etc.). If page code or a legacy library pollutesObject.prototype, valid descriptors can be misclassified before installation.Security Assessment
injected/src/features/api-manipulation.js:187-194- error: The uncapturedObject.getPrototypeOfread is a hostile-page bypass for the new inherited descriptor path. A page can prevent protections from installing or hang traversal. Use a captured/bound primitive fromcaptured-globals.js, e.g.ReflectGetPrototypeOf = Reflect.getPrototypeOf.bind(Reflect).injected/src/features/api-manipulation.js:49-66- warning: Prototype-polluted inherited config fields let page code influence the runtime validation boundary for remote-config changes. Optional descriptor fields should be checked with capturedhasOwnProperty.call(change, key)before reading them.injected/src/content-feature.js:469-476- warning: The installed value/setter still routes throughContentFeature.defineProperty()'s debug proxy, whoseapplytrap calls mutableReflect.applyat invocation time. That leaves the end-to-end replacement path interceptable if the page tampersReflect.apply, despite the inner api-manipulation helper using capturedReflectApply.- No new messaging, origin-validation, network, iframe,
postMessage, or message-bridge issues identified. The latest commit appears to have fixed the inherited descriptor cross-shape merge issue noted earlier.Risk Level
Critical Risk - the PR changes
captured-globals.jsand expands config-driven DOM descriptor replacement across injected platforms, including prototype-chain traversal in a hostile page context.Recommendations
- Capture and use a bound prototype traversal primitive, then add a tampered-
Object.getPrototypeOfregression test for inherited overrides.- Base descriptor validation on own properties only via captured
hasOwnProperty.call(...); add anObject.prototype.valuepollution test.- Preserve method constructability/prototype shape by proxying the original function target with an
applytrap that invokes the configured replacement via capturedReflectApply.- Use captured
ReflectApplyin the outerContentFeature.defineProperty()debug proxy, or avoid re-wrapping already-masked api-manipulation replacements through page-mutableReflect.apply.Sent by Cursor Automation: Web compat and sec
Remove the implicit else branch that shadow-defined prototype-chain properties without define. Inherited overrides now use the same define:true && !hasOwnProperty path as intended, with descriptor-kind guards via mergePropertyDescriptors. Update unit tests; privacy-configuration #5215 entries will need define: true added in remote config.
Normalize merged descriptors so tsc accepts them at defineProperty and wrapProperty call sites. No remote config change required.
privacy-configuration #1779290401870 expects inputFieldFocusDetection
settings as { state } object, not a bare string.
Re-add the else path for prototype-chain properties so remote config (e.g. MediaDevices addEventListener) works without define: true. Properties absent from the entire chain still no-op unless define: true. Descriptor-kind mismatches still skip via mergePropertyDescriptors.
There was a problem hiding this comment.
Web Compatibility Assessment
injected/src/features/api-manipulation.js:185-193 - error:findPropertyDescriptor()walks inherited DOM descriptors with page-visibleObject.getPrototypeOf.apiManipulation.init()runs after page scripts can mutate globals, so a site/polyfill can make inherited descriptor overrides throw, return the wrong prototype, or loop. A one-off check with tamperedObject.getPrototypeOfcurrently throws before the inherited override is installed.injected/src/features/api-manipulation.js:210-220 - error:maskMethodReplacement()uses a fresh normal function as the callable target. It maskstoString(),.name, and.length, but changes native DOM method constructability and exposes an ownprototype; the current wrapper is constructable andObject.prototype.hasOwnProperty.call(fn, 'prototype')is true. That violates API surface fidelity for method-value overrides.injected/src/features/api-manipulation.js:50-66 - warning: descriptor validation checks optional config fields through the prototype chain ('enumerable' in change, directchange.value/change.setterValuereads). Page-sideObject.prototypepollution can misclassify otherwise valid descriptor configs before installation.injected/src/wrapper-utils.js:123-148 - error:mergePropertyDescriptors()also uses inherited descriptor keys and returns normal object-literal descriptors. WithObject.prototype.valuepolluted, an accessor/accessor merge currently returns an own data descriptor using the polluted value, changing the descriptor shape that gets installed.
Security Assessment
injected/src/features/api-manipulation.js:185-193 - error: the uncapturedObject.getPrototypeOfread is a hostile-page bypass for the new inherited descriptor path; page code can prevent protections from installing or hang traversal.injected/src/features/api-manipulation.js:50-66 - warning: inherited config fields let page-side prototype pollution influence the runtime validation boundary for remote-config changes. Optional fields should be tested with capturedhasOwnProperty.call(...)before reading.injected/src/wrapper-utils.js:123-148 - error: page-side prototype pollution can influence the descriptor C-S-S installs. Descriptor-kind checks and the final descriptor objects need to avoid inheritedvalue/get/setparticipation inObject.defineProperty's descriptor conversion.injected/src/content-feature.js:469-476 - warning: installed value/setter replacements still route through the debug proxy, whoseapplytrap calls mutableReflect.applyat invocation time. This is reached from the new method/setter replacement path despite the inner helper using capturedReflectApply.- No new messaging, origin-validation, network, iframe,
postMessage, or message-bridge issues identified in the current head.
Risk Level
Critical Risk - this PR changes captured-globals.js and expands config-driven DOM descriptor replacement across injected platforms, including inherited prototype traversal in a hostile page context.
Recommendations
- Capture and use a bound prototype traversal primitive, e.g.
ReflectGetPrototypeOf = Reflect.getPrototypeOf.bind(Reflect), and add a tampered-Object.getPrototypeOfregression test. - Preserve method constructability/prototype shape by proxying the original function target with an
applytrap that invokes the configured replacement via capturedReflectApply; add tests for ownprototypeparity andnew fn()behavior. - Make descriptor validation and descriptor merging own-property-only via captured
hasOwnProperty.call(...). - Return null-prototype descriptor objects, or otherwise guarantee inherited
value/get/setcannot participate in descriptor conversion; add tests forObject.prototype.valueon accessor merges andObject.prototype.get/seton data merges. - Use captured
ReflectApplyin the outer debug proxy, or avoid re-wrapping already-masked api-manipulation replacements through page-mutableReflect.apply.
Validation: npm run test-unit --workspace=injected -- --filter='ApiManipulation' passes (24 specs, 0 failures); targeted one-off probes reproduced the three hostile-page/API-surface gaps above.
Sent by Cursor Automation: Web compat and sec
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d2209bb. Configure here.
GuiltyDolphin
left a comment
There was a problem hiding this comment.
Discussed in call:
- both suspicious of the shadow define, but willing to proceed
- if the YT/linkedin/claude changes cause any problems we can roll back via config
- if existing API overrides are affected then this is harder to roll back via config
Config change is merged so this should apply once Windows adopts new CSS version.
Once you've finished running the final validations re existing APIs and have no concerns, feel free to merge.




Asana Task/Github Issue: https://app.asana.com/1/137249556945/task/1214898456412755?focus=true
Description
This should allow remote configuration of value descriptors that are functions.
Testing Steps
Set the following config:
{ "condition": [ { "domain": "claude.ai" } ], "patchSettings": [ { "op": "add", "path": "/apiChanges/MediaDevices.prototype.getUserMedia", "value": { "type": "descriptor", "getterValue": { "type": "function", "functionName": "debug" } } } ] },Checklist
Please tick all that apply:
Note
Medium Risk
Expands DOM API override capabilities (including methods and setters) and changes descriptor wrapping/definition behavior, which could affect site compatibility or detection resistance if edge cases are missed. Risk is mitigated by extensive new unit tests and schema validation coverage.
Overview
Enables remote
apiManipulationconfig to override value descriptors (including methods) and setter-only/accessor overrides via newvalueandsetterValuefields, with stricter validation to prevent mixing accessor/value descriptor shapes.When overriding functions, replacements are now masked to preserve observable identity (
toString(),name,length, including native-code appearance) and use captured globals (Reflect.apply,Object.defineProperty) to reduce page tampering. Inherited/prototype-chain properties can be overridden by shadow-defining an own property (without requiringdefine: true), using a newmergePropertyDescriptorshelper shared bywrapProperty.Updates the privacy-config dependency and adjusts an integration test config JSON shape; adds extensive unit tests for method replacement, inherited overrides, setter interception, and schema validation against updated privacy-configuration.
Reviewed by Cursor Bugbot for commit d2209bb. Bugbot is set up for automated code reviews on this repo. Configure here.