Skip to content

Device enumeration getCapabilities fix - #2688

Merged
jonathanKingston merged 14 commits into
mainfrom
jkt/auto/device-enumeration-getcapabilities-fix-5de6
May 15, 2026
Merged

Device enumeration getCapabilities fix#2688
jonathanKingston merged 14 commits into
mainfrom
jkt/auto/device-enumeration-getcapabilities-fix-5de6

Conversation

@jonathanKingston

@jonathanKingston jonathanKingston commented May 11, 2026

Copy link
Copy Markdown
Contributor

Asana Task/Github Issue: https://app.asana.com/1/137249556945/task/1214684048521837?focus=true

Description

Adds getCapabilities to fix issues with sites on Windows. See: duckduckgo/privacy-configuration#5136

Testing Steps

Checklist

Please tick all that apply:

  • I have tested this change locally
  • I have tested this change locally in all supported browsers
  • This change will be visible to users
  • I have added automated tests that cover this change
  • I have ensured the change is gated by config
  • This change was covered by a ship review
  • This change was covered by a tech design
  • Any dependent config has been merged

Note

Medium Risk
Changes the behavior of navigator.mediaDevices.enumerateDevices() results by overriding InputDeviceInfo.getCapabilities() for synthetic devices, which could impact site compatibility if the shim diverges from native behavior. Risk is mitigated by being scoped to the webCompat enumerateDevices setting and covered by new integration tests.

Overview
Ensures synthetic InputDeviceInfo objects returned by the webCompat enumerateDevices shim won’t throw when sites call getCapabilities() by defining a safe getCapabilities() implementation that returns {} for input devices.

Adds Playwright coverage that demonstrates native getCapabilities() throws on legacy synthetic InputDeviceInfo objects, validates the shimmed behavior for enumerated input devices, and includes device-enumeration.spec.js in the chrome-mv3 project test set.

Reviewed by Cursor Bugbot for commit fea09d0. Bugbot is set up for automated code reviews on this repo. Configure here.


@github-actions github-actions Bot added the semver-patch Bug fix / internal — no release needed label May 11, 2026
@github-actions

github-actions Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

[Beta] Generated file diff

Time updated: Tue, 12 May 2026 14:01:59 GMT

Android
    - android/adsjsContentScope.js
  • android/contentScope.js

File has changed

Apple
    - apple/contentScope.js

File has changed

Integration
    - integration/contentScope.js

File has changed

Windows
    - windows/contentScope.js

File has changed

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Cursor assessed this PR as Medium Risk (only Low Risk is auto-approved).

This PR requires a manual review and approval from a member of one of the following teams:

  • @duckduckgo/content-scope-scripts-owners
  • @duckduckgo/apple-devs
  • @duckduckgo/android-devs
  • @duckduckgo/team-windows-development
  • @duckduckgo/extension-owners
  • @duckduckgo/config-aor
  • @duckduckgo/breakage-aor
  • @duckduckgo/breakage

@github-actions

github-actions Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

Build Branch

Branch pr-releases/jkt/auto/device-enumeration-getcapabilities-fix-5de6
Commit 1315a26e98
Updated May 12, 2026 at 12:08:05 PM UTC

Static preview entry points

QR codes (mobile preview)
Entry point QR code
Docs QR for docs preview
Static pages QR for static pages preview
Integration pages QR for integration pages preview

Integration commands

npm (Android / Extension):

npm i github:duckduckgo/content-scope-scripts#pr-releases/jkt/auto/device-enumeration-getcapabilities-fix-5de6

Swift Package Manager (Apple):

.package(url: "https://github.com/duckduckgo/content-scope-scripts.git", branch: "pr-releases/jkt/auto/device-enumeration-getcapabilities-fix-5de6")

git submodule (Windows):

