import { describe, test, expect } from 'bun:test'; import { escapeHtml, renderWorldHtml } from './world-html.ts'; describe('escapeHtml — XSS safety', () => { test('escapes the 5 critical chars', () => { expect(escapeHtml('')) .toBe('<script>alert(1)</script>'); }); test('escapes ampersand FIRST (or double-escape bug happens)', () => { expect(escapeHtml('<')) .toBe('&lt;'); // the input < becomes &lt; }); test('escapes quotes (attribute context)', () => { expect(escapeHtml('onclick="alert(1)"')) .toBe('onclick="alert(1)"'); expect(escapeHtml("onclick='alert(1)'")) .toBe('onclick='alert(1)''); }); test('passthrough for safe ASCII', () => { expect(escapeHtml('Hello world.')).toBe('Hello world.'); }); test('handles null and undefined', () => { expect(escapeHtml(null)).toBe(''); expect(escapeHtml(undefined)).toBe(''); }); test('handles numbers', () => { expect(escapeHtml(42)).toBe('42'); }); test('preserves Unicode (non-ASCII that\'s not special HTML)', () => { expect(escapeHtml('Héctor García 🎉')).toBe('Héctor García 🎉'); }); test('real Opus prose injection attempt neutralized', () => { // Representative: if Opus generates this in an entity backstory, // the explorer HTML must NOT execute it. The XSS protection works // because ``; const safe = escapeHtml(attack); // The opening `<` must be escaped (this is what neutralizes the tag). expect(safe).not.toContain(' { // Even as text content, escape quotes so it can't break attribute context. const attack = `"javascript:alert(1)//`; expect(escapeHtml(attack)).toBe('"javascript:alert(1)//'); }); }); describe('renderWorldHtml', () => { const samplePage = { slug: 'people/alice-chen', type: 'person' as const, title: 'Alice Chen', compiled_truth: 'Alice Chen is a senior engineer.', timeline: '- **2023-01-15** | Promoted to staff engineer', _facts: { type: 'person', role: 'engineer' }, }; test('renders an HTML document', () => { const html = renderWorldHtml([samplePage]); expect(html).toContain(''); expect(html).toContain(' { const html = renderWorldHtml([samplePage]); // Rail link uses #slug anchor expect(html).toContain('href="#people/alice-chen"'); // Entity card has matching id expect(html).toContain('id="people/alice-chen"'); }); test('escapes all user content (XSS neutralization)', () => { const maliciousPage = { ...samplePage, title: '', compiled_truth: ``, }; const html = renderWorldHtml([maliciousPage]); // Raw