test(injected): add unit coverage for autofillPasskeys passkey interception - #2738
test(injected): add unit coverage for autofillPasskeys passkey interception#2738cursor[bot] wants to merge 4 commits into
Conversation
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/bc-b6b1f301-fd84-44f9-a038-9278e053ee7a-d810")git submodule (Windows): git -C submodules/content-scope-scripts fetch origin pr-releases/jkt/auto/bc-b6b1f301-fd84-44f9-a038-9278e053ee7a-d810
git -C submodules/content-scope-scripts checkout origin/pr-releases/jkt/auto/bc-b6b1f301-fd84-44f9-a038-9278e053ee7a-d810Pin to exact commitnpm (Android / Extension): Swift Package Manager (Apple): .package(url: "https://github.com/duckduckgo/content-scope-scripts.git", revision: "cdd64df570e665a3cbb432a732bf3bf32497d344")git submodule (Windows): git -C submodules/content-scope-scripts fetch origin pr-releases/jkt/auto/bc-b6b1f301-fd84-44f9-a038-9278e053ee7a-d810
git -C submodules/content-scope-scripts checkout cdd64df570e665a3cbb432a732bf3bf32497d344 |
[Beta] Generated file diffTime updated: Fri, 12 Jun 2026 14:37:12 GMT |
Add Jasmine tests for the Windows passkey interception flow merged in #2571, covering request registration, native message correlation, abort handling, supersede behavior, and credentials.get pass-through guards. Fix ESM import paths in autofill-passkeys.js so the feature can be loaded by the unit test runner (no behavior change). Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
26130a6 to
0230491
Compare
There was a problem hiding this comment.
Stale comment
Injected PR Evaluation: Web Compatibility & Security
Reviewed diff
7a9074e..0230491— production change is import-path normalization only; the bulk of the PR is new unit tests forAutofillPasskeys.Web Compatibility Assessment
File Lines Severity Finding injected/src/features/autofill-passkeys.js1, 10 info Import paths gain explicit .jsextensions (content-feature.js,captured-globals.js). No runtime behavior change; consistent with other strict-core feature files (web-compat.js,message-bridge.js).injected/unit-test/autofill-passkeys.js(new) info Tests validate existing pass-through paths that protect compatibility: non- conditionalmediation, missingpublicKey, non-secure context, foreignCredentialsContainerreceiver, and WebIDL promise semantics (throwing option getters reject the returned promise rather than throwing synchronously).No web-compat regressions identified in the production diff.
Security Assessment
File Lines Severity Finding injected/src/features/autofill-passkeys.js1, 10 info No security-relevant logic changes. injected/unit-test/autofill-passkeys.js(new) info Tests exercise existing security boundaries: rpIdfalls back tolocation.hostnamewhen not a string (blocks object-coercion attacks),requestIdcorrelation prevents cross-request credential injection, malformed/emptycredentialIdvalues are ignored, invalid base64 rejects. Outboundnotifypayloads use explicit{ rpId, requestId }— nonativeDataspread risk.No new security vulnerabilities introduced.
Risk Level
Low Risk — test-only coverage addition plus cosmetic import-path normalization; no changes to API overrides, messaging transports, captured globals, or initialization order.
Recommendations
- (info) All 16 new specs pass locally. No blocking items.
- (info) The tests are a solid regression harness for the existing
registerPasskeyRequest/credentials.getwrapper contract. Consider adding an integration test (Playwright) in a follow-up if end-to-end native messaging isn't already covered elsewhere — not required for this PR.- (info) The
.jsimport extension change is safe for the bundler pipeline; no action needed.Verdict: Approve from web-compat and security perspective.
Sent by Cursor Automation: Web compat and sec
There was a problem hiding this comment.
Stale comment
Injected PR Evaluation: Web Compatibility & Security
Web Compatibility Assessment
File Lines Severity Finding injected/src/features/autofill-passkeys.js1–10 info Only change is adding .jsextensions to static imports (content-feature.js,captured-globals.js). This is a bundler/ESM path normalization with no runtime behavior change. Mixed extension usage exists elsewhere in the repo; this aligns with newer feature files.injected/unit-test/autofill-passkeys.js(all) info Test-only file. No injected page-world code ships from this diff. injected/unit-test/autofill-passkeys.js377–473 info init credentials.get wrappertests document important pass-through guards already in production: non-conditional mediation, missingpublicKey, non-secure context, and foreignthisreceiver. These are the primary web-compat safety rails for the interception path.No web-compatibility regressions identified in production code.
Security Assessment
File Lines Severity Finding injected/src/features/autofill-passkeys.js1–10 info No logic, messaging, or API-wrapping changes. Security posture of the live feature is unchanged. injected/unit-test/autofill-passkeys.js200–219 info Test confirms rpIdis only taken frompublicKey.rpIdwhentypeof === 'string', rejecting object coercion viatoString()— matches the production guard at line 95.injected/unit-test/autofill-passkeys.js305–337 info Tests verify requestIdcorrelation and malformedcredentialIdrejection, covering the inbound message trust boundary exercised bypasskeySelectedsubscription.injected/unit-test/autofill-passkeys.js187–191, 446–449 info Assertions confirm outbound notify('registerPasskeyRequest', { rpId, requestId })uses an explicit field list — no object spread that could leaknativeData.injected/unit-test/autofill-passkeys.js— info Coverage gap (not a regression): no test for init()early-return whenCredentialsContainer/navigator.credentialsis absent, and no test for therandomUUIDunavailable fallback path (lines 77–80). These are follow-up opportunities, not blockers for this PR.No security vulnerabilities introduced by this diff.
Risk Level
Low Risk — the diff adds unit-test coverage and normalizes import paths; it does not modify API override logic, messaging transports, captured globals, or wrapper utilities.
Recommendations
- (info) Consider follow-up tests for
init()bail-out when the Credentials API is missing, and for therandomUUIDfallback that delegates tooriginalGet.- (info) If native ever delivers credential IDs as base64url (common in WebAuthn), consider a follow-up test + feature check — current
atob()path only handles standard base64 (partially exercised by the invalid-base64 rejection test).- Approve — this PR improves confidence in existing passkey interception behavior without changing production semantics.
Cursor Automation — Web Compatibility & Security review
Sent by Cursor Automation: Web compat and sec
There was a problem hiding this comment.
Stale comment
Web Compatibility & Security Review
Web Compatibility Assessment
No web compatibility regressions identified.
File Lines Severity Finding injected/src/features/autofill-passkeys.js1–10 info Production diff is import-path normalization only ( content-feature.js,captured-globals.js). No runtime behavior change.injected/unit-test/autofill-passkeys.js377–473 info New tests document and lock in correct pass-through behavior: non-conditional mediation, missing publicKey, non-secure contexts, and foreignCredentialsContainerreceivers. These guard against accidental broadening of thecredentials.getinterception.injected/unit-test/autofill-passkeys.js456–472 info Tests confirm WebIDL promise semantics: option-getter throws surface as rejected promises, not synchronous throws — matching native get()contract.Security Assessment
No security vulnerabilities introduced.
File Lines Severity Finding injected/src/features/autofill-passkeys.js1–10 info No changes to messaging, API overrides, or captured-globals usage. injected/unit-test/autofill-passkeys.js200–219 info Test validates rpIdis narrowed viatypeof === 'string'check, rejecting page-controlledtoString()coercion — documents an existing security property of the feature.injected/unit-test/autofill-passkeys.js305–318 info Test validates requestIdmatching on inboundpasskeySelectedmessages, preventing cross-request credential injection.injected/unit-test/autofill-passkeys.js321–354 info Tests cover malformed/invalid credentialIdhandling (empty, non-string, invalid base64).injected/unit-test/autofill-passkeys.js94–101 info Mock messaging uses explicit field construction for notifypayloads — no object spreading that could leaknativeData.Risk Level
Low Risk — test-only additions plus import-path normalization in production code; no changes to API overrides, wrapper utilities, messaging transports, or security-sensitive initialization.
Recommendations
- No blocking issues. The test suite is thorough and exercises security-relevant edge cases (rpId type coercion, requestId correlation, abort handling, superseded requests).
- Optional follow-up (info): Consider a test for the
randomUUIDabsent fallback path (line 77–79 in production), which passes through tooriginalGetwhen UUID generation is unavailable.- Verified locally: All 16 specs in
unit-test/autofill-passkeys.jspass.Sent by Cursor Automation: Web compat and sec
…ify pending
Bugbot finding (medium severity): the two specs that try to prove
a passkey request stays pending raced the request promise against
`Promise.resolve('pending')` — already settled. The race always
resolves to 'pending' regardless of whether the request promise
later settles, so the assertion provides no guard against
hang/early-completion regressions.
Replace the inline race with a settledOrPending() helper that races
against a setTimeout-based sentinel. It returns 'pending' only if
the promise hasn't settled within 50ms; otherwise it returns the
promise's resolved value or rejection reason. Both 'ignores ...
without a matching requestId' and 'ignores malformed credentialId
values' specs now actually verify the documented behaviour.
Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
There was a problem hiding this comment.
Stale comment
Web Compatibility Assessment
No web-compatibility regressions identified in this diff.
File Lines Severity Finding injected/src/features/autofill-passkeys.js1–10 info Production change is limited to adding .jsextensions on static imports (content-feature.js,captured-globals.js). This is a module-resolution convention with no runtime behavior change after bundling.injected/unit-test/autofill-passkeys.js(all) info New unit tests exercise existing CredentialsContainer.prototype.getwrapping logic but do not ship to end users. Test harness globals (Object.defineProperty,setTimeout) are isolated to the Jasmine environment.The tests document several existing pass-through guards that protect compatibility: non-conditional mediation, absent
publicKey, non-secure contexts, and foreignCredentialsContainerreceivers all bypass interception — matching native behavior expectations.Security Assessment
No security vulnerabilities introduced by this diff.
File Lines Severity Finding injected/src/features/autofill-passkeys.js1–10 info No changes to API overrides, messaging payloads, captured-globals usage, or trust-boundary checks. injected/unit-test/autofill-passkeys.js222–242 info Tests confirm existing rpIdhardening: non-stringrpIdvalues (including objects with a hostiletoString) fall back tolocation.hostnameviatypeofcheck, not coercion. This documents correct existing behavior; no production change.injected/unit-test/autofill-passkeys.js327–341 info Tests confirm passkeySelectedhandler ignores payloads with mismatchedrequestId, documenting request-correlation protection already present in production code.Outbound messaging in tests uses explicit
{ rpId, requestId }literals — nonativeDataspread risk in production or test notify paths.Risk Level
Low Risk — This PR adds unit-test coverage and normalizes import paths; it does not modify wrapper utilities, captured globals, messaging transports, or any injected runtime interception logic.
Recommendations
- No action required for web-compat or security on this diff.
- (Optional, info) Consider a Playwright integration test for the full
navigator.credentials.get({ mediation: 'conditional', publicKey })→ nativeregisterPasskeyRequest→passkeySelectedround-trip if one does not already exist; unit tests here validate logic but notwrapMethoddescriptor fidelity (toStringmasking,.name/.lengthpreservation).- (Optional, info) The
settledOrPendinghelper is a good pattern for asserting promise pending state without microtask race false-positives — worth reusing in other async interception tests.Sent by Cursor Automation: Web compat and sec
There was a problem hiding this comment.
Web Compatibility & Security Review
Web Compatibility Assessment
| File | Lines | Severity | Finding |
|---|---|---|---|
injected/src/features/autofill-passkeys.js |
1–10 | info | Only change is adding .js extensions to import paths. No runtime behavior change; consistent with other feature files. |
injected/unit-test/autofill-passkeys.js |
(all) | info | Test-only file. Does not ship to production bundles. |
injected/unit-test/autofill-passkeys.js |
399–495 | info | New tests validate existing pass-through guards: non-conditional mediation, missing publicKey, non-secure context (isSecureContext === false), and foreign CredentialsContainer receivers. These are important web-compat boundaries for the existing wrapMethod shim. |
injected/unit-test/autofill-passkeys.js |
478–494 | info | Tests confirm WebIDL-correct behavior: option getter throws surface as rejected promises, not synchronous throws — matching native credentials.get(). |
No web compatibility regressions introduced by this diff.
Security Assessment
| File | Lines | Severity | Finding |
|---|---|---|---|
injected/src/features/autofill-passkeys.js |
1–10 | info | Import-path-only change. No security surface change. |
injected/unit-test/autofill-passkeys.js |
(all) | info | Test harness uses uncaptured Object.defineProperty / Reflect.deleteProperty on globalThis — acceptable in unit tests; not injected code. |
injected/unit-test/autofill-passkeys.js |
222–242 | info | Tests document an existing security property: rpId must be typeof === 'string'; objects with toString() are rejected in favor of location.hostname. Prevents coercion-based RP ID spoofing at the messaging layer. |
injected/unit-test/autofill-passkeys.js |
327–376 | info | Tests document inbound-message hardening already in production: requestId matching, empty/non-string credentialId ignored, invalid base64 rejects. |
injected/unit-test/autofill-passkeys.js |
— | info | Coverage gap (pre-existing feature, not a regression): no test for the randomUUID absence fallback (registerPasskeyRequest lines 77–80), which silently passes through to native get(). |
No security vulnerabilities introduced by this diff. Tests strengthen confidence in existing trust-boundary checks.
Risk Level
Low Risk — This PR adds unit tests and normalizes two import paths; it does not modify API overrides, messaging transports, captured globals, or initialization logic.
Recommendations
- (info) Consider adding a unit test for the
randomUUIDunavailable fallback path to lock in pass-through behavior. - (info) Existing feature already uses
wrapMethod(toString masking viawrapper-utils); an integration test assertingnavigator.credentials.get.toString()returns[native code]would be a nice follow-up but is out of scope here. - No blocking issues for merge from a web-compat or security perspective.
Automated review — Injected PR Evaluation (Web Compatibility & Security)
Sent by Cursor Automation: Web compat and sec


