feat: Implement Prompt Builder functionality in Popup and Options
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.
This commit is contained in:
john kevin asprec
2026-07-15 15:27:41 +08:00
parent 0fef9848cb
commit acea99d7ad
40 changed files with 1971 additions and 177 deletions

View File

@@ -0,0 +1 @@
- [API key at-rest model](threat-model-apikey.md) — how LexAI stores/migrates the BYO LLM key and what "secure" does and doesn't mean here

View File

@@ -0,0 +1,18 @@
---
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.