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();
}