123import { describe, expect, it } from "vitest";4import { toHtml } from "./NoteEditor";56describe("toHtml sanitization (M15/THREAT-006)", () => {7 8 it("escapes plain text ampersand", () => {9 expect(toHtml("a & b")).toBe("a & b");10 });1112 it("converts newlines to <br> in plain text", () => {13 expect(toHtml("line1\nline2")).toBe("line1<br>line2");14 });1516 17 it("strips iframe", () => {18 expect(toHtml('<iframe src="x">payload</iframe>')).not.toContain("iframe");19 });2021 it("strips base", () => {22 expect(toHtml('<base href="//evil">')).not.toContain("base");23 });2425 it("strips meta refresh", () => {26 expect(toHtml('<meta http-equiv="refresh" content="0;url=//evil">')).not.toContain("meta");27 });2829 it("strips form", () => {30 expect(toHtml('<form action="x"><input name="y"></form>')).not.toContain("form");31 });3233 it("strips script", () => {34 expect(toHtml('<script>alert(1)</script>')).not.toContain("script");35 });3637 it("strips style", () => {38 expect(toHtml('<style>body{display:none}</style>')).not.toContain("style");39 });4041 it("strips object/embed", () => {42 expect(toHtml('<object data="x"></object>')).not.toContain("object");43 expect(toHtml('<embed src="x">')).not.toContain("embed");44 });4546 it("strips all attributes from allowed tags", () => {47 const result = toHtml('<b onclick="alert(1)" id="x" style="color:red">bold</b>');48 expect(result).toContain("<b>bold</b>");49 expect(result).not.toContain("onclick");50 expect(result).not.toContain("style");51 expect(result).not.toContain("id=");52 });5354 it("strips href from a tags (a is not in allow-list)", () => {55 const result = toHtml('<a href="//evil.com">click</a>');56 expect(result).not.toContain("href");57 expect(result).not.toContain("<a");58 });5960 61 it("preserves bold", () => {62 expect(toHtml("<b>hi</b>")).toContain("<b>hi</b>");63 });6465 it("preserves italic, underline, strikethrough", () => {66 expect(toHtml("<i>x</i><u>y</u><s>z</s>")).toContain("<i>x</i><u>y</u><s>z</s>");67 });6869 it("preserves strong and em", () => {70 expect(toHtml("<strong>x</strong><em>y</em>")).toContain("<strong>x</strong><em>y</em>");71 });7273 it("preserves headings h2 and h3", () => {74 expect(toHtml("<h2>x</h2><h3>y</h3>")).toContain("<h2>x</h2><h3>y</h3>");75 });7677 it("preserves lists", () => {78 const result = toHtml("<ul><li>a</li><li>b</li></ul>");79 expect(result).toContain("<ul>");80 expect(result).toContain("<li>a</li>");81 });8283 it("preserves br, hr, p", () => {84 expect(toHtml("<p>x<br>y</p><hr>")).toContain("<p>");85 expect(toHtml("<p>x<br>y</p><hr>")).toContain("<br>");86 expect(toHtml("<p>x<br>y</p><hr>")).toContain("<hr>");87 });8889 90 it("strips script nested inside allowed tag", () => {91 expect(toHtml("<b><script>alert(1)</script></b>")).not.toContain("script");92 });9394 it("strips javascript: URL by removing the href attribute from a-tag (a not allowed)", () => {95 const result = toHtml('<a href="javascript:alert(1)">x</a>');96 expect(result).not.toContain("javascript:");97 expect(result).not.toContain("href");98 });99100 it("strips img with onerror", () => {101 const result = toHtml('<img src="x" onerror="alert(1)">');102 expect(result).not.toContain("img");103 expect(result).not.toContain("onerror");104 });105106 it("strips svg", () => {107 expect(toHtml('<svg><script>alert(1)</script></svg>')).not.toContain("svg");108 });109});