Skip to content

security: redact secrets on the explicit add paths (#179)#181

Merged
edheltzel merged 1 commit into
mainfrom
worktree-fix+179-scrub-add-path
Jun 24, 2026
Merged

security: redact secrets on the explicit add paths (#179)#181
edheltzel merged 1 commit into
mainfrom
worktree-fix+179-scrub-add-path

Conversation

@edheltzel

Copy link
Copy Markdown
Owner

What

Wire the existing scrub() redaction seam into the explicit memory write paths so known-prefix credentials are no longer persisted in cleartext.

Before this change, recall add (CLI) and memory_add (MCP) inserted user free-text straight into SQLite with no redaction. A credential with a recognizable prefix (provider API key, bearer token, AWS access-key id, JWT, etc.) typed by the user was stored verbatim in decisions / learnings / breadcrumbs. The redaction layer that already runs on session ingestion simply did not run here. Pre-existing gap — not introduced by #156.

How

  • Single choke point. Both surfaces route through addDecision / addLearning / addBreadcrumb in src/lib/memory.ts. A small scrubFreeText helper there scrubs every persisted free-text field (decision/reasoning/alternatives · problem/solution/prevention · breadcrumb content) before insert, via the canonical scrub() re-exported at src/lib/write-safety.ts (the chore: promote write-safety scrub() pattern table into src/lib (DRY across CLI + MCP) #50 seam). Structured/enum fields (category, status, confidence, tags, importance, provenance) are never touched. Mirrors the session-extract precedent in hooks/lib/extract-core.ts.
  • No-bypass + zero ripple. Wiring at the choke point (not per-surface) means any future caller of these functions is covered too. The functions keep returning number; redacted kinds are surfaced through an optional out-arg, so none of the ~40 existing callers change.
  • Visible-by-design. When something was redacted, the distinct kinds (never the values) are surfaced: a ⚠ redacted secrets before storing: … line on the CLI after the ✓ Added … line, and an appended notice in the memory_add response. Kinds are aggregated distinct across a record's fields, reported once.
  • Markers are the visible [REDACTED:<kind>] form, so search still finds the surrounding context.

This is the anchored known-prefix layer only. It does not touch the #156 threat-detect (entropy/injection) layer, nor the extraction paths that already scrub.

Tests

New tests/commands/scrub-add-path.test.ts (disposable, obviously-fake fixtures — repeated-char filler, never real values):

  1. A planted known-prefix secret is redacted in the stored row on both recall add (runAdd*) and the three add* fns (the memory_add path).
  2. Ordinary prose with no secret is stored unchanged (no over-redaction).
  3. The redacted-kinds surfacing fires when — and only when — a secret was present; same-kind-across-fields is reported once.

Verification

  • bun run lint — clean
  • bun test — 1174 pass / 0 fail (10 new)

Closes #179

The explicit write paths — `recall add` (CLI) and `memory_add` (MCP) —
persisted free-text directly into SQLite with no redaction, so a
known-prefix credential typed by the user was stored in cleartext.

Wire the existing `scrub()` seam (src/lib/write-safety.ts) into the
single choke point both surfaces route through: addDecision /
addLearning / addBreadcrumb in src/lib/memory.ts. Every persisted
free-text field is scrubbed before insert; structured/enum fields are
left untouched. Redacted kinds (never the values) are surfaced via an
optional out-arg — a warning line on the CLI and an appended notice in
the memory_add response — so redaction is visible-by-design, not silent.

Choke-point wiring (vs. per-surface) means any future write path that
calls these functions is covered too, and keeps the `number` return so
no existing caller changes. Mirrors the session-extract precedent in
hooks/lib/extract-core.ts.

Tests assert redaction + kind-surfacing on both `recall add` and the
three add* fns, and that ordinary prose is stored unchanged.

Closes #179
@edheltzel edheltzel merged commit 962b178 into main Jun 24, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

security: wire scrub() into the memory_add / recall add write path (known-prefix secrets persist un-redacted)

1 participant