Skip to content

security(CORE-1126): tighten CSP on local renderer windows#3316

Open
jeanfbrito wants to merge 4 commits into
masterfrom
security/CORE-1126-csp
Open

security(CORE-1126): tighten CSP on local renderer windows#3316
jeanfbrito wants to merge 4 commits into
masterfrom
security/CORE-1126-csp

Conversation

@jeanfbrito
Copy link
Copy Markdown
Member

@jeanfbrito jeanfbrito commented Apr 29, 2026

Summary

Addresses Doyensec audit finding ROC-Q422-4 — Weak CSP Directives Set In Main Renderer.

Applies a hardened, tailored Content-Security-Policy meta tag to all three local renderer HTML files.

Before / After

src/public/index.html

Before:

default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'

After:

default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'

(Replaced frame-src 'none' with explicit font-src 'self' data:'frame-src is redundant when default-src 'self' is present; font-src data: covers icon fonts loaded via data URIs from rocketchat.css.)

src/public/video-call-window.html

Before: script-src 'self'
After:

default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'

src/public/log-viewer-window.html

Before: script-src 'self'
After:

default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'

Why each directive

Directive Reason
default-src 'self' Closes the open default (audit finding: missing default-src)
script-src 'self' Allows only bundled scripts; no inline or remote scripts
style-src 'self' 'unsafe-inline' Required: all three windows use inline style= attributes and <style> blocks
img-src 'self' data: error.css uses data: background-image
font-src 'self' data: Icon font CSS (rocketchat.css) loads fonts via data URIs
connect-src 'self' No XHR/fetch from these windows; explicit allowlist
object-src 'none' Audit recommendation; no <object>/<embed>/<applet> in repo
base-uri 'self' Prevents base-tag injection (audit recommendation)
form-action 'self' Prevents formjacking (audit recommendation)
frame-ancestors 'none' Prevents these windows from being framed by any context

Out of scope (not in this PR)

  • nodeIntegration/contextIsolation hardening — covered by ROC-Q422-5 (separate ticket). Changing webPreferences is a larger refactor with regression risk; intentionally deferred.
  • Inline-style elimination — would require refactoring React component output; out of scope for this security patch.
  • Nonce/hash for script-src — current 'self' restriction is already a meaningful improvement. Nonce-based allowlisting can be added in a follow-up once a build pipeline for nonce injection is in place.
  • <webview> CSPvideo-call-window.html uses a <webview> tag to load Jitsi. In Electron, <webview> is not governed by the parent window's frame-src/child-src; its CSP is controlled separately via webview.setAttribute('partition', ...) or webContents session handlers. No change to webPreferences or session handlers in this PR.

Test plan

  • yarn lint — clean (no ESLint errors)
  • npx tsc --noEmit — clean (no TypeScript errors)
  • yarn test — 213 tests passed, 17 suites
  • Manual smoke — UI smoke test requires a display server not available in this environment. Reviewer should: launch yarn start, open each window (main shell, video call, log viewer), open DevTools Console, and confirm zero CSP violation errors appear on load.

Summary by CodeRabbit

  • Chores
    • Strengthened content security policies across app pages and windows to tighten resource loading and network connections, permit needed media and inline styling, allow inline/data media when required, block plugin/object embedding, and restrict framing, navigation, and form targets to the same origin—improving protection against injection and unauthorized embedding.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6fbdb116-03dd-4e4b-aee9-42ffdc1c9326

📥 Commits

Reviewing files that changed from the base of the PR and between 6f00ea4 and 07aaa05.

📒 Files selected for processing (3)
  • src/public/index.html
  • src/public/log-viewer-window.html
  • src/public/video-call-window.html
✅ Files skipped from review due to trivial changes (1)
  • src/public/log-viewer-window.html
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/public/video-call-window.html
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: check (ubuntu-latest)
  • GitHub Check: check (windows-latest)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
src/public/index.html (1)

5-5: CSP update looks good and consistent with the hardening goals.

This policy is meaningfully tightened while preserving required renderer behavior (network + webview schemes) and keeping high-value restrictions (object-src 'none', base-uri 'self', form-action 'self') in place.


Walkthrough

Three HTML files in src/public/ expand their Content-Security-Policy meta tags from a simple script-src 'self' to comprehensive policies: default-src 'self', explicit script-src/style-src (includes 'unsafe-inline'), img-src/font-src (allow data:), connect-src (allowed schemes/origins), object-src 'none', base-uri 'self', form-action 'self', and frame-src/child-src restricted to https:/http:.

Changes

Cohort / File(s) Summary
Content Security Policy Updates
src/public/index.html, src/public/log-viewer-window.html, src/public/video-call-window.html
Replaced minimal script-src 'self' CSP meta tags with full CSPs: default-src 'self', explicit script-src, style-src 'self' 'unsafe-inline', img-src/font-src allowing data:, connect-src (explicit origins/schemes), object-src 'none', base-uri 'self', form-action 'self', and frame-src/child-src restricted to https:/http:. Review attention: differing connect-src specifics across files and presence of 'unsafe-inline' for styles.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Suggested labels

type: chore

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'security(CORE-1126): tighten CSP on local renderer windows' accurately describes the main change: expanding and hardening Content-Security-Policy directives across three local renderer HTML files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • CORE-1126: Request failed with status code 401

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
Review rate limit: 4/8 reviews remaining, refill in 24 minutes and 50 seconds.

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

Copy link
Copy Markdown
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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/public/index.html`:
- Line 5: The meta-delivered CSP includes frame-ancestors which is ignored for
clickjacking protection; remove the frame-ancestors directive from the <meta>
tag in src/public/index.html and instead inject it as an HTTP response header
from the Electron main process by adding/adjusting a handler like
session.defaultSession.webRequest.onHeadersReceived (or
webContents.session.webRequest.onHeadersReceived) to append/replace the
Content-Security-Policy header with a value that includes frame-ancestors
'none'; ensure the header name is "Content-Security-Policy" and that other CSP
directives from the meta tag are preserved when building the header value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 318c385b-6af6-491b-9cb9-d1b50953c516

📥 Commits

Reviewing files that changed from the base of the PR and between e1fe77e and 1700741.

📒 Files selected for processing (3)
  • src/public/index.html
  • src/public/log-viewer-window.html
  • src/public/video-call-window.html
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: check (ubuntu-latest)
  • GitHub Check: check (windows-latest)
  • GitHub Check: Analyze (actions)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
src/public/log-viewer-window.html (1)

5-5: Same frame-ancestors meta-CSP caveat as the shared policy.

Tracking this at the root comment on src/public/index.html Line 5 to avoid duplication.

src/public/video-call-window.html (1)

5-5: CSP does not restrict webview guest URLs in Electron.

Page-level CSP on the embedder (including default-src 'self') does not restrict <webview> guest URL loading. Electron's webview operates in a separate security context. The code properly validates the guest URL via validateVideoCallUrl() before assignment, which is the appropriate security layer.

Comment thread src/public/index.html Outdated
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Security-Policy" content="script-src 'self'">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Verify whether CSP is also injected as an HTTP response header at runtime.
rg -nP -C3 'onHeadersReceived|Content-Security-Policy|webRequest|setHeader' --type=js --type=ts

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 702


🌐 Web query:

Is the CSP directive frame-ancestorsenforced when Content-Security-Policy is delivered via (Chromium/Electron context)?

💡 Result:

No, the frame-ancestors directive is not enforced when Content-Security-Policy is delivered via in Chromium or Electron. Browsers, including Chromium-based ones like Chrome and Electron, ignore the frame-ancestors directive (along with report-uri and sandbox) in meta-delivered CSP policies, as required by specification. This behavior is observed in console warnings like "The Content Security Policy directive 'frame-ancestors' is ignored when delivered via a element."

Citations:


🏁 Script executed:

#!/bin/bash
# Search for Electron main process files that might set CSP headers
fd -e js -e ts -e mjs | grep -E '(main|preload|app|background)\.(js|ts|mjs)$' | head -20

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 555


🏁 Script executed:

#!/bin/bash
# Search for response header configuration or session CSP in source files
rg -nP 'session\.|\.webRequest|\.onHeadersReceived|Content-Security-Policy' --type=js --type=ts src/ 2>/dev/null | head -50

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 2863


🏁 Script executed:

#!/bin/bash
# Search for webRequest handlers and response header manipulation
rg -nP 'webRequest|onHeadersReceived|onBeforeSendHeaders|responseHeaders' --type=js --type=ts src/

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 57


🏁 Script executed:

#!/bin/bash
# Check main.ts for CSP configuration
head -100 src/main.ts && echo "..." && tail -100 src/main.ts

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 5800


🏁 Script executed:

#!/bin/bash
# Check app.ts for CSP or response header configuration
cat -n src/app/main/app.ts | head -150

Repository: RocketChat/Rocket.Chat.Electron

Length of output: 5764


frame-ancestors in meta-delivered CSP is ignored and will not prevent clickjacking.

Move this directive to an HTTP response header set by the Electron main process (e.g., via session.webRequest.onHeadersReceived or equivalent header injection) to enforce the anti-framing policy.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/public/index.html` at line 5, The meta-delivered CSP includes
frame-ancestors which is ignored for clickjacking protection; remove the
frame-ancestors directive from the <meta> tag in src/public/index.html and
instead inject it as an HTTP response header from the Electron main process by
adding/adjusting a handler like
session.defaultSession.webRequest.onHeadersReceived (or
webContents.session.webRequest.onHeadersReceived) to append/replace the
Content-Security-Policy header with a value that includes frame-ancestors
'none'; ensure the header name is "Content-Security-Policy" and that other CSP
directives from the meta tag are preserved when building the header value.

`connect-src 'self'` blocked the fetch() calls in src/servers/renderer.ts
that probe a Rocket.Chat server's home page and /api/info endpoint when
adding or resolving a server. Widen connect-src to https: http: ws: wss:
so server resolution works against both HTTPS and plain-HTTP hosts.

video-call-window.html and log-viewer-window.html are unchanged —
neither window issues remote fetch() calls from its renderer JS.
@jeanfbrito
Copy link
Copy Markdown
Member Author

Fix: connect-src regression (found by Codex review)

A Codex review identified a P1 regression introduced by this PR: connect-src 'self' in src/public/index.html blocked all outbound fetch() calls from the main shell renderer (rootWindow.js), which runs from the file:// origin. The two affected call sites are:

  • src/servers/renderer.ts line 24: fetch(url.href) — probes the server home page when adding/resolving a Rocket.Chat server
  • src/servers/renderer.ts line 32: fetch(endpoint.href) — fetches /api/info to validate the server

With connect-src 'self', both requests were denied by CSP, silently breaking server resolution for all users.

Fix (commit 11798d8): Widened connect-src in src/public/index.html to:

connect-src 'self' https: http: ws: wss:

http: is included for self-hosted plain-HTTP local servers. ws:/wss: covers WebSocket connections initiated from the shell renderer. video-call-window.html and log-viewer-window.html are unchanged — audited and confirmed to have no remote fetch() calls from their renderer bundles.

Verification: lint clean, tsc --noEmit clean, 213/213 tests passing.

Electron <webview> elements may inherit CSP restrictions from the host
page in certain renderer modes. Both video-call-window.html (Jitsi) and
index.html (RC server webviews) set webview.src to remote http/https
URLs. Adding frame-src and child-src https: http: ensures those webview
loads are not blocked by the host page CSP.
@jeanfbrito
Copy link
Copy Markdown
Member Author

Codex review v2 finding addressed (commit 6f00ea4)

P1: frame-src / child-src missing from webview host pages

Electron <webview> elements may inherit CSP restrictions from the host renderer page. Both HTML pages that host webviews were missing frame-src and child-src directives, which could block the webview loads:

File webview.src target
src/public/video-call-window.html Jitsi URLs (http: or https:, per validateVideoCallUrl)
src/public/index.html Rocket.Chat server URLs (lastPath || serverUrl, always https: in practice but http: also allowed by the validator)

Fix applied to both files:

; frame-src https: http:; child-src https: http:

Both http: and https: included to match validateVideoCallUrl's allowed protocol list. All other directives left unchanged. No 'unsafe-inline' added to script-src. webPreferences not touched.

Verification: yarn lint clean, npx tsc --noEmit clean, yarn test 213/213 passed.

Per W3C spec, frame-ancestors directive is only recognized in HTTP response
headers. When delivered via <meta http-equiv>, it is silently ignored by
Chromium. These are file:// renderer windows in an Electron desktop app
with no realistic embedding vector — the directive provided false security.
@jeanfbrito
Copy link
Copy Markdown
Member Author

Codex v4 P2 Finding Addressed

Issue: frame-ancestors 'none' directive in <meta http-equiv> CSP is silently ignored by Chromium per W3C spec — only HTTP response headers recognize this directive.

Scope: Meta CSP in renderer windows (index.html, video-call-window.html, log-viewer-window.html) — all file://-loaded Electron renderer contexts with no realistic embedding vector.

Fix: Removed ; frame-ancestors 'none' from all three files. This eliminates false security and reduces unnecessary CSP bulk.

Verification:

  • Lint: ✓ passed
  • Typecheck: ✓ passed
  • Test suite: ✓ 213 tests passed across 17 suites

Final CSP (all three files): default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self' [https/http/ws/wss per file]; object-src 'none'; base-uri 'self'; form-action 'self'; [frame-src/child-src as appropriate]

@jeanfbrito jeanfbrito changed the base branch from master to dev April 29, 2026 19:53
Base automatically changed from dev to master May 4, 2026 22:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant