diff --git a/packages/desktop/e2e/browser-tabs.e2e.mjs b/packages/desktop/e2e/browser-tabs.e2e.mjs index f06860d862..150cea0b7c 100644 --- a/packages/desktop/e2e/browser-tabs.e2e.mjs +++ b/packages/desktop/e2e/browser-tabs.e2e.mjs @@ -213,8 +213,18 @@ async function settleDesktopStartup(page) { } } +const CSP_PAGE_PATH = "/csp-no-eval"; + async function startTargetPage() { - const server = createServer((_request, response) => { + const server = createServer((request, response) => { + if (request.url === CSP_PAGE_PATH) { + response.writeHead(200, { + "content-type": "text/html; charset=utf-8", + "content-security-policy": "default-src 'self'; script-src 'self'", + }); + response.end("CSP page

CSP page heading

"); + return; + } response.writeHead(200, { "content-type": "text/html; charset=utf-8" }); response.end(` @@ -290,6 +300,42 @@ async function waitForGuestActiveElement(client, browserId, elementId) { return false; } +// A page whose Content Security Policy omits 'unsafe-eval' refuses eval(); the tools must still work. +async function verifyEvaluateOnPageWithoutUnsafeEval(client, targetUrl) { + const created = await callBrowserTool(client, "browser_new_tab", { + url: new URL(CSP_PAGE_PATH, targetUrl).href, + }); + const browserId = created.browserId; + assert(typeof browserId === "string", "browser_new_tab returned no browserId for the CSP page"); + try { + await callBrowserTool(client, "browser_wait", { + browserId, + text: "CSP page heading", + timeoutMs: 15_000, + }); + const title = await callBrowserTool(client, "browser_evaluate", { + browserId, + function: "() => document.title", + }); + assert( + JSON.parse(title.resultJson) === "CSP page", + `browser_evaluate on a page without 'unsafe-eval' returned ${title.resultJson}`, + ); + // The control: the page itself really does refuse eval(). + const probe = await callBrowserTool(client, "browser_evaluate", { + browserId, + function: + '() => { try { (0, eval)("1"); return "eval allowed"; } catch (error) { return error.message; } }', + }); + assert( + /Content Security Policy/.test(JSON.parse(probe.resultJson)), + `The CSP page did not refuse eval(): ${probe.resultJson}`, + ); + } finally { + await callBrowserTool(client, "browser_close_tab", { browserId }); + } +} + async function createCallerAgent(daemonPort, workspaceId = workspaceIds[0]) { const transport = new StreamableHTTPClientTransport( new URL(`http://127.0.0.1:${daemonPort}/mcp/agents`), @@ -675,6 +721,8 @@ async function runRegression({ document.body.appendChild(overlayRoot); }); + await verifyEvaluateOnPageWithoutUnsafeEval(client, targetUrl); + const created = await callBrowserTool(client, "browser_new_tab", { url: targetUrl }); const browserId = created.browserId; assert(typeof browserId === "string", "browser_new_tab returned no browserId"); @@ -1106,6 +1154,7 @@ async function runRegression({ snapshot: "passed", click: "passed", localPageSelectors: "passed", + evaluateWithoutUnsafeEval: "passed", }; }