Skip to content

fix(desktop): harden Electron Windows runtime#152

Merged
Astro-Han merged 3 commits into
devfrom
codex/fix-i141-electron-hardening
Apr 22, 2026
Merged

fix(desktop): harden Electron Windows runtime#152
Astro-Han merged 3 commits into
devfrom
codex/fix-i141-electron-hardening

Conversation

@Astro-Han

@Astro-Han Astro-Han commented Apr 22, 2026

Copy link
Copy Markdown
Owner

Summary

  • Lazy-load electron-store so settings storage is not constructed during main-process module import.
  • Move renderer startup data behind preload IPC and run renderer windows with sandbox, contextIsolation, and no renderer Node access.
  • Load packaged renderer HTML through the privileged pawwork-renderer://renderer origin and narrow sidecar CORS to that origin.

Why

This is direction 2 from #141: Electron/process-layer Windows hardening. It keeps the PR scoped to upstream candidates #23373, #23523, and #23516. It does not include #23633, #23396, Windows ARM64, or the opencode-layer work already covered by #148.

Related Issue

Closes #141. PR #148 already covered the opencode-layer Windows compatibility track; this PR covers the remaining Electron/process-layer Windows hardening track.

How To Verify

  • cd packages/desktop-electron && bun test
  • cd packages/desktop-electron && bun run typecheck
  • cd packages/desktop-electron && bun run build

Screenshots/Recordings

N/A, desktop runtime hardening only.

Checklist

Summary by CodeRabbit

  • New Features

    • Implemented custom renderer protocol for serving application assets securely
    • Exposed window configuration and deep link handling through new IPC-based APIs
  • Improvements

    • Renderer process now operates with enhanced isolation through sandboxing and context isolation
    • CORS policy restricted to renderer protocol origin for improved security posture

@Astro-Han Astro-Han added bug Something isn't working windows Windows-specific P1 High priority upstream Tracked upstream or vendor behavior platform Electron shell, OS integration, packaging, updater, signing, paths, and permissions labels Apr 22, 2026
@gemini-code-assist

Copy link
Copy Markdown

Note

Gemini is unable to generate a review for this pull request due to the file types involved not being currently supported.

@coderabbitai

coderabbitai Bot commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@Astro-Han has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 38 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 38 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1de2760b-2461-4248-a3e9-da5404533e77

📥 Commits

Reviewing files that changed from the base of the PR and between d290e88 and d85e2c7.

📒 Files selected for processing (23)
  • packages/desktop-electron/electron.vite.config.ts
  • packages/desktop-electron/src/main/index.ts
  • packages/desktop-electron/src/main/ipc-window-config.test.ts
  • packages/desktop-electron/src/main/ipc.ts
  • packages/desktop-electron/src/main/renderer-protocol.test.ts
  • packages/desktop-electron/src/main/renderer-protocol.ts
  • packages/desktop-electron/src/main/server-store.test.ts
  • packages/desktop-electron/src/main/server.test.ts
  • packages/desktop-electron/src/main/server.ts
  • packages/desktop-electron/src/main/store.test.ts
  • packages/desktop-electron/src/main/store.ts
  • packages/desktop-electron/src/main/window-options.ts
  • packages/desktop-electron/src/main/windows-protocol.test.ts
  • packages/desktop-electron/src/main/windows-security.test.ts
  • packages/desktop-electron/src/main/windows.ts
  • packages/desktop-electron/src/preload/index.ts
  • packages/desktop-electron/src/preload/types.ts
  • packages/desktop-electron/src/renderer/env.d.ts
  • packages/desktop-electron/src/renderer/html.test.ts
  • packages/desktop-electron/src/renderer/index.tsx
  • packages/desktop-electron/src/renderer/startup-state.test.ts
  • packages/desktop-electron/src/renderer/startup-state.ts
  • packages/desktop-electron/src/renderer/updater.ts
📝 Walkthrough

Walkthrough

The PR introduces a custom pawwork-renderer:// protocol for serving renderer files, transitions window configuration delivery from globals injection to IPC handlers, refactors the store module for lazy initialization, and enhances security by enabling sandbox mode with proper preload isolation. Comprehensive tests validate protocol behavior, IPC signatures, store lifecycle, and startup state management.

Changes