git -C submodules/content-scope-scripts fetch origin pr-releases/jkt/auto/device-enumeration-getcapabilities-fix-5de6
git -C submodules/content-scope-scripts checkout origin/pr-releases/jkt/auto/device-enumeration-getcapabilities-fix-5de6
Pin to exact commit

npm (Android / Extension):

npm i github:duckduckgo/content-scope-scripts#1315a26e9811b8b4b08de4068713d08cdb0770f8

Swift Package Manager (Apple):

.package(url: "https://github.com/duckduckgo/content-scope-scripts.git", revision: "1315a26e9811b8b4b08de4068713d08cdb0770f8")

git submodule (Windows):

git -C submodules/content-scope-scripts fetch origin pr-releases/jkt/auto/device-enumeration-getcapabilities-fix-5de6
git -C submodules/content-scope-scripts checkout 1315a26e9811b8b4b08de4068713d08cdb0770f8

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Web Compatibility Assessment

  • injected/src/features/web-compat.js:1223-1236 - warning: The new synthetic getCapabilities fixes the illegal-invocation path, but it is detectably different from native InputDeviceInfo.prototype.getCapabilities. In Chromium the native method is inherited from the prototype, has .name === "getCapabilities", .length === 0, descriptor flags { writable: true, configurable: true, enumerable: true }, and native toString() output. The PR installs an own, unmasked JavaScript function with different descriptor flags (writable: false, enumerable: false), so scripts checking hasOwnProperty, descriptors, for...in, or function source can detect the override and branch differently.
  • injected/playwright.config.js:331-336 - info: The regression test is added only to chrome-mv3. The implementation is shared with Windows and the PR description calls out Windows breakage, so this leaves a platform-specific coverage gap, but not a runtime compatibility issue by itself.

Security Assessment

  • injected/src/features/web-compat.js:1223-1236 - warning: This path now calls page-reachable Object.defineProperty at enumerateDevices() call time and reads deviceInfo.getCapabilities from the live prototype chain. In hostile page contexts, a page can monkey-patch these globals/prototypes before invoking enumerateDevices(), causing the shim to throw, alter the returned method, or observe the protection path. Prefer captured objectDefineProperty/wrapper utilities and a captured native InputDeviceInfo.prototype.getCapabilities reference where possible.
  • No messaging, nativeData, origin-validation, postMessage, iframe, or new network/CSS-injection issues found in this diff.

Risk Level

High Risk - The change adds a new method shim/defineProperty path to an injected browser API object returned to page scripts; it is config-gated, but still changes observable API surface across platforms where webCompat.enumerateDevices is enabled.

Recommendations

  1. Preserve native method fidelity: use a named function getCapabilities() { return {}; }, mask it with wrapToString() against the native method, and align descriptor flags with the native method. If feasible, use a shadow prototype so device.hasOwnProperty('getCapabilities') remains false while device instanceof InputDeviceInfo remains true.
  2. Replace the raw Object.defineProperty with a captured define-property helper, and avoid looking up getCapabilities from page-mutable prototypes at call time.
  3. Extend the regression test to assert .name, .length, toString(), descriptor/enumerability behavior, and add Windows-project coverage or document why the extension harness is the intended proxy.

Validation run: npm --workspace injected run test-int -- --project=chrome-mv3 integration-test/device-enumeration.spec.js --reporter=list passed (5 tests).

Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

Comment thread injected/src/features/web-compat.js Outdated
Use the existing wrapToString helper so the synthetic InputDeviceInfo.getCapabilities shim returns a native-looking toString() (and toString.toString()), and align descriptor flags (writable/configurable/enumerable: true) with native prototype methods. Extends the device-enumeration integration test to assert the masked toString output.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Web Compatibility Assessment

  • injected/src/features/web-compat.js:1223-1235 - info: The updated synthetic InputDeviceInfo#getCapabilities() shim preserves the expected return contract ({}), uses a named function, masks toString()/toString.toString() with wrapToString(), and aligns writable/configurable/enumerable flags with the native prototype method. The method is still an own property rather than inherited, but these synthetic devices already expose own data properties, so I do not see this as a blocking compatibility regression.
  • injected/playwright.config.js:79-90 - info: The regression suite is now exercised in the chrome-mv3 project. This covers the extension harness, but the runtime code is shared with native bundles, so Windows/Android/Apple coverage remains a test-depth gap rather than a runtime issue.

