Skip to content

fix: sanitize HTML before dangerouslySetInnerHTML#41545

Open
yashwant938 wants to merge 3 commits intoappsmithorg:releasefrom
yashwant938:yash-fix-html-sanitization-xss
Open

fix: sanitize HTML before dangerouslySetInnerHTML#41545
yashwant938 wants to merge 3 commits intoappsmithorg:releasefrom
yashwant938:yash-fix-html-sanitization-xss

Conversation

@yashwant938
Copy link

@yashwant938 yashwant938 commented Feb 7, 2026

Harden client-side rendering paths that use dangerouslySetInnerHTML by sanitizing HTML with a small allowlist sanitizer.\n\n- Add utils/sanitizeHtml helper (removes scripts, event handlers, unsafe URLs; enforces rel=noopener noreferrer for target=_blank).\n- Apply sanitization to release notes, upgrade carousel details, tern tooltip docs, manual upgrades, and CRUD info modal.\n- Add unit tests for sanitizer behavior.\n\nThis reduces DOM-XSS risk if upstream HTML or interpolated content is compromised.

Description

Tip

Add a TL;DR when the description is longer than 500 words or extremely technical (helps the content, marketing, and DevRel team).

Please also include relevant motivation and context. List any dependencies that are required for this change. Add links to Notion, Figma or any other documents that might be relevant to the PR.

Fixes #Issue Number
or
Fixes Issue URL

Warning

If no issue exists, please create an issue first, and check with the maintainers if the issue is valid.

Automation

/ok-to-test tags=""

🔍 Cypress test results

Caution

If you modify the content in this section, you are likely to disrupt the CI result for your PR.

Communication

Should the DevRel and Marketing teams inform users about this change?

  • Yes
  • No

Summary by CodeRabbit

  • New Features

    • Added a client-side HTML sanitizer to ensure unsafe HTML is stripped before rendering.
  • Bug Fixes

    • Sanitized dynamic HTML across product descriptions, upgrade prompts, release notes and tooltips to prevent unsafe rendering.
  • Style

    • Improved rendering of inline code blocks and adjusted URL presentation in upgrade messages for clearer display.
  • Tests

    • Added unit tests validating HTML sanitization behavior and safety guarantees.

Harden client-side rendering paths that use dangerouslySetInnerHTML by sanitizing HTML with a small allowlist sanitizer.\n\n- Add utils/sanitizeHtml helper (removes scripts, event handlers, unsafe URLs; enforces rel=noopener noreferrer for target=_blank).\n- Apply sanitization to release notes, upgrade carousel details, tern tooltip docs, manual upgrades, and CRUD info modal.\n- Add unit tests for sanitizer behavior.\n\nThis reduces DOM-XSS risk if upstream HTML or interpolated content is compromised.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 7, 2026

Walkthrough

Adds a dependency-free client-side HTML sanitizer and integrates it into several UI components to sanitize dynamic HTML (descriptions, tooltips, messages) before rendering, plus unit tests and a minor constant presentation tweak.

Changes

Cohort / File(s) Summary
Sanitizer + tests
app/client/src/utils/sanitizeHtml.ts, app/client/src/utils/sanitizeHtml.test.ts
New default-exported sanitizeHtml function with whitelist-based tag/attribute handling, URL validation, rel normalization, SSR fallback; unit tests covering scripts, event handlers, unsafe URLs, and rel/target adjustments.
Component integrations
app/client/src/ce/pages/Upgrade/Carousel.tsx, app/client/src/components/BottomBar/ManualUpgrades.tsx, app/client/src/pages/Applications/ProductUpdatesModal/ReleaseComponent.tsx, app/client/src/pages/Editor/GeneratePage/components/CrudInfoModal.tsx, app/client/src/utils/autocomplete/ternDocTooltip.tsx
Imported sanitizeHtml and React useMemo in components; produce memoized sanitized HTML (e.g., descriptions, tooltips, success messages, disclaimers) and render via dangerouslySetInnerHTML. Minor styling tweak for inline code in manual upgrades.
Constants tweak
app/client/src/ce/constants/messages.ts
Small presentation change: URL now rendered inside a plain code block (styling adjusted).

Sequence Diagram(s)

sequenceDiagram
    participant Component as UI Component
    participant Memo as useMemo
    participant Sanitizer as sanitizeHtml
    participant DOM as Browser DOM

    Component->>Memo: provide dynamicHtml
    Memo->>Sanitizer: call sanitizeHtml(dynamicHtml)
    Sanitizer-->>Memo: return safeHtml
    Memo-->>Component: memoized safeHtml
    Component->>DOM: render via dangerouslySetInnerHTML(safeHtml)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

A tidy scrub before the DOM's delight,
Stripping scripts and handlers out of sight,
Links made safe, and code blocks neat,
Memoized cleans keep rendering sweet,
Frontend gleams in sanitized light. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main change: adding HTML sanitization before dangerouslySetInnerHTML usage to mitigate XSS risks.
Description check ✅ Passed The PR description provides clear context on the sanitizer implementation, files modified, and security purpose, but lacks issue linkage and CI automation details required by the template.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/client/src/utils/autocomplete/ternDocTooltip.tsx (1)

25-29: ⚠️ Potential issue | 🔴 Critical

Rules of Hooks violation: useMemo called after early return.

useMemo on line 29 is below the early return on line 25. React requires hooks to be called unconditionally and in the same order every render. This will cause runtime warnings and potentially broken state.

Move the useMemo above the early return.

