Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion packages/trace-viewer/src/ui/workbenchLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,19 @@ export const WorkbenchLoader: React.FunctionComponent<{
return () => document.removeEventListener('paste', listener);
});
React.useEffect(() => {
const allowedOrigins = new Set<string>([window.location.origin]);
const allowParam = new URL(window.location.href).searchParams.get('allowPostMessageOrigin');
if (allowParam) {
try {
// Normalize via the URL parser so callers cannot smuggle in paths or
// wildcards, and so we compare on the same shape MessageEvent.origin uses.
allowedOrigins.add(new URL(allowParam).origin);
} catch {
// Ignore malformed values; default same-origin policy still applies.
}
}
const listener = (e: MessageEvent) => {
if (e.origin !== window.location.origin)
if (!allowedOrigins.has(e.origin))
return;
const { method, params } = e.data;

Expand Down
65 changes: 65 additions & 0 deletions tests/library/trace-viewer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2062,6 +2062,71 @@ test("shouldn't render not-blob trace received from message", async ({ showTrace
await expect(traceViewer.actionTitles).not.toBeVisible();
});

test('should ignore cross-origin blob trace by default', {
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/40960' },
}, async ({ showTraceViewer, server }) => {
const traceViewer = await showTraceViewer(undefined, { host: 'localhost' });
const viewerURL = traceViewer.page.url();

// Host an embedder page on a different origin than the viewer and have it
// iframe the viewer, then push a Blob to it via postMessage. Without an
// explicit opt-in the viewer must ignore cross-origin messages.
server.setRoute('/embed-trace-viewer.html', (_, res) => {
res.setHeader('Content-Type', 'text/html');
res.end(`<!DOCTYPE html><iframe id="viewer" src="${viewerURL}"></iframe>`);
});
await traceViewer.page.goto(`${server.PREFIX}/embed-trace-viewer.html`);
await traceViewer.page.frameLocator('#viewer').locator('.drop-target').waitFor();

await traceViewer.page.evaluate(trace => {
const uint8Array = Uint8Array.from(atob(trace), c => c.charCodeAt(0));
const iframe = document.getElementById('viewer') as HTMLIFrameElement;
iframe.contentWindow!.postMessage({
method: 'load',
params: {
trace: new Blob([uint8Array], { type: 'application/zip' }),
}
}, '*');
}, fs.readFileSync(traceFile, 'base64'));

// Drop target should remain visible inside the iframe, meaning the load was rejected.
const viewerFrame = traceViewer.page.frameLocator('#viewer');
// Give the listener a chance to run before asserting nothing happened.
await traceViewer.page.waitForTimeout(500);
await expect(viewerFrame.locator('.drop-target')).toBeVisible();
await expect(viewerFrame.locator('.action-title')).not.toBeVisible();
});

test('should accept cross-origin blob trace when allowPostMessageOrigin matches', {
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/40960' },
}, async ({ showTraceViewer, server }) => {
const traceViewer = await showTraceViewer(undefined, { host: 'localhost' });
const viewerURL = new URL(traceViewer.page.url());
viewerURL.searchParams.set('allowPostMessageOrigin', server.PREFIX);

server.setRoute('/embed-trace-viewer.html', (_, res) => {
res.setHeader('Content-Type', 'text/html');
res.end(`<!DOCTYPE html><iframe id="viewer" src="${viewerURL.toString()}"></iframe>`);
});
await traceViewer.page.goto(`${server.PREFIX}/embed-trace-viewer.html`);
const viewerFrame = traceViewer.page.frameLocator('#viewer');
await viewerFrame.locator('.drop-target').waitFor();

await traceViewer.page.evaluate(trace => {
const uint8Array = Uint8Array.from(atob(trace), c => c.charCodeAt(0));
const iframe = document.getElementById('viewer') as HTMLIFrameElement;
iframe.contentWindow!.postMessage({
method: 'load',
params: {
trace: new Blob([uint8Array], { type: 'application/zip' }),
}
}, '*');
}, fs.readFileSync(traceFile, 'base64'));

await expect(viewerFrame.locator('.drop-target')).not.toBeVisible();
await expect(viewerFrame.locator('.action-title').first()).toBeVisible();
});

test('should not trip over complex urls in style tags', {
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/35681' },
}, async ({ runAndTrace, page }) => {
Expand Down