Cohort / File(s) Summary
Renderer Protocol Setup
src/main/renderer-protocol.ts, src/main/renderer-protocol.test.ts, src/main/windows.ts, src/main/windows-protocol.test.ts
Implements custom pawwork-renderer:// protocol with URL construction (rendererUrl), file resolution with path-traversal protection (resolveRendererFile), and registration in Electron's privileged schemes. Windows now load HTML via protocol URLs instead of filesystem paths.
IPC Configuration & Deep Links
src/main/ipc.ts, src/main/ipc-window-config.test.ts, src/main/index.ts, src/preload/index.ts, src/preload/types.ts
Adds two new IPC handlers: get-window-config (returns updater/WSL settings) and consume-initial-deep-links (returns and clears queued deep links). Preload API exposes corresponding async methods. Removes mainWindowGlobals() function and globals injection pattern.
Store Lazy Initialization
src/main/store.ts, src/main/store.test.ts, src/main/server.ts, src/main/server-store.test.ts
Converts store from eager singleton export to lazy getStore(name?) function with internal caching. Settings operations (getDefaultServerUrl, setDefaultServerUrl, getWslConfig, setWslConfig) now call getStore() on each invocation. Tests validate constructor is deferred until first getStore() call.
Window Security & Preload
src/main/windows.ts, src/main/windows-security.test.ts, electron.vite.config.ts
Enables sandbox mode (sandbox: true), context isolation, disables node integration, and updates preload path from .mjs to .js. Vite config explicitly sets preload output to CommonJS format. Window creation functions now accept no arguments (removed Globals parameter).
Renderer Startup State
src/renderer/startup-state.ts, src/renderer/startup-state.test.ts, src/renderer/index.tsx, src/renderer/updater.ts
Introduces startup-state factory that fetches window config and deep links via IPC at app boot, with fallback on errors. Renderer replaces window.__OPENCODE__ global access with startupState methods (updaterEnabled(), wslEnabled(), consumeInitialDeepLinks()). Deep-link buffering now uses pushPendingDeepLinks() utility.
Types & Documentation
src/renderer/env.d.ts, src/renderer/html.test.ts
Removes updaterEnabled and wsl from global Window.__OPENCODE__ type (only deepLinks remains). Updates test documentation to reflect renderer protocol semantics instead of file:// protocol behavior.

Sequence Diagram

