From 816f675d9045bc2f38d17e23b11f600cfa1e1631 Mon Sep 17 00:00:00 2001 From: Administrator Date: Sat, 21 Mar 2026 15:05:33 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix:=20broken=20tests,=20empty?= =?UTF-8?q?=20password=20validation,=20asset=20URL=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix 3 failing config-store tests: update expectations to match multi-account array format (tests passed with old single-object format) - backup-crypto: reject empty/null passwords on encrypt+decrypt instead of producing weak keys silently - updater: validate assetUrl and assetName before downloading to prevent crash on incomplete update metadata All 59 tests now passing. Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/backup-crypto.js | 2 ++ lib/updater.js | 3 +++ tests/config-store.test.js | 22 ++++++++++++---------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/backup-crypto.js b/lib/backup-crypto.js index 6e18bc8..48d3a47 100644 --- a/lib/backup-crypto.js +++ b/lib/backup-crypto.js @@ -18,6 +18,7 @@ function deriveKey(password, salt) { * Returns a Buffer: MHU1 | salt(16) | iv(12) | tag(16) | ciphertext */ function encrypt(config, password) { + if (!password || typeof password !== 'string') throw new Error('Passwort darf nicht leer sein'); const plaintext = Buffer.from(JSON.stringify(config), 'utf-8'); const salt = crypto.randomBytes(SALT_LEN); const iv = crypto.randomBytes(IV_LEN); @@ -37,6 +38,7 @@ function encrypt(config, password) { * Returns the config object or throws on invalid password/data. */ function decrypt(buffer, password) { + if (!password || typeof password !== 'string') throw new Error('Passwort darf nicht leer sein'); if (buffer.length < MAGIC.length + SALT_LEN + IV_LEN + TAG_LEN + 1) { throw new Error('Ungültiges Backup-Format'); } diff --git a/lib/updater.js b/lib/updater.js index a9fc8b6..1dfbd7c 100644 --- a/lib/updater.js +++ b/lib/updater.js @@ -152,6 +152,9 @@ async function installUpdate(onProgress) { if (!check || !check.available) { throw new Error('Kein Update verfuegbar'); } + if (!check.assetUrl || !check.assetName) { + throw new Error('Update-Asset unvollstaendig (URL oder Name fehlt)'); + } // Stage: downloading const tmpDir = app.getPath('temp'); diff --git a/tests/config-store.test.js b/tests/config-store.test.js index 5dcf9c1..f62e24a 100644 --- a/tests/config-store.test.js +++ b/tests/config-store.test.js @@ -51,22 +51,24 @@ describe('ConfigStore', () => { }); it('save then load round-trips', async () => { - await store.save({ hosters: { 'doodstream.com': { enabled: true, apiKey: 'test-key-123' } } }); + await store.save({ hosters: { 'doodstream.com': [{ id: 'test-1', enabled: true, authType: 'api', apiKey: 'test-key-123' }] } }); const config = store.load(); - assert.equal(config.hosters['doodstream.com'].apiKey, 'test-key-123'); + assert.equal(config.hosters['doodstream.com'][0].apiKey, 'test-key-123'); }); it('load merges with defaults for missing hosters', () => { - // Write partial config + // Write partial config in old single-object format (triggers migration) fs.writeFileSync(store.filePath, JSON.stringify({ hosters: { 'doodstream.com': { apiKey: 'abc' } } }), 'utf-8'); const config = store.load(); - assert.equal(config.hosters['doodstream.com'].apiKey, 'abc'); - // Other hosters should still have defaults - assert.equal(config.hosters['voe.sx'].enabled, true); - assert.equal(config.hosters['voe.sx'].apiKey, ''); + // Old format is migrated to array + assert.ok(Array.isArray(config.hosters['doodstream.com'])); + assert.equal(config.hosters['doodstream.com'][0].apiKey, 'abc'); + // Other hosters should still have defaults (empty arrays) + assert.ok(Array.isArray(config.hosters['voe.sx'])); + assert.equal(config.hosters['voe.sx'].length, 0); }); it('hosterSettings merge fills gaps with defaults', () => { @@ -83,11 +85,11 @@ describe('ConfigStore', () => { it('save only updates provided sections', async () => { // Save hoster settings first await store.save({ hosterSettings: { 'doodstream.com': { retries: 10, maxSpeedKbs: 0, parallelCount: 2, restartBelowKbs: 0, timeIntervalSec: 0, maxSizeMb: 0 } } }); - // Save hosters credentials separately - await store.save({ hosters: { 'doodstream.com': { enabled: true, apiKey: 'key123' } } }); + // Save hosters credentials separately (array format) + await store.save({ hosters: { 'doodstream.com': [{ id: 'test-1', enabled: true, authType: 'api', apiKey: 'key123' }] } }); const config = store.load(); - assert.equal(config.hosters['doodstream.com'].apiKey, 'key123'); + assert.equal(config.hosters['doodstream.com'][0].apiKey, 'key123'); assert.equal(config.hosterSettings['doodstream.com'].retries, 10); // preserved });