fix(copilot): redact sim_key API keys from persisted Mothership chat messages#4434
fix(copilot): redact sim_key API keys from persisted Mothership chat messages#4434TheodoreSpeaks merged 3 commits intostagingfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Adds a client-side, in-memory cache to capture keys during live streaming and re-inject them after the post-stream refetch, so users can still copy a newly-generated key while older messages remain masked; this also handles credential tags split across streamed text chunks by coalescing assistant text blocks before redaction. Introduces reusable UI/utilities: a new Reviewed by Cursor Bugbot for commit f54ee3a. Bugbot is set up for automated code reviews on this repo. Configure here. |
Greptile SummaryThis PR prevents raw
Confidence Score: 3/5Safe to merge for single-key flows; the per-block cursor reset will serve the wrong key value when multiple API keys are generated in one turn and span separate content blocks. One P1 defect on the changed path: the multi-block restoration cursor resets per block, producing wrong displayed key values for the second+ API key in a multi-key turn. DB redaction (the primary security goal) is unaffected, but the UX correctness of the key-restore feature is broken for the multi-key case. Score is 3/5 (below the P1 ceiling of 4) because the broken path is the core feature this PR introduces. apps/sim/lib/copilot/chat/sim-key-redaction.ts — restoreRevealedSimKeysForMessage block-mapping loop (lines 244–250); also apps/sim/lib/copilot/chat/sim-key-redaction.test.ts needs a test covering two redacted tags in separate contentBlocks entries. Important Files Changed
Sequence DiagramsequenceDiagram
participant SSE as SSE Stream
participant UC as useChat (flush)
participant BPA as buildPersistedAssistantMessage
participant DB as Postgres
participant SEL as message selector (useMemo)
participant UI as CredentialDisplay
SSE->>UC: streaming text chunks (runningText accumulates)
UC->>UC: captureRevealedSimKeys(revealedSimKeysRef, [assistantId, requestId], runningText)
Note over UC: in-memory cache: {assistantId → ['sk-sim-...']}
SSE->>BPA: OrchestratorResult (full content + contentBlocks)
BPA->>BPA: redactSensitiveContent(content)
BPA->>BPA: mergeAndRedactPersistedBlocks(contentBlocks)
BPA->>BPA: redactToolCallResult('generate_api_key', result)
BPA->>DB: INSERT persisted message (no raw key)
DB-->>UC: chatHistory refetch (redacted message)
UC->>SEL: chatHistory.messages.map(toDisplayMessage)
SEL->>SEL: restoreRevealedSimKeysForMessage(msg, revealedSimKeysRef)
Note over SEL: looks up cache by msg.id or msg.requestId
SEL->>UI: ChatMessage with value restored (in-memory only)
UI->>UI: ApiKeyReveal renders copyable key
Reviews (1): Last reviewed commit: "fix(copilot): redact sim_key API keys fr..." | Re-trigger Greptile |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a996b75. Configure here.

Summary
<credential type="sim_key">tags andgenerate_api_keytool result keys before persisting assistant messages — DB never stores raw keys, replays show "API key hidden"requestId; restore them on read so post-finalize refetch can't clobber the live key the user just generated<ApiKeyReveal>component (with copy button or dots when redacted) used by Mothership chat, settings API-keys modal, and Copilot settingsuseCopyToClipboardhook, adopted byApiKeyRevealandCopyCodeButtonType of Change
Testing
Tested manually via Chrome DevTools MCP — generated multiple keys in one chat, refreshed, confirmed redacted "••••••" rendering for prior keys and live-key visibility for the most recent generation.
bun run lintandbun run check:api-validation:strictpass. New unit tests (20/20 pass) cover redaction across split chunks, multi-key restore, and request-id fallback.(localhost key, also removed right after)


on refresh:
Checklist