Bonsai-Chat-WebGPU / scripts /test-artifact-csp.mjs
WaveCut's picture
Release browser-local Bonsai WebGPU chat
0ed8124 verified
Raw
History Blame Contribute Delete
5.25 kB
import { chromium } from '@playwright/test';
import { createServer as createHttpServer } from 'node:http';
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)), '..');
const probeHits = [];
function listen(server) {
return new Promise((resolveListen, reject) => {
server.once('error', reject);
server.listen(0, '127.0.0.1', () => {
server.off('error', reject);
const address = server.address();
if (!address || typeof address === 'string') {
reject(new Error('Local probe did not expose a TCP port'));
return;
}
resolveListen(address.port);
});
});
}
function close(server) {
return new Promise((resolveClose, reject) => {
server.close((error) => error ? reject(error) : resolveClose());
});
}
const probe = createHttpServer((request, response) => {
probeHits.push(request.url ?? '/');
response.writeHead(204, { 'Access-Control-Allow-Origin': '*' });
response.end();
});
let browser;
let vite;
try {
const probePort = await listen(probe);
const probeBaseUrl = `http://127.0.0.1:${probePort}`;
vite = await createViteServer({
root,
logLevel: 'error',
server: { host: '127.0.0.1', port: 0 },
plugins: [{
name: 'artifact-csp-probe-page',
configureServer(server) {
server.middlewares.use('/artifact-csp-probe.html', (_request, response) => {
response.setHeader('Content-Type', 'text/html; charset=utf-8');
response.end('<!doctype html><title>Artifact CSP probe</title>');
});
},
}],
});
await vite.listen();
const viteAddress = vite.httpServer?.address();
if (!viteAddress || typeof viteAddress === 'string') throw new Error('Vite probe page did not expose a TCP port');
const controlResponse = await fetch(`${probeBaseUrl}/control`);
if (!controlResponse.ok || probeHits.length !== 1) throw new Error('Local probe control request failed');
probeHits.length = 0;
browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.goto(`http://127.0.0.1:${viteAddress.port}/artifact-csp-probe.html`);
const payloads = [
{ id: 'comment-head', html: `<!-- <head> --><img src="${probeBaseUrl}/probe/comment-head">` },
{ id: 'before-head', html: `<img src="${probeBaseUrl}/probe/before-head"><head><title>late head</title></head>` },
{ id: 'inline-script', html: `<script>location.href=${JSON.stringify(`${probeBaseUrl}/probe/inline-script`)}</script>` },
{ id: 'meta-refresh', html: `<meta http-equiv="refresh" content="0;url=${probeBaseUrl}/probe/meta-refresh">` },
];
for (const payload of payloads) {
await page.evaluate(async ({ artifactId, sourceHtml }) => {
const { prepareHtmlArtifact } = await import('/src/lib/agent-tools.ts');
const artifact = prepareHtmlArtifact(sourceHtml, 'CSP probe', artifactId);
const iframe = document.createElement('iframe');
iframe.dataset.artifactId = artifactId;
iframe.setAttribute('sandbox', '');
iframe.setAttribute('credentialless', '');
await new Promise((resolveLoad) => {
iframe.addEventListener('load', resolveLoad, { once: true });
iframe.srcdoc = artifact.sandboxedSource;
document.body.append(iframe);
});
}, { artifactId: payload.id, sourceHtml: payload.html });
}
await page.evaluate(async () => {
const { prepareHtmlArtifact } = await import('/src/lib/agent-tools.ts');
const artifact = prepareHtmlArtifact(
'<style>#static-card{color:rgb(17, 34, 51);background:rgb(238, 244, 240)}</style><section id="static-card">Static artifact rendered</section>',
'Static render probe',
'static-render',
);
const iframe = document.createElement('iframe');
iframe.dataset.artifactId = artifact.id;
iframe.setAttribute('sandbox', '');
iframe.setAttribute('credentialless', '');
await new Promise((resolveLoad) => {
iframe.addEventListener('load', resolveLoad, { once: true });
iframe.srcdoc = artifact.sandboxedSource;
document.body.append(iframe);
});
});
await new Promise((resolveWait) => setTimeout(resolveWait, 200));
if (probeHits.length > 0) {
throw new Error(`Artifact CSP leaked ${probeHits.length} request(s): ${probeHits.join(', ')}`);
}
const staticCard = page.frameLocator('iframe[data-artifact-id="static-render"]').locator('#static-card');
await staticCard.waitFor();
const staticRender = await staticCard.evaluate((element) => ({
text: element.textContent,
color: getComputedStyle(element).color,
background: getComputedStyle(element).backgroundColor,
}));
if (staticRender.text !== 'Static artifact rendered'
|| staticRender.color !== 'rgb(17, 34, 51)'
|| staticRender.background !== 'rgb(238, 244, 240)') {
throw new Error(`Static HTML/CSS render failed: ${JSON.stringify(staticRender)}`);
}
console.log(`artifact CSP probe passed: ${payloads.length} bypass payloads emitted 0 requests; static HTML/CSS rendered`);
} finally {
await browser?.close();
await vite?.close();
await close(probe);
}