Security Assessment

  • injected/src/features/web-compat.js:1223 - warning: typeof deviceInfo.getCapabilities reads through the live InputDeviceInfo.prototype chain at enumerateDevices() call time. A hostile page can replace that prototype slot with a getter or non-function before calling enumerateDevices(), causing the compat path to skip/throw and fall back to native enumeration. Prefer checking a captured/native descriptor or method reference instead of touching the synthetic object's page-mutable prototype chain.
  • No nativeData, message bridge, origin-validation, postMessage, iframe, network, or CSS-injection issues found in this diff.

Risk Level

High Risk - The PR changes the observable surface of an injected browser API object returned to page scripts, albeit behind the existing webCompat.enumerateDevices config gate.

Recommendations

  1. Replace the deviceInfo.getCapabilities capability check with a captured native method/descriptor check, e.g. a module-load reference or captured getOwnPropertyDescriptor(InputDeviceInfo.prototype, 'getCapabilities'), so page prototype tampering cannot alter the shim decision.
  2. If this fix is intended for native app breakage, add at least one native-bundle integration run or document why chrome-mv3 is the intended proxy.

Validation: npm run test-int -- integration-test/device-enumeration.spec.js --project=chrome-mv3 --reporter=list passed (5 tests); git diff --check passed.

Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

@jonathanKingston jonathanKingston changed the title Jkt/auto/device enumeration getcapabilities fix 5de6 Device enumeration getCapabilities fix May 11, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Web Compatibility Assessment

  • injected/playwright.config.js, line 347 - error: Adding integration-test/device-enumeration.spec.js to the chrome-mv3 project currently makes the targeted suite fail. npm run test-int -- integration-test/device-enumeration.spec.js --project=chrome-mv3 --reporter=list failed 1/5 because the disabled-feature test read window.results as undefined. The page sets results asynchronously from renderResults(), so the spec needs to wait for results-ready/window.results before asserting.
  • injected/src/features/web-compat.js, lines 1175-1176 - warning: The intermediate prototype fixes the own-method detectability, but it changes prototype identity for synthetic input devices (Object.getPrototypeOf(device) !== InputDeviceInfo.prototype). That is an observable API-surface difference for scripts that inspect __proto__ chains. If this tradeoff is intentional, add a regression assertion documenting it.

Security Assessment

  • injected/src/features/web-compat.js, line 1176 - warning: typeof syntheticInputDeviceInfoPrototype.getCapabilities still reads through the live page-mutable InputDeviceInfo.prototype chain at enumerateDevices() call time. A hostile page can replace that slot with a getter that throws or returns a non-function, forcing the catch path to fall back to native enumeration. Prefer checking a captured/native descriptor or method reference instead of touching the synthetic object's prototype chain.
  • No nativeData, message bridge, origin-validation, postMessage, iframe, network, or CSS-injection issues found in this diff.

Risk Level

High Risk - The PR changes the observable surface of an injected browser API object returned to page scripts and enables a new integration suite for that path.

Recommendations

  1. Fix the newly enabled integration test by waiting for the test page to publish results before reading window.results; rerun the chrome-mv3 device-enumeration target.
  2. Avoid the live prototype-chain read for getCapabilities; use a captured descriptor/method check.
  3. Add explicit tests for the accepted prototype-chain shape if the shadow prototype is the chosen compatibility tradeoff.
Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

Comment thread injected/playwright.config.js
Comment thread injected/src/features/web-compat.js Outdated
Adds a per-site config switch under webCompat.enumerateDevices so we can react to breakage in the wild without redeploying code:

