CASE FILE#6347WARNFiled 2026-04-13

Preventing XSS: OpenHands and dangerouslySetInnerHTML

OpenHands uses React's dangerouslySetInnerHTML to render user-generated content. SSG flags the unsafe pattern before it's saved — DOM-based XSS from unescaped HTML is a critical web security risk.

Case ID
#6347
Severity
WARN
Decision
BLOCK
Agent
OpenHands
Category
Source Code

Correction note (2026-10-13). The per-call timings and latency figures that appeared in this article had no benchmark behind them and were removed. Sample output in this article is illustrative, not a recorded run. The only published measurement is p50 0.39 ms and p99 1.91 ms (sigmashake-bench daemon-socket run, 2026-06-10, ssg 0.29.156, n=500). Sub-2 ms evaluation is a design target.

Describe the task

You ask OpenHands to render some user-generated HTML content in a React component. The agent uses the React escape hatch for raw HTML injection:

function UserPost({ content }: { content: string }) {
  return <div dangerouslySetInnerHTML={{ __html: content }} />;
}

If content is user-supplied and not sanitized, an attacker can inject a script tag and execute arbitrary JavaScript in your users' browsers — stealing sessions, redirecting to phishing pages, or exfiltrating data. SSG's no-dangerous-inner-html rule flags the write.

# illustrative sample output
[BLOCK] write_file › src/components/UserPost.tsx — rule: no-dangerous-inner-html
Reason: dangerouslySetInnerHTML with a variable source detected.

Give SSG context

SSG intercepts write calls via the SSG MCP Server integration for OpenHands agents and validates component code before saving.

Required context

rule no-dangerous-inner-html {
  enabled true
  priority 90
  severity error
  DENY write
  IF content CONTAINS "dangerouslySetInnerHTML"
  AND content CONTAINS "__html:"
  AND content NOT CONTAINS "DOMPurify"
  AND content NOT CONTAINS "sanitize"
  MESSAGE "dangerouslySetInnerHTML without sanitization is an XSS risk. Use DOMPurify or a safe renderer."
}

Optional context

The rule allows the pattern when DOMPurify is already imported in the same file. If your team uses a different sanitization library, add it to the exclusion:

rule no-dangerous-inner-html-custom-sanitizer {
  enabled true
  priority 90
  severity error
  DENY write
  IF content CONTAINS "dangerouslySetInnerHTML"
  AND content CONTAINS "__html:"
  AND content NOT CONTAINS "DOMPurify"
  AND content NOT CONTAINS "sanitize"
  AND content NOT CONTAINS "sanitizeHtml"
  AND content NOT CONTAINS "xss("
  MESSAGE "dangerouslySetInnerHTML without sanitization is an XSS risk. Use DOMPurify or a safe renderer."
}

What SSG does

SSG checks for the presence of dangerouslySetInnerHTML alongside a variable source and the absence of a sanitizer reference. Files that import and use DOMPurify before passing to __html will pass this check.

Ask the agent to add sanitization

dangerouslySetInnerHTML was blocked: the content source is not sanitized.
Rewrite using DOMPurify:
  import DOMPurify from 'dompurify';
  <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(content) }} />

Or use a safe Markdown renderer like react-markdown instead of raw HTML.

Suggest a Markdown renderer as the correct approach

For most user-generated content, Markdown rendering is safer than raw HTML:

If the content is Markdown, use react-markdown:
  import ReactMarkdown from 'react-markdown';
  <ReactMarkdown>{content}</ReactMarkdown>
This renders safe HTML without exposing you to injection.

Audit all dangerouslySetInnerHTML uses in your codebase

Run grep -r "dangerouslySetInnerHTML" src/ after onboarding SSG to find historical uses that predate the rule. Address those before new vulnerabilities are introduced.

How your rule avoids false positives

The condition AND content NOT CONTAINS "DOMPurify" passes the check if DOMPurify is imported anywhere in the file — even if it's not on the same line as the dangerouslySetInnerHTML call. For stricter enforcement, require the sanitize call to be adjacent to the __html assignment.

Check the rule against your test files

Test files may render unsanitized HTML intentionally to test XSS scenarios. Add a path exclusion for *.test.tsx or *.spec.tsx if your tests legitimately use the unsafe pattern.

Filed by OpenHands
Engine ssg v1 · 2026-04-13
End of case file

Stop reading. Start governing.

Test a rule against any tool call right now. No install required.