From 6cd7498f70e6f28b226c5d968b2deec8d6c012c9 Mon Sep 17 00:00:00 2001 From: Administrator Date: Mon, 8 Jun 2026 22:03:19 +0200 Subject: [PATCH] fix(critical): safeSend infinite recursion + queueMicrotask, plus 6 audit findings --- lib/config-store.js | 7 ++++++- lib/upload-manager.js | 2 ++ lib/vidmoly-upload.js | 2 +- main.js | 5 +++-- renderer/app.js | 29 ++++++++++++++++++++++++++--- 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/lib/config-store.js b/lib/config-store.js index ca0be23..18a9da6 100644 --- a/lib/config-store.js +++ b/lib/config-store.js @@ -277,7 +277,12 @@ class ConfigStore { if (fs.existsSync(this.filePath)) { const existing = fs.readFileSync(this.filePath, 'utf-8'); if (existing && existing.trim().length > 2) { - fs.writeFileSync(backupPath, existing, 'utf-8'); + let isValid = false; + try { + const parsed = JSON.parse(existing); + isValid = parsed && typeof parsed === 'object' && (parsed.hosters || parsed.hosterSettings || parsed.globalSettings); + } catch {} + if (isValid) fs.writeFileSync(backupPath, existing, 'utf-8'); } } } catch {} diff --git a/lib/upload-manager.js b/lib/upload-manager.js index ce80b08..f03d2c8 100644 --- a/lib/upload-manager.js +++ b/lib/upload-manager.js @@ -316,6 +316,7 @@ class UploadManager extends EventEmitter { const DEDUP_CHUNK = 200; for (let i = 0; i < tasks.length; i += DEDUP_CHUNK) { + if (signal.aborted) break; const end = Math.min(i + DEDUP_CHUNK, tasks.length); for (let j = i; j < end; j++) { const task = tasks[j]; @@ -334,6 +335,7 @@ class UploadManager extends EventEmitter { const SPAWN_CHUNK = 100; const promises = []; for (let i = 0; i < tasks.length; i += SPAWN_CHUNK) { + if (signal.aborted) break; const end = Math.min(i + SPAWN_CHUNK, tasks.length); for (let j = i; j < end; j++) promises.push(this._runJob(tasks[j], results, signal)); if (end < tasks.length) await new Promise(setImmediate); diff --git a/lib/vidmoly-upload.js b/lib/vidmoly-upload.js index bdcc6c1..608fcaf 100644 --- a/lib/vidmoly-upload.js +++ b/lib/vidmoly-upload.js @@ -382,7 +382,7 @@ class VidmolyUploader { } } - if (best && (bestScore > 0 || newFiles.length === 1)) { + if (best && bestScore > 0) { return this._buildUrlsFromCode(best.file_code); } } diff --git a/main.js b/main.js index 7672d77..f7ecb38 100644 --- a/main.js +++ b/main.js @@ -231,7 +231,7 @@ function rotLog(msg, ts) { function safeSend(channel, data) { if (!mainWindow || mainWindow.isDestroyed()) return false; try { - safeSend(channel, data); + mainWindow.webContents.send(channel, data); return true; } catch (err) { debugLog(`safeSend(${channel}) failed: ${err && err.message ? err.message : err}`); @@ -1155,7 +1155,8 @@ app.on('before-quit', () => { if (remoteServer) { remoteServer.stop(); remoteServer = null; } destroyCaptureWindow(); } catch {} - destroyDropTargetWindow(); + try { destroyDropTargetWindow(); } catch {} + try { if (tray && !tray.isDestroyed()) { tray.destroy(); tray = null; } } catch {} // Flush pending log buffers synchronously so no lines are lost. try { if (_debugLogBuffer.length) { diff --git a/renderer/app.js b/renderer/app.js index 0c9d76e..cae23af 100644 --- a/renderer/app.js +++ b/renderer/app.js @@ -2500,7 +2500,7 @@ function _computeQueueStats() { } _queueStatsCache = { total, remaining, inProgress, done, errors, bytesRemaining, totalSize, remainingSize, inProgressBytes }; - queueMicrotask(() => { _queueStatsCache = null; }); + (typeof queueMicrotask === 'function' ? queueMicrotask : (fn) => Promise.resolve().then(fn))(() => { _queueStatsCache = null; }); return _queueStatsCache; } @@ -3691,7 +3691,10 @@ async function deleteAccount(accountId) { // Fire-and-forget the persist. The earlier `await getConfig()` round-trip // was redundant (we already have the truth in memory) and was the main // source of perceived lag on add/delete. - window.api.saveConfig({ hosters: config.hosters }).catch(() => {}); + window.api.saveConfig({ hosters: config.hosters }).catch((err) => { + if (window.api && window.api.debugLog) window.api.debugLog(`deleteAccount saveConfig failed: ${err && err.message ? err.message : err}`); + showCopyToast('Account-Löschung konnte nicht persistiert werden — bitte erneut versuchen.'); + }); } function readAccountCredsFromModal(authType) { @@ -3897,6 +3900,15 @@ async function _commitAccount(ctx, creds, validatedStatus, validatedMessage) { const idx = config.hosters[ctx.hosterName].findIndex(a => a.id === accountId); if (idx >= 0) { config.hosters[ctx.hosterName][idx] = { ...config.hosters[ctx.hosterName][idx], ...creds }; + } else { + _accountModalBusy = false; + const _sb = document.getElementById('saveAccountBtn'); if (_sb) _sb.disabled = false; + const _st = document.getElementById('accountModalStatus'); + if (_st) { + _st.textContent = 'Account nicht mehr in der Config — wurde extern gelöscht. Modal schließen und neu anlegen.'; + _st.className = 'account-modal-status error'; + } + return; } } else { accountId = `${ctx.hosterName}-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`; @@ -4844,4 +4856,15 @@ function updateStatsPanel() { } // --- Start --- -init(); +init().catch((err) => { + try { + if (window.api && window.api.debugLog) window.api.debugLog(`init failed: ${err && err.stack ? err.stack : err}`); + const root = document.getElementById('app') || document.body; + if (root) { + const banner = document.createElement('div'); + banner.style.cssText = 'position:fixed;top:0;left:0;right:0;background:#5a1e1e;color:#fff;padding:8px;z-index:99999;font-family:sans-serif;font-size:13px'; + banner.textContent = 'Initialisierung fehlgeschlagen: ' + (err && err.message ? err.message : err) + ' — bitte Diagnose-Paket exportieren oder Programm neu starten.'; + root.appendChild(banner); + } + } catch {} +});