Spaces:
Running
Running
| 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('<!doctype html><title>JavaScript sandbox probe</title>'); | |
| }); | |
| }, | |
| }], | |
| }); | |
| 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 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, 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.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: local execution, 4 blocked capabilities, timeout, and recovery'); | |
| } finally { | |
| await browser?.close(); | |
| await vite?.close(); | |
| } | |