[Detail Bug] Screenshot rendering: XSS when prettifying text/plain API responses
# Detail Bug Report
https://app.detail.dev/org_06887db3-bf54-40ab-976d-46c66ab2b840/bugs/bug_ac4f2a30-ec5c-4f97-99d1-feb74772537e
# Summary
- **Context**: The `packages/screenshot/src/pretty/html.js` module generates HTML to display API responses (JSON/text) with syntax highlighting for screenshot capture.
- **Bug**: User-controlled content from external API responses is directly embedded into HTML templates without HTML escaping, enabling Cross-Site Scripting (XSS) attacks.
- **Actual vs. expected**: Content is inserted raw into the HTML using template literals, allowing execution of malicious JavaScript; it should be HTML-escaped before insertion.
- **Impact**: Attackers can execute arbitrary JavaScript when users screenshot malicious APIs, potentially stealing sensitive data from the browser automation context or interfering with the screenshot process.
# Code with Bug
In `packages/screenshot/src/pretty/html.js`:
```javascript
module.exports = (payload, { contentType, prism, theme }) => {
const css = `${resetCSS}\n${theme}`
const lang = language(contentType)
const code = content(payload, contentType) // No HTML escaping
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
${css}
</head>
<body id="screenshot">
<pre><code class="${lang}">${code}</code></pre> // <-- BUG 🔴 unescaped user content injected into HTML
<script>${prism}</script>
</body>
</html>`
}
```
# Explanation
`code` can contain attacker-controlled API response text (notably for `contentType: 'text'`). It is interpolated directly into an HTML string passed to the browser, so any embedded HTML (e.g., `<script>`, `<img onerror=...>`, SVG) is parsed and can execute immediately.
A partial defense exists elsewhere (`if (isHtmlContent(content)) return`), but `is-html-content` only detects content that *starts with* `<` (regex `/^\s*</`). Payloads like `"OK\n<script>...</script>"` bypass the check.
Prism.js does not prevent exploitation because scripts/event handlers can run during initial HTML parsing before Prism sanitizes/highlights the code.
## Codebase Inconsistency
`packages/screenshot/src/pretty/index.js` attempts to avoid HTML injection via:
```javascript
if (isHtmlContent(content)) return
```
…but that check does not cover the common bypass where the response begins with non-`<` text.
# Exploit Scenario
An attacker controls an API endpoint that returns `text/plain` content containing a script tag or an HTML element with an event handler after some benign text (so it doesn’t start with `<`). When a user screenshots/prettifies that API response, the generated HTML executes the attacker’s JavaScript in the automation browser context.
# Recommended Fix
HTML-escape `content(payload, contentType)` before inserting it into the template (or refactor to set code via `textContent` rather than string interpolation). Example escape approach:
```javascript
const code = escapeHtml(content(payload, contentType))
```
# History
This bug was introduced in commit 18ab1b4b. The commit added support for prettifying text/plain API responses in addition to JSON responses, and the refactor began interpolating raw strings into HTML without escaping.
关闭于 2026-05-17 0 条评论