Describe the bug
In code/core/src/manager/components/sidebar/Brand.tsx (lines 48 and 50), the theme.brand.title value is rendered directly using dangerouslySetInnerHTML without sanitization:
// Brand.tsx:41-51
// When image is explicitly set to null, enable custom HTML support
if (image === null) {
if (title === null) {
return null;
}
if (!url) {
return <div dangerouslySetInnerHTML={{ __html: title }} />;
}
return <LogoLink href={url} target={targetValue} dangerouslySetInnerHTML={{ __html: title }} />;
}
While this is partially intentional (the comment says "enable custom HTML support"), it allows arbitrary HTML — including <script> tags — to be injected into the Storybook Manager UI through the theme configuration.
Attack scenario: A malicious or compromised npm package (e.g., a Storybook addon or a transitive dependency) that modifies the Storybook theme could inject arbitrary JavaScript into the Manager UI via theme.brand.title. This is a realistic threat in supply-chain attack scenarios.
Reproduction link
https://stackblitz.com/edit/storybook-new
Reproduction steps
- In
.storybook/manager.ts, configure the theme with a script tag in brand.title:
import { addons } from 'storybook/manager-api';
import { create } from 'storybook/theming';
addons.setConfig({
theme: create({
base: 'light',
brand: {
image: null,
title: '<img src=x onerror="alert(\'XSS via theme.brand.title\')">',
},
}),
});
- Start Storybook.
- Observe that the injected script executes in the Manager UI.
Expected behavior: theme.brand.title should be treated as plain text and escaped before rendering, or the documentation should clearly warn about the XSS risk of using image: null with HTML content in title.
System
Storybook version : 10.4.0-alpha.15
Affected file : code/core/src/manager/components/sidebar/Brand.tsx (lines 48, 50)
Additional context
Suggested fix: Use textContent for plain text rendering by default, and opt-in to raw HTML only via an explicitly named prop (e.g., titleHTML) with a clear security warning in the docs.
// Safe default
return <div>{title}</div>;
// Opt-in for raw HTML with explicit prop
return <div dangerouslySetInnerHTML={{ __html: titleHTML }} />;
Alternatively, sanitize with a library such as DOMPurify before passing to dangerouslySetInnerHTML.
Describe the bug
In
code/core/src/manager/components/sidebar/Brand.tsx(lines 48 and 50), thetheme.brand.titlevalue is rendered directly usingdangerouslySetInnerHTMLwithout sanitization:While this is partially intentional (the comment says "enable custom HTML support"), it allows arbitrary HTML — including
<script>tags — to be injected into the Storybook Manager UI through the theme configuration.Attack scenario: A malicious or compromised npm package (e.g., a Storybook addon or a transitive dependency) that modifies the Storybook theme could inject arbitrary JavaScript into the Manager UI via
theme.brand.title. This is a realistic threat in supply-chain attack scenarios.Reproduction link
https://stackblitz.com/edit/storybook-new
Reproduction steps
.storybook/manager.ts, configure the theme with a script tag inbrand.title:Expected behavior:
theme.brand.titleshould be treated as plain text and escaped before rendering, or the documentation should clearly warn about the XSS risk of usingimage: nullwith HTML content intitle.System
Additional context
Suggested fix: Use
textContentfor plain text rendering by default, and opt-in to raw HTML only via an explicitly named prop (e.g.,titleHTML) with a clear security warning in the docs.Alternatively, sanitize with a library such as DOMPurify before passing to
dangerouslySetInnerHTML.