- 'syntheticPrototype' (default, unchanged): intermediate prototype with own getCapabilities. Hides hasOwnProperty('getCapabilities'); one-level prototype-chain depth difference.
- 'instanceOwn': preserve InputDeviceInfo.prototype as the direct prototype; place an own masked getCapabilities on the instance. Restores Object.getPrototypeOf(d) === InputDeviceInfo.prototype.
- 'disabled': no shim; native brand check throws TypeError 'Illegal invocation'.

Also fixes the previously flaky disabled/enabled tests by waiting for the renderResults 'results-ready' event before reading window.results, and adds coverage for the two new modes.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Web Compatibility Assessment

  • injected/src/features/web-compat.js:1173-1322 - info: The latest revision adds explicit getCapabilitiesShim modes. The default syntheticPrototype path preserves the non-throwing getCapabilities() contract for synthetic input devices, masks toString() / toString.toString(), and keeps a per-site escape hatch for the direct-prototype and disabled semantics.
  • injected/playwright.config.js:83 and injected/integration-test/device-enumeration.spec.js:8-351 - info: The previously failing chrome-mv3 device-enumeration target is now covered with the async results-ready wait and mode-specific assertions.

Security Assessment

  • No new actionable security findings in this revision. I did not find changes to message bridge trust boundaries, origin validation, nativeData, postMessage, iframe access, network fetches, or CSS injection.
  • The live InputDeviceInfo.prototype/getCapabilities lookup is the same residual hardening point discussed earlier; the new config switch documents and contains the compatibility trade-off, so I am not re-raising it as a duplicate blocker.

Risk Level

High Risk - This still changes the observable surface of an injected browser API object returned to page scripts, but it is config-gated and now has targeted integration coverage for the supported modes.

Recommendations

  • No blocking changes from this pass.
  • If this path is hardened further, prefer a captured/native descriptor or method reference for the getCapabilities capability decision so page prototype tampering cannot influence shim selection.

Validation: npm run test-int -- integration-test/device-enumeration.spec.js --project=chrome-mv3 --reporter=list passed (7 tests), and git diff --check origin/main...origin/jkt/auto/device-enumeration-getcapabilities-fix-5de6 passed.

Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

cursoragent and others added 2 commits May 12, 2026 09:10
If a site needs the unaltered native semantics, the entire enumerateDevices feature can already be disabled, in which case no synthetic devices are produced and the native getCapabilities works normally. The 'disabled' shim mode kept producing synthetic devices but reintroduced the illegal-invocation throw, which is just the original bug.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
Both shim modes (syntheticPrototype, instanceOwn) build the same masked no-op descriptor for getCapabilities and call this.defineProperty with the same flags. Pull that into a single helper to keep the createMediaDeviceInfo branches readable.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Web Compatibility Assessment

  • injected/src/features/web-compat.js:1171-1326 - info: The current revision keeps the getCapabilities() fix behind the existing webCompat.enumerateDevices setting. The default syntheticPrototype mode avoids the native brand-check throw for synthetic input devices, preserves instanceof InputDeviceInfo, returns {}, and masks both toString() and toString.toString() in the covered path. The instanceOwn mode remains available for sites that need direct InputDeviceInfo.prototype identity, with the expected own-property trade-off.
  • injected/integration-test/device-enumeration.spec.js:13-305 and injected/playwright.config.js:406-417 - info: The Chrome MV3 regression coverage now waits for async page results and covers the default shim plus instanceOwn behavior. I do not see a runtime web-compat blocker in the current test/config changes.

Security Assessment

  • No new actionable security findings in this revision. I did not find changes to message bridge trust boundaries, origin validation, nativeData, postMessage, iframe access, network fetches, or CSS injection.
  • Residual hardening note, not a blocker: the shim decision still reads from the live InputDeviceInfo.prototype chain when checking typeof ...getCapabilities; a captured/native descriptor would be stronger against hostile prototype tampering, but this is the same non-blocking point raised in earlier passes.

