Some checks failed
CI — Test & Build / Test & Build (push) Failing after 39s
- 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.
19 lines
1.9 KiB
Markdown
19 lines
1.9 KiB
Markdown
---
|
|
name: threat-model-apikey
|
|
description: LexAI API-key at-rest threat model and the storage/migration invariants an auditor must preserve
|
|
metadata:
|
|
type: project
|
|
---
|
|
|
|
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.
|