sequenceDiagram
    participant App as Electron App
    participant Main as Main Process
    participant Store as Store (Lazy)
    participant IPC as IPC Handlers
    participant Preload as Preload
    participant Renderer as Renderer
    participant StartupState as Startup State

    App->>Main: app.whenReady()
    Main->>IPC: registerIpcHandlers(deps)
    Main->>Main: registerRendererProtocol()
    Main->>Main: createMainWindow()
    Main->>Preload: Window created with preload

    Renderer->>StartupState: initialize()
    StartupState->>Preload: window.api.getWindowConfig()
    Preload->>IPC: ipcRenderer.invoke("get-window-config")
    IPC->>Store: deps.getWindowConfig()
    Store-->>IPC: { updaterEnabled, wslEnabled }
    IPC-->>Preload: config
    Preload-->>StartupState: config

    par Deep Links Fetch
        StartupState->>Preload: window.api.consumeInitialDeepLinks()
        Preload->>IPC: ipcRenderer.invoke("consume-initial-deep-links")
        IPC->>Main: deps.consumeInitialDeepLinks()
        Main-->>IPC: string[]
        IPC-->>Preload: deep links
        Preload-->>StartupState: deep links
    end

    StartupState->>StartupState: ready.resolve()
    Renderer->>StartupState: await ready
    Renderer->>Renderer: render() with startupState
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 Protocol schemes and IPC flows so bright,
Lazy stores that wake when needed right,
Sandboxes strong with preload's secure care,
Configuration dances through the air,
Deep links consumed, no globals here—
A renderer protocol draws ever near! 🎯

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(desktop): harden Electron Windows runtime' directly and clearly describes the main change: hardening Electron runtime security on Windows through process-layer improvements.
Description check ✅ Passed The description is comprehensive and follows the template well, with clear Summary, Why, Related Issue, How To Verify sections, and a completed Checklist addressing all major template items.
Linked Issues check ✅ Passed The PR successfully implements the Electron/process-layer hardening track of issue #141 through lazy-loaded electron-store, preload IPC for startup data, sandbox/contextIsolation enablement, and privileged renderer protocol with restricted CORS—all aligned with the stated objectives and scope.
Out of Scope Changes check ✅ Passed All changes remain scoped to Electron/process-layer hardening as intended. The PR explicitly excludes opencode-layer work (#148), ARM64 support, and upstream PRs #23633/#23396, maintaining focus on the stated objective.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-i141-electron-hardening

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

@Astro-Han Astro-Han force-pushed the codex/fix-i141-electron-hardening branch from 582352d to d290e88 Compare April 22, 2026 17:24
@Astro-Han Astro-Han marked this pull request as ready for review April 22, 2026 17:26

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

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

Inline comments:
In `@packages/desktop-electron/src/main/renderer-protocol.test.ts`:
- Around line 20-30: Add a test in renderer-protocol.test.ts that passes
malformed/invalid URL inputs to resolveRendererFile and asserts it returns null
(not throwing); specifically call resolveRendererFile(root, "not-a-url") and
maybe other bad inputs like an empty string or a truncated scheme (e.g.,
"pawwork-renderer:/bad") and assert toBeNull for each, ensuring
resolveRendererFile handles URL parsing failures gracefully.

In `@packages/desktop-electron/src/main/renderer-protocol.ts`:
- Around line 11-27: In resolveRendererFile, after decoding the pathname with
decodeURIComponent(url.pathname) add an explicit null-byte check (e.g., if
(path.includes('\0')) return null) to reject inputs containing %00 before any
fs/path operations; update or add unit tests that call resolveRendererFile with
null-byte attempts (for example "pawwork-renderer://renderer/index.html%00.js")
to assert it returns null.

In `@packages/desktop-electron/src/main/store.test.ts`:
- Around line 27-34: The failing assertion is caused by shared cached
default-store state; update the test to use a test-unique store name when
calling getStore so constructorCalls is isolated per test (e.g., generate a
unique string and pass it into getStore instead of relying on the default), then
assert constructorCalls transitions 0 -> 1 accordingly; locate and modify the
test invoking getStore and the constructorCalls assertions in store.test.ts to
supply a unique name for each run.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 4b692009-d89b-454a-9c9f-9a1b7a42537a

📥 Commits

Reviewing files that changed from the base of the PR and between 3ff9c0b and d290e88.

📒 Files selected for processing (22)
  • packages/desktop-electron/electron.vite.config.ts
  • packages/desktop-electron/src/main/index.ts
  • packages/desktop-electron/src/main/ipc-window-config.test.ts
  • packages/desktop-electron/src/main/ipc.ts
  • packages/desktop-electron/src/main/renderer-protocol.test.ts
  • packages/desktop-electron/src/main/renderer-protocol.ts
  • packages/desktop-electron/src/main/server-store.test.ts
  • packages/desktop-electron/src/main/server.test.ts
  • packages/desktop-electron/src/main/server.ts
  • packages/desktop-electron/src/main/store.test.ts
  • packages/desktop-electron/src/main/store.ts
  • packages/desktop-electron/src/main/windows-protocol.test.ts
  • packages/desktop-electron/src/main/windows-security.test.ts
  • packages/desktop-electron/src/main/windows.ts
  • packages/desktop-electron/src/preload/index.ts
  • packages/desktop-electron/src/preload/types.ts
  • packages/desktop-electron/src/renderer/env.d.ts
  • packages/desktop-electron/src/renderer/html.test.ts
  • packages/desktop-electron/src/renderer/index.tsx
  • packages/desktop-electron/src/renderer/startup-state.test.ts
  • packages/desktop-electron/src/renderer/startup-state.ts
  • packages/desktop-electron/src/renderer/updater.ts
💤 Files with no reviewable changes (2)
  • packages/desktop-electron/src/main/store.ts
  • packages/desktop-electron/src/renderer/env.d.ts

Comment thread packages/desktop-electron/src/main/renderer-protocol.test.ts
Comment thread packages/desktop-electron/src/main/renderer-protocol.ts
Comment thread packages/desktop-electron/src/main/store.test.ts

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Nits only; the three-commit slice is well scoped and the threat model is coherent. Findings below are mostly defensive-hardening gaps and test-strength nitpicks.

Comment thread packages/desktop-electron/src/main/renderer-protocol.ts
Comment thread packages/desktop-electron/src/main/renderer-protocol.ts Outdated
Comment thread packages/desktop-electron/src/main/renderer-protocol.ts Outdated
Comment thread packages/desktop-electron/src/main/windows.ts Outdated
Comment thread packages/desktop-electron/src/main/windows-protocol.test.ts
Comment thread packages/desktop-electron/src/renderer/startup-state.ts Outdated
Comment thread packages/desktop-electron/src/renderer/startup-state.ts Outdated
Comment thread packages/desktop-electron/src/renderer/index.tsx
Comment thread packages/desktop-electron/src/main/server.ts
Comment thread packages/desktop-electron/src/renderer/startup-state.ts Outdated
@Astro-Han Astro-Han force-pushed the codex/fix-i141-electron-hardening branch 2 times, most recently from 366455c to c7e7496 Compare April 22, 2026 18:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working P1 High priority platform Electron shell, OS integration, packaging, updater, signing, paths, and permissions upstream Tracked upstream or vendor behavior windows Windows-specific

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve Windows compatibility and restore Windows unit signal

1 participant