fix(security): remove plaintext credential storage fallback
Credential writes now require Electron safeStorage encryption. Legacy plaintext values remain readable for migration and are encrypted during the next successful save. The UI override and status IPC are removed.
This commit is contained in:
+6
-7
@@ -73,7 +73,6 @@ const DEFAULTS = {
|
||||
// load() sets logMode after the merge, looking at the saved-only data.
|
||||
resumeQueueOnLaunch: true,
|
||||
autoStartRestoredQueue: false,
|
||||
allowPlaintextCredentialStorage: false,
|
||||
parallelUploadCount: 0, // 0 = use per-hoster limits only
|
||||
scaleParallelUploads: false,
|
||||
lastBrowseDirectory: '',
|
||||
@@ -182,7 +181,7 @@ function applyHistoryRetention(history, retention, nowMs) {
|
||||
}
|
||||
|
||||
class ConfigStore {
|
||||
constructor(app, options = {}) {
|
||||
constructor(app) {
|
||||
const useUserDataDir = app && (
|
||||
app.isPackaged ||
|
||||
(app.commandLine && typeof app.commandLine.hasSwitch === 'function' && app.commandLine.hasSwitch('user-data-dir'))
|
||||
@@ -201,7 +200,6 @@ class ConfigStore {
|
||||
this._cacheKey = '';
|
||||
this._perfLog = null;
|
||||
this._wqDepth = 0;
|
||||
this._allowPlaintextCredentialStorage = options.allowPlaintextCredentialStorage === true;
|
||||
|
||||
// Migrate config from old location if current doesn't exist
|
||||
if (!fs.existsSync(this.filePath) && app && app.isPackaged) {
|
||||
@@ -448,6 +446,7 @@ class ConfigStore {
|
||||
...DEFAULTS.globalSettings,
|
||||
...savedGlobal
|
||||
};
|
||||
delete globalSettings.allowPlaintextCredentialStorage;
|
||||
// Deep-merge nested objects so new keys are always present
|
||||
for (const key of Object.keys(DEFAULTS.globalSettings)) {
|
||||
const def = DEFAULTS.globalSettings[key];
|
||||
@@ -487,10 +486,10 @@ class ConfigStore {
|
||||
// on every write was a primary long-running main-thread stall.
|
||||
_serializeForDisk(config) {
|
||||
const hosters = this._clone(config.hosters || {});
|
||||
secretStore.encryptCredentials({ hosters }, {
|
||||
allowPlaintext: this._allowPlaintextCredentialStorage || config.globalSettings?.allowPlaintextCredentialStorage === true
|
||||
});
|
||||
return JSON.stringify({ ...config, hosters }, null, 2);
|
||||
const globalSettings = this._clone(config.globalSettings || {});
|
||||
delete globalSettings.allowPlaintextCredentialStorage;
|
||||
secretStore.encryptCredentials({ hosters });
|
||||
return JSON.stringify({ ...config, globalSettings, hosters }, null, 2);
|
||||
}
|
||||
|
||||
_commit(config) {
|
||||
|
||||
+3
-10
@@ -38,23 +38,17 @@ function isEncrypted(value) {
|
||||
return typeof value === 'string' && value.startsWith(SENTINEL);
|
||||
}
|
||||
|
||||
function getAvailabilityStatus() {
|
||||
return getSafeStorage() ? 'available' : 'unavailable';
|
||||
}
|
||||
|
||||
function encryptField(value, options = {}) {
|
||||
function encryptField(value) {
|
||||
if (!value || typeof value !== 'string') return value;
|
||||
if (isEncrypted(value)) return value;
|
||||
const ss = getSafeStorage();
|
||||
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 (cause) {
|
||||
if (options.allowPlaintext === true) return value;
|
||||
throw new SecretStoreError('SECRET_STORE_ENCRYPT_FAILED', 'Zugangsdaten konnten nicht sicher verschlüsselt werden', cause);
|
||||
}
|
||||
}
|
||||
@@ -88,14 +82,13 @@ function mapHosterAccounts(config, fn) {
|
||||
return config;
|
||||
}
|
||||
|
||||
function encryptCredentials(config, options = {}) {
|
||||
return mapHosterAccounts(config, value => encryptField(value, options));
|
||||
function encryptCredentials(config) {
|
||||
return mapHosterAccounts(config, encryptField);
|
||||
}
|
||||
function decryptCredentials(config) { return mapHosterAccounts(config, decryptField); }
|
||||
|
||||
module.exports = {
|
||||
SecretStoreError,
|
||||
getAvailabilityStatus,
|
||||
encryptField,
|
||||
decryptField,
|
||||
encryptCredentials,
|
||||
|
||||
Reference in New Issue
Block a user