Skip to main content
interlace
Plugin: browser-securityRules

no-postmessage-innerhtml

The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:

🔒 Disallow using innerHTML with postMessage data

CWE: CWE-693
OWASP Mobile: OWASP Mobile Top 10

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

Rule Details

This rule prevents using innerHTML, outerHTML, insertAdjacentHTML(), or document.write() with data received from postMessage events. This pattern enables XSS attacks through malicious messages.

Why is this dangerous?

When you use innerHTML with postMessage data:

  1. Any window can send messages to your page
  2. Malicious HTML/scripts are executed when assigned to innerHTML
  3. Attackers can steal data, session tokens, and perform actions as the user

Examples

❌ Incorrect

// Direct innerHTML with event.data
window.addEventListener('message', (event) => {
  element.innerHTML = event.data;
});

// Nested property access
window.addEventListener('message', (event) => {
  container.innerHTML = event.data.content;
});

// outerHTML is equally dangerous
window.addEventListener('message', (event) => {
  widget.outerHTML = event.data.html;
});

// insertAdjacentHTML
window.addEventListener('message', (event) => {
  list.insertAdjacentHTML('beforeend', event.data);
});

// document.write (rare but dangerous)
window.addEventListener('message', (event) => {
  document.write(event.data);
});

✅ Correct

// Use textContent for plain text
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://trusted.com') return;
  element.textContent = event.data;
});

// Sanitize before using innerHTML
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://trusted.com') return;
  const sanitized = DOMPurify.sanitize(event.data);
  element.innerHTML = sanitized;
});

// Use intermediate variable (sanitized)
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://trusted.com') return;
  const safeHtml = DOMPurify.sanitize(event.data.content);
  container.innerHTML = safeHtml;
});

// Use DOM APIs instead
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://trusted.com') return;
  const text = document.createTextNode(event.data);
  element.appendChild(text);
});

Options

OptionTypeDefaultDescription
allowInTestsbooleantrueSkip this rule in *.test.* / *.spec.* files
{
  "browser-security/no-postmessage-innerhtml": [
    "error",
    {
      "allowInTests": true
    }
  ]
}
OptionTypeDefaultDescription
allowInTestsbooleantrueSkip checking in test files (_.test.ts, _.spec.ts)

When Not To Use It

You may disable this rule if:

  • You're handling postMessage in a sandbox/isolated context
  • You have thorough sanitization that the rule can't detect

However, always sanitize postMessage data before rendering as HTML.

Rule ownership

This rule fires only when the receiver is positively identified as window / self / globalThis / parent / top in the same file. X.onmessage = … on a receiver this file cannot resolve is not evidence of postMessage — it is unknown, and unknown belongs to no-innerhtml / no-eval, which report it without claiming a provenance they cannot prove.

The two tests are complements, so exactly one rule reports any given value. Before this gate both fired at the identical range in recommended.

A receiver that arrives as a parameter or from another module therefore falls to the generic rule. That is deliberate: the alternative is guessing.

Known False Negatives

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

Event Data Stored in Variable

Why: Data stored in variables not traced.

// ❌ NOT DETECTED - Data stored first
window.addEventListener('message', (event) => {
  const html = event.data;
  element.innerHTML = html;
});

Mitigation: Sanitize before any variable assignment.

Separate Handler Function

Why: Handler internals not analyzed.

// ❌ NOT DETECTED - External handler
window.addEventListener('message', handlePostMessage);

Mitigation: Apply rule to handler implementations.

Custom Sanitizer

Why: Non-standard sanitizers may not be recognized.

// ❌ NOT DETECTED - Custom sanitizer
element.innerHTML = mySanitizeFunc(event.data);

Mitigation: Configure trusted sanitizer names.

OWASP Mapping

CategoryID
OWASP Top 10 2021A03:2021 - Injection
CWECWE-79
CVSS8.8 (High)

Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.