CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | /**
* Security regression — stored XSS in markdown rendering.
*
* `renderMarkdown()` feeds untrusted markdown (READMEs, issues, PR comments,
* wikis, discussions, releases) into ~30 `dangerouslySetInnerHTML` sinks.
* These tests lock in that the final `sanitize-html` stage strips executable
* payloads while preserving normal markdown + our syntax-highlighted code
* blocks. Do not weaken these assertions without a security review.
*/
import { describe, it, expect } from "bun:test";
import { renderMarkdown } from "../lib/markdown";
describe("renderMarkdown — XSS sanitization", () => {
it("strips raw <script> tags and their payload", () => {
const out = renderMarkdown("<script>alert(1)</script>");
expect(out).not.toContain("<script");
expect(out).not.toContain("alert(1)");
});
it("strips img onerror handlers", () => {
const out = renderMarkdown("<img src=x onerror=alert(1)>");
expect(out).not.toContain("onerror");
expect(out.toLowerCase()).not.toContain("onerror=");
});
it("strips svg onload handlers", () => {
const out = renderMarkdown("<svg onload=alert(1)></svg>");
expect(out.toLowerCase()).not.toContain("onload");
expect(out).not.toContain("<svg");
});
it("strips iframes with javascript: src", () => {
const out = renderMarkdown('<iframe src="javascript:alert(1)"></iframe>');
expect(out).not.toContain("<iframe");
expect(out.toLowerCase()).not.toContain("javascript:");
});
it("neutralizes javascript: links in markdown link syntax", () => {
const out = renderMarkdown("[x](javascript:alert(1))");
// No executable javascript: URL should survive in an href.
expect(/href=["']?\s*javascript:/i.test(out)).toBe(false);
expect(out.toLowerCase()).not.toContain("javascript:alert");
});
it("strips inline event handlers on otherwise-allowed tags", () => {
const out = renderMarkdown('<a href="https://example.com" onclick="alert(1)">x</a>');
expect(out.toLowerCase()).not.toContain("onclick");
});
it("drops <style>, <object>, <embed>, <form> tags", () => {
const out = renderMarkdown(
"<style>body{display:none}</style><object data=x></object><embed src=x><form></form>",
);
expect(out).not.toContain("<style");
expect(out).not.toContain("<object");
expect(out).not.toContain("<embed");
expect(out).not.toContain("<form");
});
it("disallows data: URIs on images", () => {
const out = renderMarkdown("</script>)");
expect(out.toLowerCase()).not.toContain("data:text/html");
});
});
describe("renderMarkdown — normal rendering preserved", () => {
it("renders headings", () => {
const out = renderMarkdown("# Hello");
expect(out).toContain("<h1");
expect(out).toContain("Hello");
});
it("renders bold text", () => {
const out = renderMarkdown("**bold**");
expect(out).toContain("<strong>bold</strong>");
});
it("renders safe http links", () => {
const out = renderMarkdown("[site](https://example.com)");
expect(out).toContain('href="https://example.com"');
});
it("renders images from http(s) sources", () => {
const out = renderMarkdown("");
expect(out).toContain('src="https://example.com/a.png"');
expect(out).toContain('alt="alt"');
});
it("preserves syntax-highlighted fenced code blocks", () => {
const out = renderMarkdown("```js\nconst x = 1;\n```");
expect(out).toContain("<pre>");
expect(out).toContain("<code");
// language class must survive so highlight.js CSS applies.
expect(out).toContain('class="language-js"');
// highlight.js emits hljs-* spans; the class attribute must survive.
expect(out).toContain("hljs-");
expect(out).toContain('class="hljs-');
});
it("renders inline code", () => {
const out = renderMarkdown("use `bun test` here");
expect(out).toContain("<code>bun test</code>");
});
});
|