Spaces:
Running
Running
File size: 2,814 Bytes
0ed8124 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | 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();
}
|