diff --git a/SOURCE_MANIFEST.txt b/SOURCE_MANIFEST.txt index 55b52aa..6d804b6 100644 --- a/SOURCE_MANIFEST.txt +++ b/SOURCE_MANIFEST.txt @@ -141,7 +141,7 @@ c2c9e4ba251d93a530a52b2d0079787680261c314083bb99d2356fc177719613 2434 c8780510ff6e77a16e9c4a88c3c981a0d9d1a4dff8eafd16afdeefbb4b03aac9 1064 SECURITY.md 619515f524cb89960370ffcbd3fafd3c0e178b95f69c5868b1dd44777f23ec1e 2081 setup-windows.ps1 dd613d04b366f2cd071a1685a414016a5fb008082ed1b4cb8b24b79c100f640a 2412 src/main/audit-service.cjs -b8c3a41b9a49bc1c0387813737247bf86c940d525131f00da7192a5b8c99aa8d 34144 src/main/config-store.cjs +e3af59fafa497d032541979bea5fe2b98187b1ced619c567d15ae79ee0904c1f 34629 src/main/config-store.cjs 2fb04b1494b39f5d7c0720fa5fd298cd46fa85dc1b696d77657592347fcf1819 2731 src/main/configuration-backup.cjs 86e9fc2eda66b4b563f6c4bbb87d3e8514340d484fb503b73137e63b6b05c3c9 14597 src/main/deploy-key-lifecycle-service.cjs 7cbfe51973d6607203cb197652ed7f296a3f6b6b644df876957117866a47d802 2159 src/main/deployment-identity.cjs diff --git a/src/main/config-store.cjs b/src/main/config-store.cjs index 82a7315..0e206b8 100644 --- a/src/main/config-store.cjs +++ b/src/main/config-store.cjs @@ -3,9 +3,21 @@ const fs = require('node:fs/promises'); const path = require('node:path'); const crypto = require('node:crypto'); -const { safeStorage } = require('electron'); const { normalizeBaseUrl, assertHttpUrl, assertWorkflowFileName, assertBranchName, assertEnvironmentName, assertCloneRemote, assertRepositoryRelativePath, assertRepositoryRelativePaths } = require('../shared/validation.cjs'); +let cachedSafeStorage; + +function getSafeStorage() { + if (cachedSafeStorage !== undefined) return cachedSafeStorage; + try { + const electron = require('electron'); + cachedSafeStorage = electron && typeof electron === 'object' ? electron.safeStorage || null : null; + } catch { + cachedSafeStorage = null; + } + return cachedSafeStorage; +} + const DEFAULT_CONFIG = { schemaVersion: 13, setupComplete: false, @@ -215,6 +227,7 @@ class ConfigStore { return { persistent: true, preserved: false }; } + const safeStorage = getSafeStorage(); if (safeStorage?.isEncryptionAvailable?.()) { this.data.gitea.encryptedToken = safeStorage.encryptString(value).toString('base64'); this.sessionToken = null; @@ -230,6 +243,7 @@ class ConfigStore { if (this.sessionToken) return this.sessionToken; if (!this.data.gitea.encryptedToken) return ''; try { + const safeStorage = getSafeStorage(); return safeStorage?.decryptString?.(Buffer.from(this.data.gitea.encryptedToken, 'base64')) || ''; } catch { return ''; @@ -240,6 +254,7 @@ class ConfigStore { encryptSecret(value) { const text = String(value || ''); if (!text) return null; + const safeStorage = getSafeStorage(); if (!safeStorage?.isEncryptionAvailable?.()) { const error = new Error('Secure credential storage is unavailable. ForgeFlow will not persist server passwords or key passphrases.'); error.code = 'SECURE_STORAGE_UNAVAILABLE'; @@ -250,7 +265,10 @@ class ConfigStore { decryptSecret(value) { if (!value) return ''; - try { return safeStorage?.decryptString?.(Buffer.from(value, 'base64')) || ''; } + try { + const safeStorage = getSafeStorage(); + return safeStorage?.decryptString?.(Buffer.from(value, 'base64')) || ''; + } catch { return ''; } }