fix(app): remove plaintext fallback from key vault when crypto unavailable

storeApiKey() now throws when encryption is not available instead of
storing keys in plaintext. ApiKeyManager.vue catches the error and
displays a warning message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-06 01:35:48 +00:00
co-authored by Claude Opus 4.6
parent 5c4afe00e5
commit 6720711302
2 changed files with 17 additions and 12 deletions
@@ -136,12 +136,17 @@ async function loadProviders() {
async function saveKey() {
if (!selectedProvider.value || !newKey.value.trim()) return
await storeApiKey(selectedProvider.value, newKey.value.trim())
statusMessage.value = `${PROVIDER_NAMES[selectedProvider.value] ?? selectedProvider.value} key saved`
statusError.value = false
newKey.value = ''
selectedProvider.value = ''
await loadProviders()
try {
await storeApiKey(selectedProvider.value, newKey.value.trim())
statusMessage.value = `${PROVIDER_NAMES[selectedProvider.value] ?? selectedProvider.value} key saved`
statusError.value = false
newKey.value = ''
selectedProvider.value = ''
await loadProviders()
} catch (err) {
statusMessage.value = err instanceof Error ? err.message : 'Failed to store key — encryption required'
statusError.value = true
}
}
async function removeKey(provider: string) {
+6 -6
View File
@@ -1,7 +1,7 @@
/**
* Encrypted API key vault using IndexedDB + AES-256-GCM.
* Keys are encrypted with the session key derived from passphrase.
* Falls back to plaintext storage when crypto is disabled (dev mode).
* Refuses to store keys when encryption is unavailable.
*/
import {
isCryptoEnabled,
@@ -38,13 +38,13 @@ export async function storeApiKey(provider: string, key: string): Promise<void>
if (typeof indexedDB === 'undefined') return
const db = await openDB()
const sessionKey = getSessionKey()
const useEncryption = isCryptoEnabled() && !!sessionKey
const value = useEncryption
? await encryptToString(key, sessionKey!)
: key
if (!isCryptoEnabled() || !sessionKey) {
throw new Error('Encryption required: cannot store API keys without an active session key. Please set a passphrase first.')
}
const record: VaultRecord = { provider, value, encrypted: useEncryption }
const value = await encryptToString(key, sessionKey)
const record: VaultRecord = { provider, value, encrypted: true }
return new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, 'readwrite')