Files
LexAI/.claude/agent-memory/security-auditor/threat-model-apikey.md
john kevin asprec acea99d7ad
Some checks failed
CI — Test & Build / Test & Build (push) Failing after 39s
feat: Implement Prompt Builder functionality in Popup and Options
- 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.
2026-07-15 15:27:41 +08:00

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
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.