🐛 fix: broken tests, empty password validation, asset URL check

- 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) <noreply@anthropic.com>
This commit is contained in:
Administrator 2026-03-21 15:05:33 +01:00
parent 54daaf0410
commit 816f675d90
3 changed files with 17 additions and 10 deletions

View File

@ -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');
}

View File

@ -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');

View File

@ -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
});