-
Notifications
You must be signed in to change notification settings - Fork 0
fix(electron): block untrusted window.open and navigations #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ import { | |
| BrowserWindow, | ||
| Menu, | ||
| ipcMain, | ||
| shell, | ||
| type IpcMainInvokeEvent, | ||
| type MenuItemConstructorOptions, | ||
| } from "electron" | ||
|
|
@@ -115,6 +116,35 @@ function assertTrustedIpcSender(event: IpcMainInvokeEvent) { | |
| assertTrustedUrl(senderUrl, "render IPC sender") | ||
| } | ||
|
|
||
| function isTrustedUrl(rawUrl: string) { | ||
| try { | ||
| assertTrustedUrl(rawUrl, "url") | ||
| return true | ||
| } catch { | ||
| return false | ||
| } | ||
| } | ||
|
|
||
| function hardenWindow(win: BrowserWindow) { | ||
| win.webContents.setWindowOpenHandler(({ url }) => { | ||
| if (isTrustedUrl(url)) { | ||
| return { action: "allow" } | ||
| } | ||
|
Comment on lines
+129
to
+132
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Returning Useful? React with 👍 / 👎.
Comment on lines
+130
to
+132
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The trust predicate returns true for any Useful? React with 👍 / 👎. |
||
| void shell.openExternal(url).catch(() => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This forwards every untrusted popup URL directly into Useful? React with 👍 / 👎. |
||
| // ignore – we already refused to open it inside the app | ||
| }) | ||
| return { action: "deny" } | ||
| }) | ||
|
|
||
| const guardNavigation = (event: Electron.Event, url: string) => { | ||
| if (!isTrustedUrl(url)) { | ||
| event.preventDefault() | ||
| } | ||
| } | ||
| win.webContents.on("will-navigate", guardNavigation) | ||
| win.webContents.on("will-redirect", guardNavigation) | ||
| } | ||
|
|
||
| function getBundledBinaryEnv(): NodeJS.ProcessEnv { | ||
| const env: NodeJS.ProcessEnv = {} | ||
| const ffmpegPath = | ||
|
|
@@ -564,6 +594,7 @@ async function createWindow() { | |
| contextIsolation: true, | ||
| }, | ||
| }) | ||
| hardenWindow(mainWindow) | ||
|
|
||
| if (useDevServer && process.env.VITE_DEV_SERVER_URL) { | ||
| assertTrustedUrl(process.env.VITE_DEV_SERVER_URL, "VITE_DEV_SERVER_URL") | ||
|
|
@@ -603,6 +634,7 @@ function createRenderSettingsWindow() { | |
| sandbox: false, | ||
| }, | ||
| }) | ||
| hardenWindow(renderSettingsWindow) | ||
| renderSettingsWindow.setMenu(null) | ||
| renderSettingsWindow.setMenuBarVisibility(false) | ||
|
|
||
|
|
@@ -641,6 +673,7 @@ function createRenderProgressWindow() { | |
| preload: resolveRenderPreloadPath(), | ||
| }, | ||
| }) | ||
| hardenWindow(renderProgressWindow) | ||
| renderProgressWindow.setMenu(null) | ||
| renderProgressWindow.setMenuBarVisibility(false) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This allow branch trusts any URL whose
originis"null", which includes schemes likedata:andjavascript:(not just your app files). BecauseisTrustedUrl()delegates toassertTrustedUrl(), a renderer can still open opaque-origin popup URLs inside Electron instead of being denied/externalized, which bypasses the new hardening goal for untrustedwindow.opentargets.Useful? React with 👍 / 👎.