Phase: 8 Sequence: 001 Slug: xss-in-innerhtml Verdict: VALID Rationale: Stored XSS confirmed via unsanitized markdown-to-HTML conversion with innerHTML directive; payload creation requires admin access but execution is automatically triggered by any blog viewer Severity-Original: high Severity: high PoC-Status: pending Pre-FP-Flag: none Debate: piolium/attack-surface/balanced-chamber-summary.md ## Summary The blog post rendering in `web/src/routes/blog/[slug].tsx` uses a custom `contentToHtml()` function that performs raw string concatenation without HTML escaping, combined with SolidJS's `innerHTML` directive that bypasses framework-level escaping. This creates a stored XSS vulnerability: any blog post containing HTML/JavaScript tags will execute in the context of every viewer's browser. ## Location - `web/src/routes/blog/[slug].tsx` lines 14–46 (contentToHtml function) - `web/src/routes/blog/[slug].tsx` line 121 (innerHTML binding) ## Attacker Control An admin user (or attacker with admin access via session theft, SQL injection, or credential compromise) can create a blog post containing malicious HTML/JavaScript. The payload is stored in the `blogPosts.content` column and rendered on every page view. ## Trust Boundary Crossed Server-side data (blog post content) → Browser execution context (innerHTML). This crosses the server-to-client trust boundary, allowing server-stored data to execute as JavaScript in the victim's browser. ## Impact Stored XSS affecting all blog post viewers. Attackers can: - Steal session cookies and JWT tokens - Perform actions on behalf of victims (account takeover) - Redirect users to phishing pages - Deface blog content ## Evidence ```typescript // contentToHtml() — no HTML escaping function contentToHtml(markdown: string): string { const lines = markdown.split("\n"); let html = ""; for (const line of lines) { if (line.startsWith("## ")) { html += `
${line}
`; // No escaping } } return html; } // Line 121: innerHTML={contentHtml()} — bypasses SolidJS escaping ``` ## Reproduction Steps 1. Admin creates blog post with content: `