Skip to content

Commit 578d3c2

Browse files
Guard a11y frame-sync debug setter access
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent 87ec6d5 commit 578d3c2

3 files changed

Lines changed: 85 additions & 1 deletion

File tree

currentState.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ HyperAgent exposes a TypeScript SDK for browser automation with three primary pa
199199
- `collectExecutionContexts()` now guards `session.on`/`session.off` method reads and listener attach/detach calls,
200200
- context collection now tolerates trap-prone runtime listener method getters while preserving sanitized diagnostics in debug mode.
201201
- `collectExecutionContexts()` now also guards trap-prone `session.send` reads for `Runtime.enable`, preventing getter traps from aborting the context collection path.
202+
- Hardened frame-sync debug handling in a11y context provider:
203+
- `syncFrameContextManager()` now guards trap-prone frame-manager `setDebug` calls, preserving sync flow with sanitized debug warnings.
202204
- Hardened CDP command dispatch in Playwright session adapter:
203205
- `PlaywrightSessionAdapter.send()` now guards trap-prone `session.send` method reads and wraps sync send failures with sanitized/diagnostic context.
204206
- Hardened Playwright session listener wrappers:

src/context-providers/a11y-dom/index.test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,4 +400,76 @@ describe("getA11yDOM error formatting", () => {
400400
errorSpy.mockRestore();
401401
}
402402
});
403+
404+
it("continues when sync frame-manager debug setter traps", async () => {
405+
const logSpy = jest.spyOn(console, "log").mockImplementation(() => {});
406+
const warnSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
407+
const errorSpy = jest.spyOn(console, "error").mockImplementation(() => {});
408+
const page = {
409+
evaluate: jest.fn().mockResolvedValue(undefined),
410+
url: jest.fn(() => "https://example.com"),
411+
} as unknown as Page;
412+
const session = {
413+
id: "session-1",
414+
send: jest.fn(async (method: string) => {
415+
if (method === "Accessibility.getFullAXTree") {
416+
return { nodes: [] };
417+
}
418+
if (method === "Page.getFrameTree") {
419+
throw new Error("frame tree unavailable");
420+
}
421+
return {};
422+
}),
423+
on: jest.fn(),
424+
off: jest.fn(),
425+
};
426+
getCDPClientMock.mockResolvedValue({
427+
rootSession: session,
428+
acquireSession: jest.fn().mockResolvedValue(session),
429+
});
430+
getOrCreateFrameContextManagerMock.mockReturnValue({
431+
setDebug: jest
432+
.fn()
433+
.mockImplementationOnce(() => undefined)
434+
.mockImplementation(() => {
435+
throw new Error(`sync-debug\u0000\n${"x".repeat(2_000)}`);
436+
}),
437+
ensureInitialized: jest.fn().mockResolvedValue(undefined),
438+
captureOOPIFs: jest.fn().mockResolvedValue(undefined),
439+
setFrameFilteringEnabled: jest.fn(),
440+
getOOPIFs: jest.fn(() => []),
441+
getFrameIndex: jest.fn(),
442+
getFrameSession: jest.fn(),
443+
getExecutionContextId: jest.fn(),
444+
getFrameIdByIndex: jest.fn(),
445+
getFrameByBackendNodeId: jest.fn(),
446+
setFrameSession: jest.fn(),
447+
upsertFrame: jest.fn(),
448+
assignFrameIndex: jest.fn(),
449+
});
450+
buildBackendIdMapsMock.mockResolvedValue({
451+
frameMap: new Map(),
452+
backendNodeMap: {},
453+
xpathMap: {},
454+
frameMetadataMap: new Map(),
455+
frameTree: new Map(),
456+
});
457+
458+
try {
459+
const result = await getA11yDOM(page, true);
460+
expect(result.domState).toBe("Error: Could not extract accessibility tree");
461+
const warning = String(
462+
warnSpy.mock.calls.find((call) =>
463+
String(call[0] ?? "").includes("Failed to configure sync debug mode")
464+
)?.[0] ?? ""
465+
);
466+
expect(warning).toContain("[truncated");
467+
expect(warning).not.toContain("\u0000");
468+
expect(warning).not.toContain("\n");
469+
} finally {
470+
logSpy.mockRestore();
471+
warnSpy.mockRestore();
472+
errorSpy.mockRestore();
473+
}
474+
});
403475
});

src/context-providers/a11y-dom/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,17 @@ async function syncFrameContextManager({
364364
rootSession,
365365
debug,
366366
}: SyncFrameContextOptions): Promise<void> {
367-
manager.setDebug(debug);
367+
try {
368+
manager.setDebug(debug);
369+
} catch (error) {
370+
if (debug) {
371+
console.warn(
372+
`[FrameContext] Failed to configure sync debug mode: ${formatA11yDiagnostic(
373+
error
374+
)}`
375+
);
376+
}
377+
}
368378

369379
const { frameTree } =
370380
await rootSession.send<Protocol.Page.GetFrameTreeResponse>(

0 commit comments

Comments
 (0)