Asana Task/Github Issue: Coverage automation — addresses untested
autofillPasskeysfeature merged in #2571Description
The
autofillPasskeysfeature interceptsnavigator.credentials.get()for conditional WebAuthn passkey autofill on Windows. It shipped with no unit tests despite complex concurrency, messaging, and WebIDL semantics.This PR adds focused Jasmine coverage for the highest-risk paths and fixes ESM import suffixes in
autofill-passkeys.jsso the module loads under the unit test runner (no runtime behavior change).Risky behavior now covered
registerPasskeyRequestnotifies native withrpId+requestId, subscribes topasskeySelected, and completes via narrowedoriginalGetwith decodedallowCredentials.AbortError;AbortSignalabort/reason handling and already-aborted fast path.requestId, empty/non-stringcredentialId; rejects invalid base64 instead of hanging.publicKey, non-secure context, and foreigncredentials.getreceivers do not register passkey requests.Test files added/updated
injected/unit-test/autofill-passkeys.js(16 specs)injected/src/features/autofill-passkeys.js(.jssuffix on ESM imports only)Why these tests materially reduce regression risk
navigator.credentials.getis a security-sensitive, promise-based API. Regressions in pass-through conditions, abort/supersede handling, or native message correlation can hang site login flows or break WebAuthn ceremonies. These tests lock in the behavior explicitly documented in the #2571 review thread without requiring Windows/WebView2 integration infrastructure.Testing Steps
cd injected && npm run test-unit -- --filter=AutofillPasskeysChecklist
Note
Low Risk
Test-only coverage plus import path fixes; no intentional changes to passkey interception behavior in production.
Overview
Adds Jasmine unit coverage for the
AutofillPasskeysinjected feature (conditional WebAuthn passkey interception) and tweaks ESM import paths inautofill-passkeys.jsso the module loads under the unit test runner.The new
injected/unit-test/autofill-passkeys.jssuite exercisesregisterPasskeyRequest: nativeregisterPasskeyRequestnotify withrpId/requestId,passkeySelectedsubscription, narrowedoriginalGetwith decodedallowCredentials,rpIdfallback tolocation.hostnamewhenpublicKey.rpIdis not a string, superseding pending requests, andAbortSignalhandling.It also locks in message validation (wrong
requestId, malformedcredentialId, invalid base64) and WebIDL-style rejection when option getters throw, plusinitwrapper pass-through for non-conditional mediation, missingpublicKey, non-secure context, and non-singletoncredentials.getreceivers versus interception onnavigator.credentials.Production change: only
.jssuffixes on imports fromcontent-featureandcaptured-globals; no intended runtime behavior change.Reviewed by Cursor Bugbot for commit 3449a00. Bugbot is set up for automated code reviews on this repo. Configure here.