Risk Level

High Risk - The PR changes the observable surface of an injected browser API object returned to page scripts, though the behavior is config-gated and covered by targeted integration tests.

Recommendations

  • No blocking changes from this pass.
  • If hardening continues, prefer a captured InputDeviceInfo.prototype.getCapabilities descriptor or method reference for the capability decision.

Validation: npm run test-int -- integration-test/device-enumeration.spec.js --project=chrome-mv3 --reporter=list passed (6 tests), and git diff --check c0029efd3832e24977d83ca656e65d3042b3a2d2...39c2b5a96c258ad1178b1268f175ac37c37618ae passed.

Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Web Compatibility Assessment

  • injected/src/features/web-compat.js:1160-1322 - info: The current revision keeps the synthetic InputDeviceInfo#getCapabilities() path masked with wrapToString() including toString.toString(), and preserves native-like method descriptor flags. The default syntheticPrototype mode intentionally trades direct prototype identity for hiding the shim from instance hasOwnProperty() checks; instanceOwn remains available for sites that require Object.getPrototypeOf(device) === InputDeviceInfo.prototype.
  • injected/src/features/web-compat.js:1290-1291 - info: Unknown getCapabilitiesShim values now fall back to syntheticPrototype; there is no longer a separate disabled mode in this revision. That is acceptable from this pass because the whole enumerateDevices subfeature remains remote-config gated, but it does mean rollback is at the subfeature level rather than the individual shim behavior.
  • injected/playwright.config.js:83 and injected/integration-test/device-enumeration.spec.js:8-322 - info: The chrome-mv3 coverage now exercises the async result wait, native illegal-invocation regression case, default synthetic-prototype shim, and instanceOwn compatibility mode.

Security Assessment

  • No new actionable security findings in this synchronized revision. I did not find changes to message bridge trust boundaries, origin validation, nativeData, postMessage, iframe access, network fetches, or CSS injection.
  • The live InputDeviceInfo.prototype/getCapabilities lookup remains the same residual hardening point discussed earlier; I am not re-raising it as a duplicate blocker.

Risk Level

High Risk - This changes the observable surface of an injected browser API object returned to page scripts, but it is config-gated and has targeted integration coverage for the supported modes.

Recommendations

  • No blocking changes from this pass.
  • If this path is hardened further, prefer a captured/native descriptor or method reference for the getCapabilities capability decision so page prototype tampering cannot influence shim selection.

Validation: npm run test-int -- integration-test/device-enumeration.spec.js --project=chrome-mv3 --reporter=list passed (6 tests), and git diff --check c0029efd3832e24977d83ca656e65d3042b3a2d2...FETCH_HEAD passed.

Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

…viceInfo prototypes

Replaces the single-method getCapabilities shim with a generic prototype-walker that defines a masked no-op for every function-valued own property on MediaDeviceInfo.prototype and InputDeviceInfo.prototype. Explicit instance-level definitions (deviceId, kind, label, groupId, toJSON) win, so toJSON semantics are preserved.

Renames the per-site setting from 'getCapabilitiesShim' to 'shimMode' since it now covers every method in the space, not just getCapabilities. Adds a regression test that plants a synthetic future method on InputDeviceInfo.prototype and verifies it gets auto-shimmed with a native-looking name/toString, while toJSON keeps its explicit own behavior.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Web Compatibility Assessment

  • injected/src/features/web-compat.js:1174-1196 - error: defineSyntheticMethodShims() scans the live InputDeviceInfo.prototype / MediaDeviceInfo.prototype chain at enumerateDevices() call time and installs DDG no-op shims for every function-valued property it finds. That shadows page/polyfill/analytics methods added or wrapped before enumerateDevices() runs; real MediaDeviceInfo objects would inherit the page's method, but synthetic devices now return {} instead. The new test at injected/integration-test/device-enumeration.spec.js:307-365 codifies this by adding futurePrototypeMethod on the page prototype and expecting the DDG shim to replace it, which is a third-party script compatibility regression.

