/** * Phase 2 adversarial — UI-side defenses. Confirms that Svelte's default * text interpolation escapes everywhere user-supplied content surfaces. * * This is a belt-and-braces test: Svelte 5 escapes `{value}` by default, * so failures here would mean someone reached for `{@html}` somewhere * they shouldn't. */ import { test, expect } from '../../fixtures/test'; test.describe('Adversarial — UI render escape', () => { test('display name with BOLD`; const r = await api.join(payload); await page.goto('/'); await page.evaluate(({ j, u, n, p }) => { localStorage.setItem('eventsnap_jwt', j); localStorage.setItem('eventsnap_user_id', u); localStorage.setItem('eventsnap_display_name', n); localStorage.setItem('eventsnap_pin', p); }, { j: r.jwt, u: r.user_id, n: payload, p: r.pin }); page.on('dialog', (d) => { throw new Error(`Dialog fired: ${d.message()}`); }); await page.goto('/account'); await page.waitForLoadState('domcontentloaded'); const fired = await page.evaluate(() => (window as any).__xssFired === true); expect(fired).toBe(false); // tag inside the name should also not render as bold — Svelte escapes the entire string. const boldCount = await page.locator('b:has-text("BOLD")').count(); expect(boldCount).toBe(0); }); test('rendering of a known SQL-injection-shaped name does not break the page', async ({ page, api }) => { const payload = `'); DROP TABLE users; --`; const r = await api.join(payload); await page.goto('/'); await page.evaluate(({ j, u, n, p }) => { localStorage.setItem('eventsnap_jwt', j); localStorage.setItem('eventsnap_user_id', u); localStorage.setItem('eventsnap_display_name', n); localStorage.setItem('eventsnap_pin', p); }, { j: r.jwt, u: r.user_id, n: payload, p: r.pin }); await page.goto('/account'); // Page renders. await expect(page.getByRole('link', { name: 'Galerie' })).toBeVisible(); }); });