From d76d1f8ff9ad5e1658bfb100b867db8823c9d26c Mon Sep 17 00:00:00 2001 From: Holger Benl Date: Tue, 6 Oct 2026 12:38:28 +0200 Subject: [PATCH 1/2] Fix BiDiFacade.evaluate arguments --- src/firefox/bidi.ts | 2 +- src/firefox/index.ts | 2 +- src/firefox/snapshot/resolver.ts | 2 +- tests/firefox/snapshot/resolver.test.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/firefox/bidi.ts b/src/firefox/bidi.ts index 3a4f2cd..14d5e89 100644 --- a/src/firefox/bidi.ts +++ b/src/firefox/bidi.ts @@ -160,7 +160,7 @@ export class BiDiFacade extends EventEmitter { context: BrowsingContext.BrowsingContext, expression: string ): Promise { - return remoteValueToNative(await this.evaluateRaw(expression, context)) as T; + return remoteValueToNative(await this.evaluateRaw(context, expression)) as T; } async evaluateRaw( diff --git a/src/firefox/index.ts b/src/firefox/index.ts index 8ff5907..0d9e171 100644 --- a/src/firefox/index.ts +++ b/src/firefox/index.ts @@ -144,7 +144,7 @@ export class FirefoxClient { * native value; throws on a script exception. */ async evaluate(expression: string): Promise { - return await this.getBidi().evaluate(expression, this.getContext()); + return await this.getBidi().evaluate(this.getContext(), expression); } // UID-based input methods diff --git a/src/firefox/snapshot/resolver.ts b/src/firefox/snapshot/resolver.ts index bf33a4a..fe04b62 100644 --- a/src/firefox/snapshot/resolver.ts +++ b/src/firefox/snapshot/resolver.ts @@ -26,7 +26,7 @@ export class UidResolver { */ async clear(context: BrowsingContext.BrowsingContext): Promise { try { - await this.bidi.evaluate(CLEAR_SCRIPT, context); + await this.bidi.evaluate(context, CLEAR_SCRIPT); logDebug('Snapshot UIDs cleared'); } catch { logDebug('Unable to clear snapshot UIDs (page may be navigating)'); diff --git a/tests/firefox/snapshot/resolver.test.ts b/tests/firefox/snapshot/resolver.test.ts index 067a4b3..60aebd0 100644 --- a/tests/firefox/snapshot/resolver.test.ts +++ b/tests/firefox/snapshot/resolver.test.ts @@ -68,7 +68,7 @@ describe('UidResolver', () => { await resolver.clear(CONTEXT); expect(mockBiDi.evaluate).toHaveBeenCalledOnce(); - expect(mockBiDi.evaluate.mock.calls[0][0]).toContain('__clearUidRegistry'); + expect(mockBiDi.evaluate.mock.calls[0][1]).toContain('__clearUidRegistry'); }); it('should not throw when the page cannot be reached', async () => { From b3a38aa4917a9bac64a79bb7d663dfc94bf850a0 Mon Sep 17 00:00:00 2001 From: Holger Benl Date: Tue, 6 Oct 2026 13:00:33 +0200 Subject: [PATCH 2/2] Bug 2073916 - Run the snapshot manager in a sandbox --- src/firefox/bidi.ts | 22 ++++++++++++++-------- src/firefox/snapshot/manager.ts | 5 ++++- src/firefox/snapshot/resolver.ts | 21 ++++++++++++++------- tests/firefox/snapshot/resolver.test.ts | 10 +++++++--- tsconfig.json | 1 - 5 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/firefox/bidi.ts b/src/firefox/bidi.ts index 14d5e89..12cfde6 100644 --- a/src/firefox/bidi.ts +++ b/src/firefox/bidi.ts @@ -158,19 +158,21 @@ export class BiDiFacade extends EventEmitter { async evaluate( context: BrowsingContext.BrowsingContext, - expression: string + expression: string, + sandbox?: string ): Promise { - return remoteValueToNative(await this.evaluateRaw(context, expression)) as T; + return remoteValueToNative(await this.evaluateRaw(context, expression, sandbox)) as T; } async evaluateRaw( context: BrowsingContext.BrowsingContext, - expression: string + expression: string, + sandbox?: string ): Promise { const result = await this.sendCommand('script.evaluate', { expression, awaitPromise: true, - target: { context }, + target: { context, sandbox }, }); if (result.type === 'success') { return result.result; @@ -183,21 +185,25 @@ export class BiDiFacade extends EventEmitter { async callFunction( context: BrowsingContext.BrowsingContext, functionDeclaration: string, - args: Script.LocalValue[] + args: Script.LocalValue[], + sandbox?: string ): Promise { - return remoteValueToNative(await this.callFunctionRaw(context, functionDeclaration, args)) as T; + return remoteValueToNative( + await this.callFunctionRaw(context, functionDeclaration, args, sandbox) + ) as T; } async callFunctionRaw( context: BrowsingContext.BrowsingContext, functionDeclaration: string, - args: Script.LocalValue[] + args: Script.LocalValue[], + sandbox?: string ): Promise { const result = await this.sendCommand('script.callFunction', { functionDeclaration, arguments: args, awaitPromise: true, - target: { context }, + target: { context, sandbox }, }); if (result.type === 'success') { return result.result; diff --git a/src/firefox/snapshot/manager.ts b/src/firefox/snapshot/manager.ts index 52769e3..c64f0fd 100644 --- a/src/firefox/snapshot/manager.ts +++ b/src/firefox/snapshot/manager.ts @@ -14,6 +14,8 @@ import type { Snapshot, SnapshotJson, InjectedScriptResult } from './types.js'; import { formatSnapshotTree } from './formatter.js'; import { UidResolver } from './resolver.js'; +export const SNAPSHOT_MANAGER_SANDBOX = 'snapshot-manager'; + /** * Options for snapshot creation */ @@ -218,7 +220,8 @@ export class SnapshotManager { return window.__createSnapshot(nextElementId, options); } `, - [nativeToLocalValue(nextElementId), nativeToLocalValue(options || {})] + [nativeToLocalValue(nextElementId), nativeToLocalValue(options || {})], + SNAPSHOT_MANAGER_SANDBOX ); return result; diff --git a/src/firefox/snapshot/resolver.ts b/src/firefox/snapshot/resolver.ts index fe04b62..41c383d 100644 --- a/src/firefox/snapshot/resolver.ts +++ b/src/firefox/snapshot/resolver.ts @@ -5,6 +5,7 @@ */ import type { BrowsingContext, Script } from 'webdriver-bidi-protocol'; +import { SNAPSHOT_MANAGER_SANDBOX } from './manager.js'; import { BiDiFacade } from '../bidi.js'; import { nativeToLocalValue } from '../../utils/local-value.js'; import { logDebug } from '../../utils/logger.js'; @@ -26,7 +27,7 @@ export class UidResolver { */ async clear(context: BrowsingContext.BrowsingContext): Promise { try { - await this.bidi.evaluate(context, CLEAR_SCRIPT); + await this.bidi.evaluate(context, CLEAR_SCRIPT, SNAPSHOT_MANAGER_SANDBOX); logDebug('Snapshot UIDs cleared'); } catch { logDebug('Unable to clear snapshot UIDs (page may be navigating)'); @@ -40,9 +41,12 @@ export class UidResolver { context: BrowsingContext.BrowsingContext, uid: string ): Promise { - const selector = await this.bidi.callFunction(context, SELECTOR_SCRIPT, [ - nativeToLocalValue(uid), - ]); + const selector = await this.bidi.callFunction( + context, + SELECTOR_SCRIPT, + [nativeToLocalValue(uid)], + SNAPSHOT_MANAGER_SANDBOX + ); if (!selector) { throw new Error(notFoundMessage(uid)); } @@ -57,9 +61,12 @@ export class UidResolver { context: BrowsingContext.BrowsingContext, uid: string ): Promise { - const element = await this.bidi.callFunctionRaw(context, RESOLVE_SCRIPT, [ - nativeToLocalValue(uid), - ]); + const element = await this.bidi.callFunctionRaw( + context, + RESOLVE_SCRIPT, + [nativeToLocalValue(uid)], + SNAPSHOT_MANAGER_SANDBOX + ); if (element?.type !== 'node' || !element.sharedId) { throw new Error(notFoundMessage(uid)); } diff --git a/tests/firefox/snapshot/resolver.test.ts b/tests/firefox/snapshot/resolver.test.ts index 60aebd0..a9e5500 100644 --- a/tests/firefox/snapshot/resolver.test.ts +++ b/tests/firefox/snapshot/resolver.test.ts @@ -4,6 +4,7 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; import { BrowsingContext, Script } from 'webdriver-bidi-protocol'; +import { SNAPSHOT_MANAGER_SANDBOX } from '@/firefox/snapshot/manager'; import { UidResolver } from '@/firefox/snapshot/resolver.js'; import { nativeToLocalValue } from '@/utils/local-value'; @@ -33,9 +34,12 @@ describe('UidResolver', () => { const element = await resolver.resolveUidToElement(CONTEXT, 'e0'); expect(element).toEqual({ sharedId: 'shared-1' }); - expect(mockBiDi.callFunctionRaw).toHaveBeenCalledWith(CONTEXT, expect.any(String), [ - nativeToLocalValue('e0'), - ]); + expect(mockBiDi.callFunctionRaw).toHaveBeenCalledWith( + CONTEXT, + expect.any(String), + [nativeToLocalValue('e0')], + SNAPSHOT_MANAGER_SANDBOX + ); }); it('should throw when the page has no element for the UID', async () => { diff --git a/tsconfig.json b/tsconfig.json index 9cdff3c..45b8f1b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,7 +26,6 @@ "noUncheckedIndexedAccess": true, "allowUnusedLabels": false, "allowUnreachableCode": false, - "exactOptionalPropertyTypes": true, "noImplicitOverride": true, "useUnknownInCatchVariables": true, "isolatedModules": true