import { describe, it, expect } from 'vitest' import { sanitizeHtml, sanitizeText } from '../../../src/lib/sanitize.js' describe('sanitize', () => { describe('sanitizeHtml', () => { it('returns empty string for empty input', () => { expect(sanitizeHtml('')).toBe('') }) it('preserves valid markdown-rendered HTML tags', () => { const input = '
Hello bold and italic
' + 'A quote' + '
code block' +
'| Col |
|---|
| Cell |
') expect(result).toContain('') expect(result).toContain('
') expect(result).toContain('
- ') expect(result).toContain('
') expect(result).toContain('') expect(result).toContain('') expect(result).toContain('
') expect(result).toContain('
') expect(result).toContain('') expect(result).toContain('') expect(result).toContain('
') expect(result).toContain('
') }) it('preserves allowed attributes on links', () => { const input = 'Link' const result = sanitizeHtml(input) expect(result).toContain('href="https://example.com"') expect(result).toContain('rel="noopener noreferrer"') }) it('preserves img tags with src and alt', () => { const input = '' const result = sanitizeHtml(input) expect(result).toContain('src="https://example.com/img.png"') expect(result).toContain('alt="Photo"') }) it('strips script tags', () => { const input = '
Hello
' const result = sanitizeHtml(input) expect(result).not.toContain('' const result = sanitizeHtml(input) expect(result).not.toContain('' const result = sanitizeText(input) expect(result).not.toContain('<') expect(result).not.toContain('>') expect(result).toContain('Bold') expect(result).not.toContain('evil') }) it('preserves plain text', () => { const input = 'How to configure PostgreSQL?' expect(sanitizeText(input)).toBe('How to configure PostgreSQL?') }) it('applies NFC normalization', () => { const nfd = 'caf\u0065\u0301' const nfc = 'caf\u00E9' expect(sanitizeText(nfd)).toBe(nfc) }) it('strips bidirectional override characters', () => { const input = '\u202AHello\u202E World\u200F' const result = sanitizeText(input) expect(result).toBe('Hello World') }) it('strips HTML from titles with injection attempts', () => { const input = 'TopicTitle' const result = sanitizeText(input) expect(result).not.toContain('
{ const large = 'A'.repeat(100_000) const result = sanitizeText(large) expect(result.length).toBe(100_000) }) it('strips nested HTML tags', () => { const input = '
' const result = sanitizeText(input) expect(result).not.toContain('<') expect(result).toContain('Nested') }) it('handles homoglyph-style text (NFC normalization of composed chars)', () => { // Latin Small Letter A with Ring Above: U+0061 + U+030A -> U+00E5 const decomposed = '\u0061\u030A' const composed = '\u00E5' expect(sanitizeText(decomposed)).toBe(composed) }) }) })Nested