release: v2.1.12 reliability and security hardening
CI / verify (push) Waiting to run

Verify update artifacts with exact metadata and SHA-512, require host-confirmed upload completion before cleanup, harden credentials and backups, improve queue recovery and skipped-state reporting, expand Windows path coverage, and add CI packaging checks.
This commit is contained in:
Sucukdeluxe
2026-08-11 22:36:21 +02:00
parent 2c124c848c
commit 2ba0106ef0
47 changed files with 2069 additions and 1192 deletions
+108
View File
@@ -0,0 +1,108 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const Module = require('node:module');
const secretStorePath = require.resolve('../lib/secret-store');
function withSecretStore(safeStorage, action) {
const originalLoad = Module._load;
Module._load = function load(request, parent, isMain) {
if (request === 'electron') return { safeStorage };
return originalLoad.call(this, request, parent, isMain);
};
delete require.cache[secretStorePath];
try {
return action(require(secretStorePath));
} finally {
Module._load = originalLoad;
delete require.cache[secretStorePath];
}
}
function availableSafeStorage(overrides = {}) {
return {
isEncryptionAvailable: () => true,
encryptString: value => Buffer.from(`protected:${value}`),
decryptString: value => value.toString().replace(/^protected:/, ''),
...overrides
};
}
test('reports whether secure credential storage is available', () => {
withSecretStore(availableSafeStorage(), secretStore => {
assert.equal(secretStore.getAvailabilityStatus(), 'available');
});
withSecretStore(null, secretStore => {
assert.equal(secretStore.getAvailabilityStatus(), 'unavailable');
});
});
test('encrypts and decrypts fields when secure storage is available', () => {
withSecretStore(availableSafeStorage(), secretStore => {
const encrypted = secretStore.encryptField('secret');
assert.match(encrypted, /^enc:v1:/);
assert.equal(secretStore.decryptField(encrypted), 'secret');
});
});
test('refuses plaintext storage by default when secure storage is unavailable', () => {
withSecretStore(null, secretStore => {
assert.throws(
() => secretStore.encryptField('secret'),
error => error instanceof secretStore.SecretStoreError
&& error.code === 'SECRET_STORE_UNAVAILABLE'
);
});
});
test('allows plaintext storage only through an explicit opt-in', () => {
withSecretStore(null, secretStore => {
assert.equal(secretStore.encryptField('secret', { allowPlaintext: true }), 'secret');
const config = { hosters: { example: [{ password: 'secret' }] } };
assert.equal(
secretStore.encryptCredentials(config, { allowPlaintext: true }).hosters.example[0].password,
'secret'
);
});
});
test('refuses plaintext storage by default when encryption fails', () => {
const failure = new Error('encryption failed');
withSecretStore(availableSafeStorage({ encryptString: () => { throw failure; } }), secretStore => {
assert.throws(
() => secretStore.encryptField('secret'),
error => error instanceof secretStore.SecretStoreError
&& error.code === 'SECRET_STORE_ENCRYPT_FAILED'
&& error.cause === failure
);
assert.equal(secretStore.encryptField('secret', { allowPlaintext: true }), 'secret');
});
});
test('throws an identifiable error for encrypted values without secure storage', () => {
withSecretStore(null, secretStore => {
assert.throws(
() => secretStore.decryptField('enc:v1:cHJvdGVjdGVkOnNlY3JldA=='),
error => error instanceof secretStore.SecretStoreError
&& error.code === 'SECRET_STORE_UNAVAILABLE'
);
});
});
test('throws an identifiable error when decryption fails', () => {
const failure = new Error('decryption failed');
withSecretStore(availableSafeStorage({ decryptString: () => { throw failure; } }), secretStore => {
assert.throws(
() => secretStore.decryptField('enc:v1:invalid'),
error => error instanceof secretStore.SecretStoreError
&& error.code === 'SECRET_STORE_DECRYPT_FAILED'
&& error.cause === failure
);
});
});
test('keeps legacy plaintext values readable without secure storage', () => {
withSecretStore(null, secretStore => {
assert.equal(secretStore.decryptField('legacy-secret'), 'legacy-secret');
});
});