Q120: every IPC call passes one gate - the app's own page, input checked against a typed contract
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
43591d4c99
commit
04c8cce0c4
10 files changed
+435
-103
No files matched your search
@@ -1,10 +1,21 @@
|
||||
import type { BrowserWindow } from "electron";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
export function attachMainWindowSecurity(win: BrowserWindow, devServerOrigin: string | null): void {
|
||||
/** Whether a URL is the app's own page: the exact index.html it was built with, or any route of the dev server it is served by. */
|
||||
export function isAppPage(url: string, appPage: string): boolean {
|
||||
if (!URL.canParse(url)) return false;
|
||||
const target = new URL(url);
|
||||
const page = new URL(appPage);
|
||||
if (page.protocol !== "file:") return target.origin === page.origin;
|
||||
if (target.protocol !== "file:") return false;
|
||||
const path = (value: URL): string => (process.platform === "win32" ? fileURLToPath(value).toLowerCase() : fileURLToPath(value));
|
||||
return path(target) === path(page);
|
||||
}
|
||||
|
||||
export function attachMainWindowSecurity(win: BrowserWindow, appPage: string): void {
|
||||
win.webContents.setWindowOpenHandler(() => ({ action: "deny" }));
|
||||
const blockNavigation = (event: Electron.Event, url: string): void => {
|
||||
if (devServerOrigin && url.startsWith(devServerOrigin)) return;
|
||||
if (url.startsWith("file://")) return;
|
||||
if (isAppPage(url, appPage)) return;
|
||||
event.preventDefault();
|
||||
};
|
||||
win.webContents.on("will-navigate", blockNavigation);
|
||||
|
||||
Reference in new issue
Block a user