security(cleanup): redact secrets in learned few-shot examples (H3)#52
Conversation
… cloud send When cleanup runs against a backend with redaction enabled, the live dictation text is already passed through Redactor before being sent to the LLM, but the learned few-shot examples pulled from history are not. Those examples are user text from prior corrections, so they can contain the same secrets the redactor exists to scrub (API keys, GitHub PATs, etc.). Run them through the redactor on the same condition (active_backend.redact == true) so cloud backends never see secrets re-injected via few-shot. Adds Redactor.redact_pairs() helper + two unit tests. Tracks H3 in issue #51. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pre-merge checks · ✅ 2 · ⚠ 0 · ❌ 0 · ⏭ 1
|
WalkthroughLearned few-shot examples pulled from correction history are now redacted before being sent to cloud backends. This closes a gap where secrets could leak via the few-shot channel even though live dictation was already redacted. The fix is gated on the same backend redact flag used for live text. Changes
🎯 Effort: 1 (Trivial) · ⏱ ~5 minutes Generated by Sebastion AI · docs |
|
🔒 Sebastion AI — security audit complete. No exploitable findings on this diff. ✅ Audited by Sebastion AI · docs · install on more repos |
lewiswigmore
left a comment
There was a problem hiding this comment.
Approved. Closes H3 from the security review — few-shot pairs are now redacted on the same condition as live dictation, so cloud backends can't see secrets re-injected through the learning channel. Pair-redaction unit-tested, full suite green (302 passed).
Summary
Closes H3 from the security review (issue #51).
When cleanup runs against a redact-enabled backend, the live dictation is passed through
Redactorbefore being sent to the LLM, but learned few-shot examples pulled from history were not. Those examples are user text from prior corrections, so they can contain the same secrets the redactor exists to scrub (sk-…,ghp_…, etc.).This change runs few-shot pairs through the redactor on the same condition (
active_backend.redact == true) that already gates the live-dictation redaction. Net effect: no cloud backend ever sees secrets re-injected via the few-shot channel.Changes
dictate/redact.py— addRedactor.redact_pairs(pairs)helper.dictate/app.py— in_phase_cleanup, when few_shot is non-empty and the active backend hasredact: true, redact pairs before passing tocleanup.clean_sync.tests/test_redact.py— 2 new tests covering pair redaction.Why this is gated, not unconditional
Self-hosted / local backends (Ollama, llama.cpp, MLX) explicitly opt out of redaction in
backends.yamlbecause the user already owns the model + machine. This PR preserves that — only the cloud-bound few-shot is rewritten.Tests
pytest: 302 passed (was 297; +5 from rebase fence-test addition and these 2)ruff check+ruff format --checkon changed files: clean