security: scheme-validate URLs handed to shell.openExternal
The open-external IPC was a pass-through:
ipcMain.handle("open-external", async (_, url) =>
await shell.openExternal(url));
shell.openExternal on Windows happily resolves any URL scheme the OS
knows how to launch — including file:// paths, ms-settings:, shell:,
javascript:, and assorted protocol handlers. The renderer is
contextIsolated + nodeIntegration: false so direct exploits are
blocked, but an XSS landing through (for example) a streamer name
that smuggled HTML into a renderer template would have a clean path
through this IPC to launch arbitrary local executables via the OS
shell.
Validation gate: reject anything that isn't an http:// or https://
URL. Trim before the test so a smuggled leading/trailing whitespace
attempt does not slip through. Rejected requests get a debug-log
entry (truncated to 200 chars so a megabyte payload doesnt nuke the
log) and return silently — the renderer caller already swallows
the promise without checking, so silent-drop matches existing
behaviour.
Defence-in-depth. No known active exploit; just removing an
unnecessary surface.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7e60d0e920
commit
c6f423b5ac
15
src/main.ts
15
src/main.ts
@ -6975,7 +6975,20 @@ ipcMain.handle('install-update', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('open-external', async (_, url: string) => {
|
ipcMain.handle('open-external', async (_, url: string) => {
|
||||||
await shell.openExternal(url);
|
// Only allow https / http URLs — never let the renderer push a
|
||||||
|
// file://, javascript:, or shell:-style URL through to the OS
|
||||||
|
// shell.openExternal handler. The renderer is contextIsolated +
|
||||||
|
// nodeIntegration: false, but an XSS through (e.g.) a streamer name
|
||||||
|
// smuggling a payload into a template would otherwise hand the
|
||||||
|
// attacker shell.openExternal which on Windows happily resolves
|
||||||
|
// file:///C:/Windows/System32/calc.exe.
|
||||||
|
if (typeof url !== 'string') return;
|
||||||
|
const trimmed = url.trim();
|
||||||
|
if (!/^https?:\/\//i.test(trimmed)) {
|
||||||
|
appendDebugLog('open-external-rejected', { url: trimmed.slice(0, 200) });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await shell.openExternal(trimmed);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Tracks active standalone clip downloads so cancel-download / window-all-closed
|
// Tracks active standalone clip downloads so cancel-download / window-all-closed
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user