const { expect } = require("chai"); const { marked } = require("marked"); const DOMPurify = require("isomorphic-dompurify"); /** * Helper that mirrors the server-side rendering pipeline in webview.ts: * DOMPurify.sanitize(marked.marked(content, { headerIds: false, mangle: false })) */ function renderAndSanitize(markdown) { const raw = marked(markdown, { headerIds: false, mangle: false }); return DOMPurify.sanitize(raw); } describe("Markdown sanitization", function () { // --------------------------------------------------------------- // Script injection // --------------------------------------------------------------- describe("removes script tags", function () { it("strips inline '); expect(html).to.not.include("' ); expect(html).to.not.include("document.cookie\n\nWorld" ); expect(html).to.not.include("'); expect(html).to.not.include("onerror"); }); it("strips onload handler on img", function () { const html = renderAndSanitize( '' ); expect(html).to.not.include("onload"); }); it("strips onmouseover handler on a tag", function () { const html = renderAndSanitize( 'hover me' ); expect(html).to.not.include("onmouseover"); expect(html).to.include("hover me"); }); it("strips onfocus handler on input", function () { const html = renderAndSanitize(''); expect(html).to.not.include("onfocus"); }); }); // --------------------------------------------------------------- // javascript: URLs // --------------------------------------------------------------- describe("removes javascript: URLs", function () { it("strips javascript: href in anchor", function () { const html = renderAndSanitize( 'click' ); expect(html).to.not.include("javascript:"); }); it("strips javascript: href in markdown link syntax", function () { const html = renderAndSanitize("[click](javascript:alert(1))"); expect(html).to.not.include("javascript:"); }); }); // --------------------------------------------------------------- // iframe / object / embed // --------------------------------------------------------------- describe("removes dangerous elements", function () { it("strips iframe", function () { const html = renderAndSanitize( '' ); expect(html).to.not.include("' ); expect(html).to.not.include("'); expect(html).to.not.include("' ); expect(html).to.not.include("javascript:"); }); }); // --------------------------------------------------------------- // SVG-based attacks // --------------------------------------------------------------- describe("removes SVG-based XSS", function () { it("strips svg with onload", function () { const html = renderAndSanitize(''); expect(html).to.not.include("onload"); }); it("strips svg with embedded script", function () { const html = renderAndSanitize( "" ); expect(html).to.not.include("click' ); expect(html).to.not.include("data:text/html"); }); }); // --------------------------------------------------------------- // style-based attacks // --------------------------------------------------------------- describe("removes style-based attacks", function () { it("strips style tags with expressions", function () { const html = renderAndSanitize( "" ); expect(html).to.not.include("javascript:"); }); }); // --------------------------------------------------------------- // Safe content is preserved // --------------------------------------------------------------- describe("preserves safe markdown content", function () { it("preserves headings", function () { const html = renderAndSanitize("# Heading 1\n## Heading 2"); expect(html).to.include("

"); expect(html).to.include("Heading 1"); expect(html).to.include("

"); }); it("preserves paragraphs", function () { const html = renderAndSanitize("Hello world\n\nSecond paragraph"); expect(html).to.include("

"); expect(html).to.include("Hello world"); }); it("preserves bold and italic", function () { const html = renderAndSanitize("**bold** and *italic*"); expect(html).to.include("bold"); expect(html).to.include("italic"); }); it("preserves links", function () { const html = renderAndSanitize("[example](https://example.com)"); expect(html).to.include("https://example.com"); expect(html).to.include("example"); }); it("preserves images", function () { const html = renderAndSanitize( "![alt](https://example.com/img.png)" ); expect(html).to.include("npm install"); }); it("preserves unordered lists", function () { const html = renderAndSanitize("- item 1\n- item 2\n- item 3"); expect(html).to.include("

    "); expect(html).to.include("
  • "); expect(html).to.include("item 1"); }); it("preserves ordered lists", function () { const html = renderAndSanitize("1. first\n2. second"); expect(html).to.include("
      "); expect(html).to.include("first"); }); it("preserves blockquotes", function () { const html = renderAndSanitize("> This is a quote"); expect(html).to.include("
      "); expect(html).to.include("This is a quote"); }); it("preserves tables", function () { const html = renderAndSanitize("| A | B |\n|---|---|\n| 1 | 2 |"); expect(html).to.include(""); expect(html).to.include("
      "); expect(html).to.include(""); }); it("preserves horizontal rules", function () { const html = renderAndSanitize("---"); expect(html).to.include("alert("xss")\n\n**Bold text**' ); expect(html).to.not.include("Bold text"); }); it("strips event handlers from otherwise-safe tags", function () { const html = renderAndSanitize( 'photo' ); expect(html).to.not.include("onerror"); expect(html).to.include("photo.jpg"); expect(html).to.include('alt="photo"'); }); }); });