Antd injection risk almost never comes from a bug in the Ant Design library itself; it comes from your application handing untrusted data to a component in a way that ends up rendered as HTML. Ant Design (the antd package) is one of the most widely used React component libraries, and like React itself it escapes text by default. So when cross-site scripting shows up in an antd app, the cause is usually a specific pattern you introduced, not the framework. This post covers where antd injection actually lives and how to shut it down.
React and antd give you a lot of protection for free. The trouble starts at the seams where developers deliberately bypass that protection, often without realizing the risk.
React escapes by default, until you opt out
By default, when you render a value in JSX, React escapes it. <Typography.Text>{userInput}</Typography.Text> renders <script> as literal text, not as a tag. This is true across antd components, and it means the common case is safe.
Injection becomes possible the moment you step outside that default. In React the primary escape hatch is dangerouslySetInnerHTML, and antd components that render rich content can expose the same danger when you pass them raw HTML. If any of that HTML is influenced by user input and is not sanitized, you have a cross-site scripting vulnerability.
// Dangerous: user-controlled HTML rendered without sanitizing
<div dangerouslySetInnerHTML={{ __html: userSuppliedHtml }} />
The prop is named dangerous on purpose. Treat every use of it as a security decision, not a convenience.
Where antd injection tends to appear
A few patterns recur in real Ant Design codebases:
Rich content props. Components that accept content which may be HTML, tooltips, popovers, notification and message bodies, table cell renderers, are safe when you pass plain strings and React escapes them, and unsafe when you pass raw HTML built from user input.
Rendering markdown or WYSIWYG output. A common feature is letting users write formatted content, then rendering the resulting HTML. If you render that HTML directly, any script an attacker embeds runs in the next viewer's browser. This is stored XSS, and it is one of the most damaging kinds because it hits every user who loads the page.
URL and redirect handling. A documented real-world example is CVE-2019-18350, an XSS in the related ant-design-pro project where the login flow's redirect parameter allowed JavaScript execution. The lesson generalizes: any value that becomes a URL, an href, or a redirect target must be validated, because javascript: URLs are an injection vector even without dangerouslySetInnerHTML.
Custom cell and column renderers in tables. When you write a render function for an antd Table column that constructs markup from row data, you are back in manual-escaping territory if any of that data is user-controlled.
How to prevent it
The defenses are concrete and layered.
Prefer plain text rendering. Wherever you can, pass strings and let React escape them. The vast majority of "rich" displays do not actually need raw HTML. This one habit removes most antd injection risk outright.
Sanitize any HTML you must render. When you genuinely need to render user-influenced HTML, run it through a sanitizer with an allow-list before it touches dangerouslySetInnerHTML. DOMPurify is the standard choice:
import DOMPurify from 'dompurify';
function SafeHtml({ html }) {
const clean = DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'ul', 'li'],
ALLOWED_ATTR: ['href'],
});
return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}
Validate URLs. Before using any user-provided value as an href or redirect, confirm it uses an allowed scheme (http, https, mailto) and reject javascript: and data: URLs. For internal redirects, match against an allow-list of known paths rather than reflecting whatever came in.
Set a Content Security Policy. A strict CSP that disallows inline scripts is a strong backstop. Even if an injection slips through, a good CSP can stop the injected script from executing, turning a critical bug into a contained one.
Keep antd itself current
Everything above concerns your own code, which is where the risk overwhelmingly lives. But the library and its ecosystem do occasionally ship security fixes, so the second half of the job is keeping antd and its dependencies patched. Ant Design pulls in a substantial dependency tree, and a vulnerability can hide in a package you never reference directly.
That is a job for automated scanning rather than manual review. A software composition analysis tool resolves the full tree behind antd and flags any dependency with a known advisory, including transitive ones. An SCA scanner such as Safeguard can surface a vulnerable transitive package before it ships. For the front-end side of things, pair dependency scanning with the code-level habits above; neither substitutes for the other. Our Snyk comparison covers how dependency coverage stacks up if you are evaluating tools.
A quick antd injection checklist
Before shipping an Ant Design feature that handles user content, verify:
- User input renders as escaped text unless there is a deliberate, documented reason otherwise.
- Every
dangerouslySetInnerHTMLis fed sanitized HTML from an allow-list sanitizer. - Any value that becomes a URL,
href, or redirect is scheme-validated. - A Content Security Policy blocks inline script as a backstop.
antdand its dependency tree are covered by automated advisory scanning.
FAQ
Is Ant Design itself vulnerable to XSS?
Ant Design inherits React's default text escaping, so out of the box it is not the source of XSS. Injection typically comes from application code that passes user-controlled HTML to dangerouslySetInnerHTML or uses unvalidated values as URLs. Keep the library patched, but focus your defenses on your own rendering code.
What is the most common antd injection mistake?
Rendering user-influenced HTML directly, most often from a markdown or rich-text editor, without sanitizing it first. This creates stored XSS that affects every user who views the content. Always sanitize with an allow-list before using dangerouslySetInnerHTML.
Do I need to sanitize plain strings in antd components?
No. When you pass a plain string to an antd component, React escapes it automatically, so <script> renders as harmless text. Sanitization is only needed when you deliberately render raw HTML. Preferring plain strings is itself a strong defense.
Does a Content Security Policy replace sanitizing input?
No, it complements it. Sanitization stops the injection from being stored or reflected; a strict CSP that blocks inline scripts stops an injected payload from executing if something slips through. Use both, since defense in depth means one failure does not become a breach.