API keys: skip undecryptable entries on load
APIKeyManager.load() decrypts every stored key with a dict comprehension and no error handling. If the .key file no longer matches the ciphertext in api_keys.json — key rotated, a partial/!mismatched data restore, or a corrupted .key — Fernet.decrypt raises cryptography.fernet.InvalidToken. app_initializer.py calls api_key_manager.load() during startup, so a single undecryptable entry takes down the whole app at boot, and the user can't reach the UI to fix it. Decrypt each key in a loop and, on InvalidToken/ValueError, log a warning and skip that one entry while still returning every key that decrypts cleanly. One bad/stale key no longer blocks startup. tests/test_api_key_manager_resilience.py saves a valid key, then injects an entry encrypted under a different Fernet key (InvalidToken) and a malformed token (ValueError), and asserts load() returns the good key and skips the bad ones without raising. Fails before this change.
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
from typing import Dict
|
||||
from cryptography.fernet import Fernet
|
||||
from cryptography.fernet import Fernet, InvalidToken
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class APIKeyManager:
|
||||
def __init__(self, data_dir: str):
|
||||
@@ -47,8 +50,12 @@ class APIKeyManager:
|
||||
return {}
|
||||
with open(self.api_keys_file, 'r', encoding="utf-8") as f:
|
||||
encrypted_keys = json.load(f)
|
||||
return {
|
||||
provider: self.decrypt_api_key(key)
|
||||
for provider, key in encrypted_keys.items()
|
||||
}
|
||||
|
||||
decrypted = {}
|
||||
for provider, key in encrypted_keys.items():
|
||||
try:
|
||||
decrypted[provider] = self.decrypt_api_key(key)
|
||||
except (InvalidToken, ValueError) as e:
|
||||
logger.warning("Failed to decrypt API key for %s: %s", provider, e)
|
||||
return decrypted
|
||||
|
||||
|
||||
Reference in New Issue
Block a user