Proposed fix
   if (!doc || !displayText) return null;
 
   const examples =
     displayText in ternDocsInfo ? ternDocsInfo[displayText].exampleArgs : null;
-  const safeDoc = useMemo(() => sanitizeHtml(doc), [doc]);

Move useMemo before the guard:

+  const safeDoc = useMemo(() => sanitizeHtml(doc ?? ""), [doc]);
+
   if (!doc || !displayText) return null;
 
   const examples =
     displayText in ternDocsInfo ? ternDocsInfo[displayText].exampleArgs : null;
🤖 Fix all issues with AI agents
In `@app/client/src/components/BottomBar/ManualUpgrades.tsx`:
- Around line 160-171: The CLEAN_URL_UPDATE description uses inline style on a
code tag which is being stripped by sanitizeHtml, so either allow the style
attribute for code tags in sanitizeHtml or move those inline styles into the
existing StyledList CSS; specifically update sanitizeHtml (function
sanitizeHtml) to permit the style attribute on the code element OR remove the
inline style from the CLEAN_URL_UPDATE template in messages
(CLEAN_URL_UPDATE.description) and add equivalent styling in the StyledList
component's CSS rules that target code elements so the visual appearance is
preserved when createMessage(...) output is sanitized.

In `@app/client/src/utils/sanitizeHtml.test.ts`:
- Around line 4-10: sanitizeHtml test fails because sanitizeElement currently
unwraps <script> children instead of removing them, so their text (e.g.,
"alert(1)") remains; update sanitizeElement to fully remove SCRIPT elements and
their content rather than returning their child nodes. Specifically, inside
sanitizeElement (and any branch that handles tagName or node.nodeName), detect
node.tagName === "SCRIPT" (or node.nodeName === "SCRIPT") and return null (or
remove the node) so its children are not processed/concatenated into the parent;
ensure sanitizeHtml uses that behavior so
sanitizeHtml("<p>ok<script>...</script></p>") yields "<p>ok</p>" and the test
assertion expecting no "alert(1)" passes.
- Around line 1-33: Add comprehensive unit tests for the sanitizeHtml function
to cover the missing security and edge cases: write tests that assert removal of
<style>, <iframe>, <object>, <embed>, and nested disallowed elements (e.g.,
<div><script>...</script></div>), ensure SVG vectors with event attributes
(e.g., <svg onload=...>) are sanitized, verify data: URI schemes are stripped or
rejected for <img src> and <a href>, confirm HTML comments are removed, add
tests for empty/null/non-string inputs (e.g., undefined, null, empty string) to
ensure graceful handling, assert that style attributes are removed while allowed
tags/attributes remain intact (positive cases), and include assertions that
rel="noopener noreferrer" is added for target="_blank"; use the existing
sanitizeHtml test file and the same describe/it structure targeting the
sanitizeHtml function.

In `@app/client/src/utils/sanitizeHtml.ts`:
- Around line 1-185: The sanitizer (sanitizeHtml, sanitizeNode, sanitizeElement)
is brittle and misses mXSS/namespace/template edge cases; replace the custom
sanitizer with a proven library: install and integrate DOMPurify, use
DOMPurify.sanitize(dirtyHtml, {ALLOWED_TAGS: Array.from(DEFAULT_ALLOWED_TAGS),
ALLOWED_ATTR: /* map from DEFAULT_ALLOWED_ATTRS_BY_TAG */}) for client-side HTML
rendering, and remove or deprecate the hand-rolled logic
(sanitizeNode/sanitizeElement/escapeHtml/isSafeUrl/ensureSafeRel) so that
DOMPurify handles SVG/MathML/ template/ noscript contexts and URL/rel
sanitization consistently.
- Around line 89-102: The sanitizeElement function currently unwraps children of
any disallowed tag which re-parents script/style/etc. text into the DOM; change
the logic so that for a dedicated set of full-remove tags (e.g., "script",
"style", "iframe", "object", "embed", "noscript") you remove the element and all
its children outright instead of moving children into the parent, while keeping
the existing "unwrap children" behavior for other disallowed tags; update
sanitizeElement to check tag membership in this FULL_REMOVE_TAGS set and call
parent.removeChild(el) immediately for those tags (ensuring parent exists) to
prevent visible or retained malicious content.
🧹 Nitpick comments (2)
app/client/src/utils/sanitizeHtml.ts (2)

59-66: escapeHtml doesn't escape backticks.

Minor, but if the escaped output ever lands inside a JS template literal context, unescaped backticks could be an issue. Standard practice is to also escape `&#96;. Low risk given current usage but worth noting.


105-105: Allocating a new empty Set on every attribute iteration for unrecognized tags.

Minor perf nit — hoist a shared empty set to avoid per-element allocation.

Proposed fix
+const EMPTY_SET = new Set<string>();
+
 function sanitizeElement(el: Element) {
   ...
-  const allowedAttrs = DEFAULT_ALLOWED_ATTRS_BY_TAG[tag] ?? new Set<string>();
+  const allowedAttrs = DEFAULT_ALLOWED_ATTRS_BY_TAG[tag] ?? EMPTY_SET;

Remove inline style attributes from CLEAN_URL_UPDATE message templates since HTML is now sanitized before rendering.\n\n- Render <code> without style attributes in message constants.\n- Move the intended wrapping/line-height to StyledList CSS for consistent styling.
- Drop script/style/iframe/object/embed/noscript entirely (do not unwrap children).\n- Disallow data: URIs in <a href> while still allowing safe data:image in <img src>.\n- Add broad test coverage for common XSS vectors and edge cases (comments, SVG/Math, style stripping, non-string inputs).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant