Commit Graph

1 Commits

Author SHA1 Message Date
Reuven 49c8019ed9 feat(encryption): ADR-096 Phase 3 — wire vault into terminal-tools
End-to-end opt-in encryption for the .claude-flow/terminals/store.json
store. Terminal command history routinely carries pasted credentials
(API keys, DB passwords), so this is the highest-stakes of the three
high-tier targets.

Mechanics — same shape as Phase 2 (session-tools) so reviewers can
diff the patterns:

  src/mcp-tools/terminal-tools.ts
    - loadTerminalStore: readFileSync → readFileMaybeEncrypted; the
      magic-byte sniff handles legacy plaintext + post-migration
      encrypted in one read path
    - saveTerminalStore: passes { encrypt: true } unconditionally;
      vault env-gate decides whether bytes are actually encrypted
    - Dropped the now-unused readFileSync import

Tests (__tests__/terminal-encryption.test.ts, 7 cases):

  encryption disabled (legacy plaintext)
    - writes plaintext JSON (first byte '{', not 'R')
    - round-trips create → list

  encryption enabled (RFE1 wire format)
    - on-disk blob starts with the RFE1 magic
    - plaintext name is NOT present anywhere in the on-disk bytes —
      stricter than the equivalent session test because terminal stores
      carry pasted credentials and an encryption regression here is a
      direct CVE
    - round-trips create → list

  migration
    - legacy plaintext store written before the gate flipped is still
      readable AFTER the gate flips on (sniff path)

  mixed: same store, plaintext-then-encrypted
    - first save plaintext (gate off), second save encrypted (gate on);
      saveTerminalStore rewrites the whole file → on-disk becomes
      encrypted bytes containing BOTH sessions. List enumerates both.

Verified:
  tsc --noEmit clean (0 errors)
  Encryption-track suite (vault + fs-secure + session + terminal):
    67/67 passing
  Full vitest suite: 1924/1924 passing, 46 skipped, 0 failures
    (was 1917; +7 from this commit)

Phase 4 wires the seven memory-initializer DB writes — the sql.js
SQLite blob containing memory entries + 384-dim embeddings. Different
shape (whole-Buffer encryption vs JSON.stringify) so it lands as its
own iteration.
2026-05-03 21:20:00 -04:00