Security Assessment

  • injected/src/features/web-compat.js:1175-1183 - warning: the new helper re-reads page-reachable globals (Set, Object.getOwnPropertyNames, Object.getOwnPropertyDescriptor, Object.prototype.hasOwnProperty) when the page calls enumerateDevices(). A hostile page can monkey-patch these to suppress shim discovery, force the catch/fallback path, or cause arbitrary page-added methods to be treated as protected native methods. Prefer captured references from captured-globals.js and add a captured getOwnPropertyNames export if this helper remains generic.
  • No nativeData, message bridge, origin-validation, postMessage, iframe, network, or CSS-injection issues found in this diff.

Risk Level

High Risk - This changes the observable API surface of injected MediaDeviceInfo/InputDeviceInfo objects returned to page scripts, including prototype-chain behavior, although it remains behind the existing webCompat.enumerateDevices config gate.

Recommendations

  1. Restrict method shimming to native descriptors captured before page script mutation, or hard-code the currently needed native method (getCapabilities) rather than scanning live page-mutable prototypes.
  2. If a prototype method has been replaced by page code, preserve that page-visible method semantics instead of shadowing it with a DDG {} shim.
  3. Use captured globals for the helper (Set, descriptor lookup, hasOwnProperty, and a new captured getOwnPropertyNames) and add a regression test where a page-defined prototype method still runs on synthetic devices.

Validation: npm run test-int -- integration-test/device-enumeration.spec.js --project=chrome-mv3 --reporter=list passed (7 tests), and git diff --check c0029efd3832e24977d83ca656e65d3042b3a2d2...69acd373711ea8cf470549c2c0aff88c77b38004 passed.

Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

Comment thread injected/src/features/web-compat.js Outdated
…ing name

Per review: the generic for-loop over MediaDeviceInfo/InputDeviceInfo prototypes is overkill for the only known problem method (getCapabilities). Revert to the single defineSyntheticGetCapabilities helper, but keep the per-site setting named 'shimMode' (not 'getCapabilitiesShim') so we can extend it to other shims later without another config rename.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Web Compatibility Assessment

  • injected/src/features/web-compat.js:1166-1245 - info: The current revision no longer scans live MediaDeviceInfo / InputDeviceInfo prototypes for arbitrary function properties. It is back to a single getCapabilities shim, masked with wrapToString() including toString.toString(), and defined via this.defineProperty() so the captured define-property path is used.
  • injected/src/features/web-compat.js:1201-1224 - info: The default syntheticPrototype mode intentionally hides getCapabilities from instance hasOwnProperty() checks while preserving instanceof InputDeviceInfo; instanceOwn remains available for sites that need direct Object.getPrototypeOf(device) === InputDeviceInfo.prototype semantics. This is still observable API-surface shaping, but the trade-off is config-selectable under the existing webCompat.enumerateDevices gate.
  • injected/integration-test/device-enumeration.spec.js and injected/playwright.config.js - info: The Chrome MV3 regression coverage now waits for async page results and exercises the native illegal-invocation case, default synthetic-prototype shim, masked toString(), delete behavior, and shimMode=instanceOwn.

Security Assessment

  • No new actionable security findings in this revision. I did not find changes to message bridge trust boundaries, origin validation, nativeData, postMessage, iframe access, network fetches, CSS injection, or captured-globals-sensitive messaging paths.
  • Residual hardening note, not a blocker: defineSyntheticGetCapabilities() still decides whether to install the shim by reading target.getCapabilities through the live page-visible prototype chain. A captured native descriptor would be stronger against hostile prototype tampering, but this is the same non-blocking point from earlier revisions.

Risk Level

