Summary
SchemaDisplayPath in schema-display.tsx renders caller-supplied content through dangerouslySetInnerHTML without escaping, so any consumer that displays a schema originating outside the app is exposed to script injection.
Where
schema-display.tsx (~line 110):
const highlightedPath = path.replaceAll(
/\{([^}]+)\}/g,
'<span class="text-blue-600 dark:text-blue-400">{$1}</span>'
);
return (
<span
className={cn("font-mono text-sm", className)}
dangerouslySetInnerHTML={{ __html: children ?? highlightedPath }}
{...props}
/>
);
Both operands of the ?? are unsafe:
children is passed straight into __html with no sanitisation at all.
highlightedPath interpolates path into an HTML string via regex, so any markup already in path survives into the DOM.
Why it matters
The natural use for this component is rendering an API or tool schema, and those routinely come from somewhere other than the app itself — an MCP server's tool definitions being the obvious case in an AI SDK context. A schema path is not a trust boundary anyone thinks about, which is what makes this easy to hit by accident.
Suggested fix
No dangerouslySetInnerHTML is needed here — the highlighting can be expressed as React nodes, which escapes by construction:
const parts = path.split(/(\{[^}]+\})/g);
return (
<span className={cn("font-mono text-sm", className)} {...props}>
{children ?? parts.map((part, i) =>
/^\{[^}]+\}$/.test(part) ? (
<span key={i} className="text-blue-600 dark:text-blue-400">{part}</span>
) : (
part
)
)}
</span>
);
That also lets children be a normal React node rather than an HTML string, which is likely what callers expect.
Version
Found in ai-elements@1.9.0, installed via the shadcn registry.
Summary
SchemaDisplayPathinschema-display.tsxrenders caller-supplied content throughdangerouslySetInnerHTMLwithout escaping, so any consumer that displays a schema originating outside the app is exposed to script injection.Where
schema-display.tsx(~line 110):Both operands of the
??are unsafe:childrenis passed straight into__htmlwith no sanitisation at all.highlightedPathinterpolatespathinto an HTML string via regex, so any markup already inpathsurvives into the DOM.Why it matters
The natural use for this component is rendering an API or tool schema, and those routinely come from somewhere other than the app itself — an MCP server's tool definitions being the obvious case in an AI SDK context. A schema path is not a trust boundary anyone thinks about, which is what makes this easy to hit by accident.
Suggested fix
No
dangerouslySetInnerHTMLis needed here — the highlighting can be expressed as React nodes, which escapes by construction:That also lets
childrenbe a normal React node rather than an HTML string, which is likely what callers expect.Version
Found in
ai-elements@1.9.0, installed via the shadcn registry.