import { chromium } from '@playwright/test'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { createServer as createViteServer } from 'vite'; const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); let browser; let vite; try { vite = await createViteServer({ root, logLevel: 'error', server: { host: '127.0.0.1', port: 0 }, plugins: [{ name: 'js-sandbox-probe-page', configureServer(server) { server.middlewares.use('/js-sandbox-probe.html', (_request, response) => { response.setHeader('Content-Type', 'text/html; charset=utf-8'); response.end('JavaScript sandbox probe'); }); }, }], }); await vite.listen(); const address = vite.httpServer?.address(); if (!address || typeof address === 'string') throw new Error('Vite probe page did not expose a TCP port'); browser = await chromium.launch({ headless: true }); const page = await browser.newPage(); await page.goto(`http://127.0.0.1:${address.port}/js-sandbox-probe.html`); const result = await page.evaluate(async () => { const { runJavaScript } = await import('/src/lib/js-sandbox.ts'); const valid = await runJavaScript('console.log("local-only"); return 2 + 3;', 2_000); const expression = await runJavaScript('19 * 23', 2_000); const blocked = []; for (const source of [ 'return await fetch("data:text/plain,leak");', 'return await navigator.storage.getDirectory();', 'return (await import("data:text/javascript,export default 7")).default;', 'return (await Function("return import(\\"data:text/javascript,export default 9\\")")()).default;', ]) { try { await runJavaScript(source, 2_000); blocked.push(false); } catch { blocked.push(true); } } let timedOut = false; try { await runJavaScript('while (true) {}', 100); } catch (error) { timedOut = error instanceof Error && error.message.includes('exceeded 100 ms'); } const recovered = await runJavaScript('return "responsive";', 2_000); return { valid, expression, blocked, timedOut, recovered }; }); if (result.valid.value !== '5' || result.valid.logs[0] !== 'local-only') { throw new Error(`Valid local evaluation failed: ${JSON.stringify(result.valid)}`); } if (result.expression.value !== '437') { throw new Error(`Expression evaluation failed: ${JSON.stringify(result.expression)}`); } if (result.blocked.some((value) => !value)) { throw new Error(`A forbidden JavaScript capability escaped the sandbox: ${JSON.stringify(result.blocked)}`); } if (!result.timedOut || result.recovered.value !== 'responsive') { throw new Error(`Timeout/recovery contract failed: ${JSON.stringify(result)}`); } console.log('js_eval sandbox passed: expression and function-body execution, 4 blocked capabilities, timeout, and recovery'); } finally { await browser?.close(); await vite?.close(); }