- Added a new "Prompt Builder" tab in the Popup for generating AI prompts with customizable parameters. - Introduced new state variables for managing prompt styles, personas, formats, and models. - Enhanced the Options page to fetch and display models based on the provided API key. - Updated the actions and types to include the new 'prompt' action and its associated parameters. - Implemented migration logic for legacy plaintext API keys to encrypted storage. - Updated the getSystemPrompt function to incorporate prompt parameters for better instruction generation. - Added tests for the new functionality, including context menu entries and prompt generation logic.
1.9 KiB
name, description, metadata
| name | description | metadata | ||
|---|---|---|---|---|
| threat-model-apikey | LexAI API-key at-rest threat model and the storage/migration invariants an auditor must preserve |
|
LexAI is a BYO-LLM-key MV3 extension with no backend. The user's provider API key is the crown-jewel secret.
Storage scheme (src/lib/crypto.ts): encKey = base64(32-byte secretbox key), apiKeyEnc = base64(nonce||ciphertext). Legacy plaintext apiKey is back-compat fallback and must not be dropped without a migration. migratePlaintextApiKey() runs on background startup: encrypts legacy plaintext into the secretbox scheme, removes plaintext only after the encrypted copy is persisted (and, when one already exists, only after verifying it decrypts).
Why: The secretbox key lives in the same chrome.storage.local as the ciphertext, so this is obfuscation against casual inspection only — anyone who can read extension storage can decrypt. This is honestly documented in crypto.ts and UI copy must not over-promise.
How to apply when auditing key-path changes:
- Never log or transmit the key except to the user's chosen provider endpoint. Grep console.* for key/config exposure; error strings in providers.ts must carry only provider label + response message, never headers/key.
- Preserve write-before-delete ordering in any migration so an interruption can't lose the only key. Note: getOrCreateEncKey persists encKey fire-and-forget (no awaited callback) — relies on Chrome FIFO storage ordering; awaiting it would be stricter.
- Both onMessage listeners (background + content) guard
sender.id !== chrome.runtime.id. Legit internal messages (content/popup/options + background→content tabs.sendMessage) all carry sender.id === chrome.runtime.id, so the guard is safe. It blocks other extensions / externally_connectable. - Content/popup must never fetch a provider directly — key handling belongs in the background worker.