const { chromium } = require('playwright');
const { readFileSync } = require('node:fs');
const assert = require('node:assert/strict');
const { extractThemeBootstrap } = require('./helpers/theme_bootstrap.cjs');
(async () => {
const origin = process.env.ODYSSEUS_TEST_STATIC_ORIGIN;
const browser = await chromium.launch({ headless: true });
try {
const page = await browser.newPage();
const errors = [];
const svgRequests = [];
const svgFailures = [];
page.on('pageerror', error => errors.push(error.message));
page.on('request', request => { if (request.url().includes('example.invalid/security-probe')) svgRequests.push(request.url()); });
page.on('requestfailed', request => { if (request.url().includes('example.invalid/security-probe')) svgFailures.push(request.failure().errorText); });
await page.route('**/security-harness', route => route.fulfill({ contentType: 'text/html', body:
'
' }));
const secrets = { access_token: 'ACCESS-SENTINEL', refresh_token: 'REFRESH-SENTINEL', api_key: 'KEY-SENTINEL', password: 'PASSWORD-SENTINEL' };
const endpoints = ['a', 'b'].map(id => ({ id: 'endpoint-' + id, provider_auth_id: 'session-' + id,
provider: 'chatgpt-subscription', name: 'ChatGPT · LABEL-SENTINEL', category: 'api',
base_url: 'https://chatgpt.com/backend-api/codex', is_enabled: true, online: true, models: [], ...secrets }));
await page.route('**/api/**', route => {
const url = new URL(route.request().url());
if (url.pathname === '/api/model-endpoints') return route.fulfill({ json: endpoints });
if (url.pathname.endsWith('/usage')) return route.fulfill({ json: { available: true, usage: { limits: [] }, ...secrets } });
if (url.pathname.includes('/device/')) return route.fulfill({ status: 400, json: { detail: 'Fixture declines device flow' } });
return route.fulfill({ json: { fonts: {}, value: null, tools: [], models: [] } });
});
// Expose the real internal loader only in this served test copy.
await page.route('**/static/js/admin-codeql-harness.js', route => route.fulfill({ contentType: 'application/javascript',
body: readFileSync('static/js/admin.js', 'utf8') + '\nexport { loadEndpoints };\n' }));
await page.goto(origin + '/security-harness');
const rendered = await page.evaluate(async () => {
const { addMessage } = await import('/static/js/chatRenderer.js');
const markdown = await import('/static/js/markdown.js');
window.executed = 0;
const payloads = [
'
',
'',
'',
'\"\'>
& ',
'Nested
link ',
'
**Valid** instruction',
];
const results = [];
for (const payload of payloads) {
const message = addMessage('user', 'In the document, edit this specific text (line 1):\n```\nselected\n```\n\nInstruction: ' + payload);
if (!message) throw new Error('addMessage failed');
const body = message.querySelector('.body');
results.push({ tag: body.querySelector('.doc-edit-tag')?.dataset.docEditRef,
unsafe: body.querySelectorAll('script, svg[onload], [onerror], [onload], a[href^="javascript:"]').length });
const direct = document.createElement('div');
direct.innerHTML = markdown.processWithThinking(payload);
results.push({ unsafe: direct.querySelectorAll('script, [onerror], [onload], a[href^="javascript:"]').length });
message.remove();
}
const valid = addMessage('user', 'In the document, edit this specific text (lines 1–2):\n```\nselected\n```\n\nInstruction: **Keep bold** and `code`');
const titles = [
{ source: '', title: 'Nested bold & text' },
{ source: '', title: '\" onload=\"parent.executed++ \n```');
document.body.appendChild(svgHost);
const directValid = document.createElement('div');
directValid.innerHTML = markdown.processWithThinking('**Keep bold** and `code`');
return { results, titles, instruction: valid.querySelector('.body').textContent,
bold: directValid.querySelector('strong')?.textContent, code: directValid.querySelector('code')?.textContent };
});
assert(rendered.results.every(result => result.unsafe === 0));
assert(rendered.results.filter((result, index) => index % 2 === 0).every(result => result.tag === 'line 1'));
assert(rendered.instruction.includes('Keep bold and code'));
assert.equal(rendered.bold, 'Keep bold');
assert.equal(rendered.code, 'code');
for (const title of rendered.titles) {
assert.equal(title.actual, title.expected);
assert.equal(title.sandbox, '');
assert.equal(title.referrer, 'no-referrer');
assert.equal(title.onload, false);
assert.equal(title.csp, "default-src 'none'; img-src 'none'; media-src 'none'; font-src 'none'; style-src 'unsafe-inline'");
}
const menus = await page.evaluate(async () => {
const { _showReaderMoreMenu } = await import('/static/js/emailLibrary/menus.js');
const { _safeRenderEmailBody } = await import('/static/js/emailLibrary/bodyRender.js');
const reader = document.createElement('div');
const anchor = document.createElement('button');
document.body.append(reader, anchor);
const labels = ['
', '