Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
29 lines
1.5 KiB
TypeScript
29 lines
1.5 KiB
TypeScript
import type { BrowserWindow } from "electron";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
/** 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 (isAppPage(url, appPage)) return;
|
|
event.preventDefault();
|
|
};
|
|
win.webContents.on("will-navigate", blockNavigation);
|
|
win.webContents.on("will-redirect", blockNavigation);
|
|
// The first-party renderer never asks for camera, microphone, geolocation,
|
|
// notifications, and the like, so deny every permission outright — matching
|
|
// the provider sign-in windows — rather than leaving the default allow.
|
|
win.webContents.session.setPermissionRequestHandler((_webContents, _permission, callback) => callback(false));
|
|
win.webContents.session.setPermissionCheckHandler(() => false);
|
|
}
|