From 7666a094f4462ef5ede32afb63549c53903e3b0a Mon Sep 17 00:00:00 2001 From: sudacode Date: Sun, 22 Mar 2026 18:34:16 -0700 Subject: [PATCH] fix: harden preload argv parsing for popup windows --- src/preload-args.test.ts | 16 ++++++++++++++++ src/preload-args.ts | 10 ++++++++++ src/preload.ts | 22 ++++++++++++++++++---- 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 src/preload-args.test.ts create mode 100644 src/preload-args.ts diff --git a/src/preload-args.test.ts b/src/preload-args.test.ts new file mode 100644 index 0000000..0eb3150 --- /dev/null +++ b/src/preload-args.test.ts @@ -0,0 +1,16 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { resolveOverlayLayerFromArgv } from './preload-args'; + +test('resolveOverlayLayerFromArgv returns null when argv is unavailable', () => { + assert.equal(resolveOverlayLayerFromArgv(null), null); +}); + +test('resolveOverlayLayerFromArgv returns parsed overlay layer when present', () => { + assert.equal(resolveOverlayLayerFromArgv(['electron', '--overlay-layer=modal']), 'modal'); + assert.equal(resolveOverlayLayerFromArgv(['electron', '--overlay-layer=visible']), 'visible'); +}); + +test('resolveOverlayLayerFromArgv ignores unsupported overlay layers', () => { + assert.equal(resolveOverlayLayerFromArgv(['electron', '--overlay-layer=secondary']), null); +}); diff --git a/src/preload-args.ts b/src/preload-args.ts new file mode 100644 index 0000000..a0d6ad3 --- /dev/null +++ b/src/preload-args.ts @@ -0,0 +1,10 @@ +export function resolveOverlayLayerFromArgv( + argv: readonly string[] | null | undefined, +): 'visible' | 'modal' | null { + const overlayLayerArg = argv?.find((arg) => arg.startsWith('--overlay-layer=')); + const overlayLayerFromArg = overlayLayerArg?.slice('--overlay-layer='.length); + + return overlayLayerFromArg === 'visible' || overlayLayerFromArg === 'modal' + ? overlayLayerFromArg + : null; +} diff --git a/src/preload.ts b/src/preload.ts index ba063ab..8d0299d 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -17,6 +17,7 @@ */ import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron'; +import { resolveOverlayLayerFromArgv } from './preload-args'; import type { SubtitleData, SubtitlePosition, @@ -51,13 +52,13 @@ import type { ControllerConfigUpdate, ControllerPreferenceUpdate, ResolvedControllerConfig, + YoutubePickerOpenPayload, + YoutubePickerResolveRequest, + YoutubePickerResolveResult, } from './types'; import { IPC_CHANNELS } from './shared/ipc/contracts'; -const overlayLayerArg = process.argv.find((arg) => arg.startsWith('--overlay-layer=')); -const overlayLayerFromArg = overlayLayerArg?.slice('--overlay-layer='.length); -const overlayLayer = - overlayLayerFromArg === 'visible' || overlayLayerFromArg === 'modal' ? overlayLayerFromArg : null; +const overlayLayer = resolveOverlayLayerFromArgv(process.argv); type EmptyListener = () => void; type PayloadedListener = (payload: T) => void; @@ -121,6 +122,13 @@ function createQueuedIpcListenerWithPayload( const onOpenRuntimeOptionsEvent = createQueuedIpcListener(IPC_CHANNELS.event.runtimeOptionsOpen); const onOpenJimakuEvent = createQueuedIpcListener(IPC_CHANNELS.event.jimakuOpen); +const onOpenYoutubeTrackPickerEvent = createQueuedIpcListenerWithPayload( + IPC_CHANNELS.event.youtubePickerOpen, + (payload) => payload as YoutubePickerOpenPayload, +); +const onCancelYoutubeTrackPickerEvent = createQueuedIpcListener( + IPC_CHANNELS.event.youtubePickerCancel, +); const onKeyboardModeToggleRequestedEvent = createQueuedIpcListener( IPC_CHANNELS.event.keyboardModeToggleRequested, ); @@ -313,10 +321,16 @@ const electronAPI: ElectronAPI = { }, onOpenRuntimeOptions: onOpenRuntimeOptionsEvent, onOpenJimaku: onOpenJimakuEvent, + onOpenYoutubeTrackPicker: onOpenYoutubeTrackPickerEvent, + onCancelYoutubeTrackPicker: onCancelYoutubeTrackPickerEvent, onKeyboardModeToggleRequested: onKeyboardModeToggleRequestedEvent, onLookupWindowToggleRequested: onLookupWindowToggleRequestedEvent, appendClipboardVideoToQueue: (): Promise => ipcRenderer.invoke(IPC_CHANNELS.request.appendClipboardVideoToQueue), + youtubePickerResolve: ( + request: YoutubePickerResolveRequest, + ): Promise => + ipcRenderer.invoke(IPC_CHANNELS.request.youtubePickerResolve, request), notifyOverlayModalClosed: (modal) => { ipcRenderer.send(IPC_CHANNELS.command.overlayModalClosed, modal); },