no-danger
no-danger rule
Keywords: React, dangerouslySetInnerHTML, XSS, security, sanitize, ESLint rule, HTML injection, LLM-optimized
Disallow dangerouslySetInnerHTML usage to prevent XSS vulnerabilities. This rule is part of eslint-plugin-react-features and provides LLM-optimized error messages.
Quick Summary
| Aspect | Details |
|---|---|
| Severity | Error (security) |
| Auto-Fix | ❌ No (requires manual review) |
| Category | React / Security |
| CWE | CWE-79 (Cross-site Scripting) |
| ESLint MCP | ✅ Optimized for ESLint MCP integration |
| Best For | All React projects, especially security-critical applications |
Error Message Format
The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:
⚠️ CWE-79 OWASP:A05 CVSS:6.1 | Cross-site Scripting (XSS) detected | MEDIUM [SOC2,PCI-DSS,GDPR,ISO27001]
Fix: Review and apply the recommended fix | https://owasp.org/Top10/A05_2021/Message Components
| Component | Purpose | Example |
|---|---|---|
| Risk Standards | Security benchmarks | CWE-79 OWASP:A05 CVSS:6.1 |
| Issue Description | Specific vulnerability | Cross-site Scripting (XSS) detected |
| Severity & Compliance | Impact assessment | MEDIUM [SOC2,PCI-DSS,GDPR,ISO27001] |
| Fix Instruction | Actionable remediation | Follow the remediation steps below |
| Technical Truth | Official reference | OWASP Top 10 |
Rule Details
Why This Matters
| Issue | Impact | Solution |
|---|---|---|
| 🔒 XSS Attacks | Malicious script execution | Use React elements |
| 💉 HTML Injection | Content manipulation | Sanitize with DOMPurify |
| 🔐 Data Theft | Cookie/session stealing | Avoid raw HTML |
| 🎭 Phishing | UI spoofing | Controlled rendering |
Configuration
This rule has no configuration options. It always reports dangerouslySetInnerHTML usage.
Examples
❌ Incorrect
// Direct HTML injection - XSS risk!
function Comment({ html }) {
return <div dangerouslySetInnerHTML={{ __html: html }} />;
}
// Even with "trusted" content
function Article({ content }) {
return <article dangerouslySetInnerHTML={{ __html: content }} />;
}
// In any element
<span dangerouslySetInnerHTML={{ __html: userInput }} />✅ Correct
<div>Hello World</div>Configuration Examples
Basic Usage
{
rules: {
'react-features/no-danger': 'error'
}
}With Inline Disable (When Necessary)
// When sanitization is properly implemented
// eslint-disable-next-line react-features/no-danger -- HTML sanitized with DOMPurify
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(content) }} />Safe Alternatives
For Markdown Content
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
function MarkdownViewer({ content }) {
return (
<ReactMarkdown remarkPlugins={[remarkGfm]}>
{content}
</ReactMarkdown>
);
}For Rich Text (WYSIWYG)
import { EditorContent, useEditor } from '@tiptap/react';
function RichTextViewer({ content }) {
const editor = useEditor({
content,
editable: false,
});
return <EditorContent editor={editor} />;
}For Sanitized HTML (When Unavoidable)
import DOMPurify from 'dompurify';
import { useMemo } from 'react';
function SanitizedHTML({ html }) {
const sanitizedHtml = useMemo(() => {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
ALLOWED_ATTR: ['href', 'target', 'rel'],
});
}, [html]);
// eslint-disable-next-line react-features/no-danger -- Sanitized with DOMPurify
return <div dangerouslySetInnerHTML={{ __html: sanitizedHtml }} />;
}When to Use eslint-disable
Only disable this rule when:
- ✅ HTML is sanitized with a trusted library (DOMPurify)
- ✅ Content comes from a trusted, controlled source
- ✅ There's no alternative React-based solution
- ✅ A comment explains the justification
// ✅ Acceptable disable with justification
// eslint-disable-next-line react-features/no-danger -- CMS content sanitized server-side
<div dangerouslySetInnerHTML={{ __html: sanitizedCmsContent }} />
// ❌ Never do this
// eslint-disable-next-line react-features/no-danger
<div dangerouslySetInnerHTML={{ __html: userInput }} />Comparison with Alternatives
| Feature | no-danger | eslint-plugin-react | Manual review |
|---|---|---|---|
| XSS Detection | ✅ Yes | ✅ Yes | ⚠️ Manual |
| CWE Reference | ✅ CWE-79 | ❌ No | ❌ No |
| LLM-Optimized | ✅ Yes | ❌ No | ❌ No |
| ESLint MCP | ✅ Optimized | ❌ No | ❌ No |
Related Rules
no-unsanitized-html- General HTML sanitizationno-unescaped-url-parameter- URL parameter XSS
Further Reading
- React dangerouslySetInnerHTML - Official React docs
- OWASP XSS Prevention - XSS prevention guide
- DOMPurify - HTML sanitization library
- CWE-79 - Cross-site Scripting reference
- ESLint MCP Setup - Enable AI assistant integration
Known False Negatives
The following patterns are not detected due to static analysis limitations:
Dynamic Variable References
Why: Static analysis cannot trace values stored in variables or passed through function parameters.
// ❌ NOT DETECTED - Prop from variable
const propValue = computedValue;
<Component prop={propValue} /> // Computation not analyzedMitigation: Implement runtime validation and review code manually. Consider using TypeScript branded types for validated inputs.
Wrapped or Aliased Functions
Why: Custom wrapper functions or aliased methods are not recognized by the rule.
// ❌ NOT DETECTED - Custom wrapper
function myWrapper(data) {
return internalApi(data); // Wrapper not analyzed
}
myWrapper(unsafeInput);Mitigation: Apply this rule's principles to wrapper function implementations. Avoid aliasing security-sensitive functions.
Imported Values
Why: When values come from imports, the rule cannot analyze their origin or construction.
// ❌ NOT DETECTED - Value from import
import { getValue } from './helpers';
processValue(getValue()); // Cross-file not trackedMitigation: Ensure imported values follow the same constraints. Use TypeScript for type safety.