High Risk - The PR changes the observable surface of synthetic MediaDeviceInfo / InputDeviceInfo objects returned to page scripts, though it is config-gated and now narrowly scoped to the known getCapabilities compatibility issue.

Recommendations

  • No blocking changes from this pass.
  • If this path is hardened further, prefer a captured/native InputDeviceInfo.prototype.getCapabilities descriptor or method reference for the capability decision.

Validation: npm run test-int -- integration-test/device-enumeration.spec.js --project=chrome-mv3 --reporter=list passed (6 tests), and git diff --check c0029efd3832e24977d83ca656e65d3042b3a2d2...8cd98eee9ca761f7ff3bd2e7610a7a2872f5faad passed.

Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

@jonathanKingston
jonathanKingston marked this pull request as ready for review May 12, 2026 14:00
@github-actions
github-actions Bot requested a review from laghee May 12, 2026 14:00

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Web Compatibility Assessment

  • injected/src/features/web-compat.js:1167-1322 - info: The runtime change remains narrowly scoped to synthetic InputDeviceInfo#getCapabilities() for the existing webCompat.enumerateDevices path. The shim is named, wrapToString()-masked including toString.toString(), returns {}, and keeps the syntheticPrototype / instanceOwn trade-off configurable.
  • injected/integration-test/helpers/harness.js:25-34 - info: The latest revision only changes the Chrome extension test harness to resolve the extension directory to an absolute path before passing it to Chromium. I do not see an injected-runtime compatibility issue from this test-only change.
  • injected/integration-test/device-enumeration.spec.js:8-305 and injected/playwright.config.js:521-532 - info: Chrome MV3 coverage now exercises the async result wait, native illegal-invocation regression case, default shim behavior, masking, delete behavior, and shimMode=instanceOwn.

Security Assessment

  • No new actionable security findings in this revision. I did not find changes to message bridge trust boundaries, origin validation, nativeData, postMessage, iframe access, network fetches, CSS injection, or captured-globals-sensitive messaging paths.
  • Residual hardening note, not a blocker: defineSyntheticGetCapabilities() still decides whether to install the shim by reading target.getCapabilities through the live page-visible prototype chain. A captured native descriptor would be stronger against hostile prototype tampering, but this is unchanged from the prior assessment.

Risk Level

High Risk - The PR changes the observable surface of synthetic MediaDeviceInfo / InputDeviceInfo objects returned to page scripts, though it is config-gated and now has targeted integration coverage.

Recommendations

  • No blocking changes from this pass.
  • If hardening continues, prefer a captured/native InputDeviceInfo.prototype.getCapabilities descriptor or method reference for the capability decision.

Validation: npm run test-int -- integration-test/device-enumeration.spec.js --project=chrome-mv3 --reporter=list passed (6 tests), and git diff --check c0029efd3832e24977d83ca656e65d3042b3a2d2...abbaf83fa0ee010da4f7f02e43d04075847797b7 passed.

Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

@laghee laghee left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall (didn't test directly with windows in the interest of time, but could if needed). We could reuse a shared prototype here (create one synthetic per web-compat instance instead of per device), but doesn't seem vital (also maybe there's some potential side-effect to doing that).

@jonathanKingston

Copy link
Copy Markdown
Contributor Author

We could reuse a shared prototype here (create one synthetic per web-compat instance instead of per device), but doesn't seem vital (also maybe there's some potential side-effect to doing that).

Yeah, I don't think we need to do that. The reason to share prototypes is to prevent prototype pollution. There wasn't really a risk of this here at all.

@jonathanKingston
jonathanKingston added this pull request to the merge queue May 15, 2026
Merged via the queue into main with commit 082a71f May 15, 2026
44 checks passed
@jonathanKingston
jonathanKingston deleted the jkt/auto/device-enumeration-getcapabilities-fix-5de6 branch May 15, 2026 11:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver-patch Bug fix / internal — no release needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants