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:
+38
-9
@@ -10,6 +10,15 @@
|
||||
const SENTINEL = 'enc:v1:';
|
||||
const CRED_FIELDS = ['password', 'apiKey'];
|
||||
|
||||
class SecretStoreError extends Error {
|
||||
constructor(code, message, cause) {
|
||||
super(message);
|
||||
this.name = 'SecretStoreError';
|
||||
this.code = code;
|
||||
if (cause !== undefined) this.cause = cause;
|
||||
}
|
||||
}
|
||||
|
||||
let _safeStorageCache = undefined;
|
||||
function getSafeStorage() {
|
||||
if (_safeStorageCache !== undefined) return _safeStorageCache;
|
||||
@@ -29,16 +38,24 @@ function isEncrypted(value) {
|
||||
return typeof value === 'string' && value.startsWith(SENTINEL);
|
||||
}
|
||||
|
||||
function encryptField(value) {
|
||||
function getAvailabilityStatus() {
|
||||
return getSafeStorage() ? 'available' : 'unavailable';
|
||||
}
|
||||
|
||||
function encryptField(value, options = {}) {
|
||||
if (!value || typeof value !== 'string') return value;
|
||||
if (isEncrypted(value)) return value;
|
||||
const ss = getSafeStorage();
|
||||
if (!ss) return value;
|
||||
if (!ss) {
|
||||
if (options.allowPlaintext === true) return value;
|
||||
throw new SecretStoreError('SECRET_STORE_UNAVAILABLE', 'Sicherer Zugangsdaten-Speicher ist nicht verfügbar');
|
||||
}
|
||||
try {
|
||||
const buf = ss.encryptString(value);
|
||||
return SENTINEL + buf.toString('base64');
|
||||
} catch {
|
||||
return value;
|
||||
} catch (cause) {
|
||||
if (options.allowPlaintext === true) return value;
|
||||
throw new SecretStoreError('SECRET_STORE_ENCRYPT_FAILED', 'Zugangsdaten konnten nicht sicher verschlüsselt werden', cause);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,12 +63,14 @@ function decryptField(value) {
|
||||
if (!value || typeof value !== 'string') return value;
|
||||
if (!isEncrypted(value)) return value;
|
||||
const ss = getSafeStorage();
|
||||
if (!ss) return '';
|
||||
if (!ss) {
|
||||
throw new SecretStoreError('SECRET_STORE_UNAVAILABLE', 'Sicherer Zugangsdaten-Speicher ist nicht verfügbar');
|
||||
}
|
||||
try {
|
||||
const buf = Buffer.from(value.slice(SENTINEL.length), 'base64');
|
||||
return ss.decryptString(buf);
|
||||
} catch {
|
||||
return '';
|
||||
} catch (cause) {
|
||||
throw new SecretStoreError('SECRET_STORE_DECRYPT_FAILED', 'Gespeicherte Zugangsdaten konnten nicht entschlüsselt werden', cause);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +88,17 @@ function mapHosterAccounts(config, fn) {
|
||||
return config;
|
||||
}
|
||||
|
||||
function encryptCredentials(config) { return mapHosterAccounts(config, encryptField); }
|
||||
function encryptCredentials(config, options = {}) {
|
||||
return mapHosterAccounts(config, value => encryptField(value, options));
|
||||
}
|
||||
function decryptCredentials(config) { return mapHosterAccounts(config, decryptField); }
|
||||
|
||||
module.exports = { encryptField, decryptField, encryptCredentials, decryptCredentials, isEncrypted };
|
||||
module.exports = {
|
||||
SecretStoreError,
|
||||
getAvailabilityStatus,
|
||||
encryptField,
|
||||
decryptField,
|
||||
encryptCredentials,
|
||||
decryptCredentials,
|
||||
isEncrypted
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user