import { test, describe, expect } from "vitest"; import { render_inline_markdown, escape_html, INLINE_CODE_RE, LINK_RE, BOLD_ASTERISK_RE, BOLD_UNDERSCORE_RE, ITALIC_ASTERISK_RE, ITALIC_UNDERSCORE_RE, PROTOCOL_RE } from "./inline-markdown"; describe("escape_html", () => { test("escapes ampersands", () => { expect(escape_html("a & b")).toBe("a & b"); }); test("escapes angle brackets", () => { expect(escape_html("")).toBe( "<script>alert(1)</script>" ); }); test("escapes HTML inside markdown syntax", () => { expect(render_inline_markdown("**bold**")).toBe( "<b>bold</b>" ); }); }); describe("safe links", () => { test("renders http links", () => { expect(render_inline_markdown("[click](http://example.com)")).toBe( 'click' ); }); test("renders https links", () => { expect(render_inline_markdown("[click](https://example.com)")).toBe( 'click' ); }); test("renders bare domain links (no protocol)", () => { expect(render_inline_markdown("[Google](google.com)")).toBe( 'Google' ); }); test("renders www links (no protocol)", () => { expect(render_inline_markdown("[Google](www.google.com)")).toBe( 'Google' ); }); test("renders relative path links", () => { expect(render_inline_markdown("[docs](/path/to/docs)")).toBe( 'docs' ); }); test("trims whitespace from URLs", () => { expect(render_inline_markdown("[click]( https://example.com )")).toBe( 'click' ); }); }); describe("XSS prevention", () => { test("blocks javascript: protocol links", () => { const result = render_inline_markdown( "[xss](javascript:alert(document.cookie))" ); // No tag should be produced — the XSS vector is neutralised. // Trailing ")" is left over because the link regex stops at the first ")". expect(result).not.toContain(" { expect(render_inline_markdown("[xss](javascript:void)")).toBe("xss"); }); test("blocks javascript: with leading spaces", () => { const result = render_inline_markdown("[xss]( javascript:alert(1) )"); expect(result).not.toContain(" { expect(render_inline_markdown("[xss](vbscript:msgbox)")).toBe("xss"); }); test("blocks data: protocol links", () => { expect( render_inline_markdown( "[xss](data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==)" ) ).toBe("xss"); }); test("blocks data: with nested HTML", () => { const result = render_inline_markdown( "[xss](data:text/html,)" ); expect(result).not.toContain(" { const input = "[bad](javascript:void) hello [good](https://example.com)"; const result = render_inline_markdown(input); expect(result).toContain("bad"); expect(result).not.toContain("javascript:"); expect(result).toContain( 'good' ); }); }); describe("combined syntax", () => { test("renders bold inside a sentence", () => { expect(render_inline_markdown("This is **important** info")).toBe( "This is important info" ); }); test("renders code and bold together", () => { expect(render_inline_markdown("Use `func()` for **speed**")).toBe( "Use func() for speed" ); }); test("renders link with surrounding text", () => { expect( render_inline_markdown("See [docs](https://docs.io) for more") ).toBe( 'See docs for more' ); }); test("empty string returns empty string", () => { expect(render_inline_markdown("")).toBe(""); }); test("plain text passes through unchanged", () => { expect(render_inline_markdown("hello world")).toBe("hello world"); }); }); });