Skip to content

XSS: Layout_Home_Body admin setting renders unsanitized HTML without DOMPurify #38707

@swarajreddy10

Description

@swarajreddy10

Description:

The Layout_Home_Body admin setting (Administration → Layout → Content → Body) renders HTML via dangerouslySetInnerHTML without sanitization, allowing stored XSS. While CSP blocks execution in default deployments, malicious HTML still reaches the DOM and is exploitable in misconfigured instances or via direct database access.

Affected File: apps/meteor/client/views/home/CustomHomePageContent.tsx (line 8)

Vulnerability Type: Stored Cross-Site Scripting (XSS)

Steps to reproduce:

  1. Log in as an administrator
  2. Navigate to Administration → Layout → Content (or /admin/layout)
  3. Enable "Show custom content to homepage"
  4. In the Body textarea, paste:
    <img src=x onerror="alert('XSS: ' + document.domain)">
  5. Save the settings
  6. Navigate to /home as any user
  7. Open browser console

Expected behavior:

The onerror attribute should be stripped during sanitization. Only <img src="x"> should render as a broken image with no script execution attempt.

Actual behavior:

The raw HTML is injected into the DOM. The onerror handler is present and attempts to execute.

Browser Console Output:

Refused to execute inline event handler because it violates the following 
Content Security Policy directive: "script-src 'self' ..."

The CSP violation proves the malicious attribute reached the DOM. CSP is not a substitute for input sanitization.

Root Cause:

// Current vulnerable code
const body = useSetting('Layout_Home_Body', '');
return <Box withRichContent dangerouslySetInnerHTML={{ __html: body }} {...props} />;

No sanitization occurs between database retrieval and DOM insertion.

Proposed Fix:

import DOMPurify from 'dompurify';

const body = useSetting('Layout_Home_Body', '');
const sanitized = DOMPurify.sanitize(body, {
  ADD_TAGS: ['iframe'],
  ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder', 'src'],
});
return <Box withRichContent dangerouslySetInnerHTML={{ __html: sanitized }} {...props} />;

Server Setup Information:

  • Version of Rocket.Chat Server: 8.2.0-develop
  • License Type: Community
  • Number of Users: N/A (development environment)
  • Operating System: Windows 11
  • Deployment Method: Development (local)
  • Number of Running Instances: 1
  • DB Replicaset Oplog: Enabled
  • NodeJS Version: 22.18.0
  • MongoDB Version: 8.0.19

Client Setup Information

  • Desktop App or Browser Version: Chrome 132, Firefox 115
  • Operating System: Windows 11

Additional context

Security Impact:

  • Severity: High (requires admin access, affects all users)
  • Attack Vectors: Compromised admin account, direct MongoDB write, malicious insider
  • Scope: All users visiting /home receive unsanitized HTML
  • Mitigation: dompurify already exists in project dependencies (packages/gazzodown)

Inconsistency in Codebase:
Other components correctly sanitize similar inputs (e.g., SidebarFooterDefault.tsx:35 uses DOMPurify.sanitize(logo)), indicating this is an oversight.

Relevant logs:

Browser Console (Chrome 132):

Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src 'self' 'unsafe-eval' https://cdn.rudderlabs.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution.

Server Logs:
No server-side errors. The vulnerability is client-side DOM injection.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions