Interlace ESLint
ESLint Interlace
Browser SecurityRules

no-innerhtml

Detects dangerous innerHTML/outerHTML assignments that can lead to Cross-Site Scripting (XSS). This rule is part of [`eslint-plugin-frontend-security`](https://

Keywords: XSS, innerHTML, outerHTML, CWE-79, security, DOM manipulation, sanitization, DOMPurify

Detects dangerous innerHTML/outerHTML assignments that can lead to Cross-Site Scripting (XSS). This rule is part of eslint-plugin-frontend-security.

⚠️ This rule errors by default in the recommended config.

Quick Summary

AspectDetails
CWE ReferenceCWE-79 (Cross-site Scripting)
Severity🔴 Critical
Auto-Fix✅ Yes (suggests DOMPurify)
CategorySecurity
Best ForFrontend apps, React/Vue/Angular, DOM manipulation

Vulnerability and Risk

Vulnerability: Assigning unsanitized user input to innerHTML or outerHTML allows attackers to inject malicious scripts.

Risk: XSS attacks can:

  • Steal session cookies and authentication tokens
  • Perform actions as the victim user
  • Redirect to phishing sites
  • Install keyloggers

How XSS via innerHTML Works

Examples

❌ Incorrect

// Direct assignment of user input - CRITICAL XSS
element.innerHTML = userInput;

// Template literal with user data - VULNERABLE
element.innerHTML = `<div>${userData.name}</div>`;

// Function result without sanitization - VULNERABLE
element.innerHTML = getUserContent();
element.outerHTML = fetchedData;

// React dangerouslySetInnerHTML (different rule, same concept)
<div dangerouslySetInnerHTML={{ __html: userContent }} />;

✅ Correct

// Use textContent for text - SAFE
element.textContent = userInput;

// Sanitize with DOMPurify - SAFE
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(userInput);

// Literal strings are safe
element.innerHTML = '<p>Static content</p>';

// Create elements programmatically - SAFE
const div = document.createElement('div');
div.textContent = userInput;
container.appendChild(div);

Options

OptionTypeDefaultDescription
allowInTestsbooleanfalseAllow innerHTML in test files
trustedSanitizersstring[]['DOMPurify.sanitize', 'sanitize', 'sanitizeHtml', 'xss', 'purify']Trusted sanitizer function names
allowLiteralStringsbooleantrueAllow innerHTML with literal strings
{
  "rules": {
    "frontend-security/no-innerhtml": [
      "error",
      {
        "trustedSanitizers": ["DOMPurify.sanitize", "myCustomSanitizer"],
        "allowLiteralStrings": true
      }
    ]
  }
}

Best Practices

1. Use DOMPurify

import DOMPurify from 'dompurify';

function renderUserContent(html) {
  return DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
    ALLOWED_ATTR: ['href'],
  });
}

element.innerHTML = renderUserContent(userInput);

2. Prefer textContent

// For plain text, always use textContent
element.textContent = userData.name; // Safe - no HTML parsing

3. Use DOM APIs

// Build DOM programmatically
function createComment(user, text) {
  const div = document.createElement('div');
  div.className = 'comment';

  const author = document.createElement('span');
  author.textContent = user.name; // Safe

  const content = document.createElement('p');
  content.textContent = text; // Safe

  div.appendChild(author);
  div.appendChild(content);
  return div;
}

Known False Negatives

The following patterns are not detected due to static analysis limitations:

Content from Variable

Why: Content assigned to variables is not traced.

// ❌ NOT DETECTED - Content from variable
const html = userInput;
element.innerHTML = html;

Mitigation: Always sanitize before assignment to any variable.

Custom Sanitizer Not Recognized

Why: Non-standard sanitizer names may not be detected.

// ❌ NOT DETECTED - Custom sanitizer
element.innerHTML = myCustomEscape(userInput);

Mitigation: Configure trustedSanitizers with custom function names.

Framework Bindings

Why: Framework-specific binding may not be recognized.

// ❌ NOT DETECTED - jQuery html()
$element.html(userInput);

// ❌ NOT DETECTED - Angular [innerHTML]
<div [innerHTML]="userContent"></div>

Mitigation: Use framework-specific security rules.

Dynamic Property Assignment

Why: Dynamic property access is not analyzed.

// ❌ NOT DETECTED - Dynamic property
const prop = 'innerHTML';
element[prop] = userInput;

Mitigation: Avoid dynamic property assignment for DOM manipulation.

Resources

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

ComponentPurposeExample
Risk StandardsSecurity benchmarksCWE-79 OWASP:A05 CVSS:6.1
Issue DescriptionSpecific vulnerabilityCross-site Scripting (XSS) detected
Severity & ComplianceImpact assessmentMEDIUM [SOC2,PCI-DSS,GDPR,ISO27001]
Fix InstructionActionable remediationFollow the remediation steps below
Technical TruthOfficial referenceOWASP Top 10

On this page