From cf6dce552efb479071d5d4428018b423d73e6c83 Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 9 Apr 2026 16:58:12 +0800 Subject: [PATCH 1/2] Fix security and reliability issues 1. Validate URL scheme in open-external-url handler - Prevent opening file:// or other dangerous schemes via shell.openExternal - Only allow http:, https:, and mailto: protocols 2. Fix latest video detection using mtime instead of lexicographic sort - Lexicographic sort gives wrong results (e.g. recording-9 > recording-10) - Now sorts by file modification time for reliable latest-file detection 3. Add null guard for AudioData.format in cloneWithTimestamp - Replace non-null assertion (!) with proper validation - Throws descriptive error if format is unexpectedly null 4. Prevent encodeQueue counter underflow in VideoExporter - Use Math.max(0, ...) to prevent negative queue count Co-Authored-By: Claude Opus 4.6 --- electron/ipc/handlers.ts | 31 ++++++++++++++++++++++++++++++- src/lib/exporter/audioEncoder.ts | 7 +++++-- src/lib/exporter/videoExporter.ts | 2 +- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/electron/ipc/handlers.ts b/electron/ipc/handlers.ts index 4cb4875..6aae4b4 100644 --- a/electron/ipc/handlers.ts +++ b/electron/ipc/handlers.ts @@ -490,7 +490,24 @@ export function registerIpcHandlers( return { success: false, message: "No recorded video found" }; } - const latestVideo = videoFiles.sort().reverse()[0]; + // Sort by most recently modified to reliably get the latest recording. + // Lexicographic sort is unreliable (e.g. recording-9.webm > recording-10.webm). + let latestVideo: string | null = null; + let latestMtimeMs = 0; + for (const file of videoFiles) { + try { + const stat = await fs.stat(path.join(RECORDINGS_DIR, file)); + if (stat.mtimeMs > latestMtimeMs) { + latestMtimeMs = stat.mtimeMs; + latestVideo = file; + } + } catch { + // Skip inaccessible files. + } + } + if (!latestVideo) { + return { success: false, message: "No recorded video found" }; + } const videoPath = path.join(RECORDINGS_DIR, latestVideo); return { success: true, path: videoPath }; @@ -616,6 +633,18 @@ export function registerIpcHandlers( ipcMain.handle("open-external-url", async (_, url: string) => { try { + const ALLOWED_SCHEMES = ["http:", "https:", "mailto:"]; + let parsed: URL; + try { + parsed = new URL(url); + } catch { + return { success: false, error: "Invalid URL" }; + } + + if (!ALLOWED_SCHEMES.includes(parsed.protocol)) { + return { success: false, error: `Unsupported URL scheme: ${parsed.protocol}` }; + } + await shell.openExternal(url); return { success: true }; } catch (error) { diff --git a/src/lib/exporter/audioEncoder.ts b/src/lib/exporter/audioEncoder.ts index 490eed2..fba3568 100644 --- a/src/lib/exporter/audioEncoder.ts +++ b/src/lib/exporter/audioEncoder.ts @@ -459,7 +459,10 @@ export class AudioProcessor { } private cloneWithTimestamp(src: AudioData, newTimestamp: number): AudioData { - const isPlanar = src.format?.includes("planar") ?? false; + if (!src.format) { + throw new Error("AudioData format is required for cloning"); + } + const isPlanar = src.format.includes("planar"); const numPlanes = isPlanar ? src.numberOfChannels : 1; let totalSize = 0; @@ -476,7 +479,7 @@ export class AudioProcessor { } return new AudioData({ - format: src.format!, + format: src.format, sampleRate: src.sampleRate, numberOfFrames: src.numberOfFrames, numberOfChannels: src.numberOfChannels, diff --git a/src/lib/exporter/videoExporter.ts b/src/lib/exporter/videoExporter.ts index d0affd1..2c6ea8d 100644 --- a/src/lib/exporter/videoExporter.ts +++ b/src/lib/exporter/videoExporter.ts @@ -422,7 +422,7 @@ export class VideoExporter { })(); this.muxingPromises.push(muxingPromise); - this.encodeQueue--; + this.encodeQueue = Math.max(0, this.encodeQueue - 1); }, error: (error) => { console.error("[VideoExporter] Encoder error:", error); From 721e8f47596ce3f5809b902c74841e6801050cf0 Mon Sep 17 00:00:00 2001 From: Test User Date: Sat, 18 Apr 2026 21:37:16 +0800 Subject: [PATCH 2/2] Fix lint, type check errors, and apply CodeRabbit review feedback - Remove trailing comma in SUPPORTED_LOCALES that caused Locale type to include undefined, fixing all downstream type errors - Remove unused webcamSizePreset from useMemo dependency array - Use parsed.toString() instead of raw url in shell.openExternal per Electron security best practice Co-Authored-By: Claude Opus 4.7 --- electron/ipc/handlers.ts | 2 +- src/components/video-editor/VideoEditor.tsx | 1 - src/i18n/config.ts | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/electron/ipc/handlers.ts b/electron/ipc/handlers.ts index 6aae4b4..5dffc84 100644 --- a/electron/ipc/handlers.ts +++ b/electron/ipc/handlers.ts @@ -645,7 +645,7 @@ export function registerIpcHandlers( return { success: false, error: `Unsupported URL scheme: ${parsed.protocol}` }; } - await shell.openExternal(url); + await shell.openExternal(parsed.toString()); return { success: true }; } catch (error) { console.error("Failed to open URL:", error); diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index 88c3aae..49ff1b5 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -484,7 +484,6 @@ export default function VideoEditor() { aspectRatio, webcamLayoutPreset, webcamMaskShape, - webcamSizePreset, webcamPosition, exportQuality, exportFormat, diff --git a/src/i18n/config.ts b/src/i18n/config.ts index 636d727..c576920 100644 --- a/src/i18n/config.ts +++ b/src/i18n/config.ts @@ -1,5 +1,5 @@ export const DEFAULT_LOCALE = "en" as const; -export const SUPPORTED_LOCALES = ["en", "zh-CN", "es", , "fr", "tr"] as const; +export const SUPPORTED_LOCALES = ["en", "zh-CN", "es", "fr", "tr"] as const; export const I18N_NAMESPACES = [ "common", "dialogs",