residoo 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +334 -46
  2. package/SECURITY.md +29 -22
  3. package/package.json +1 -1
  4. package/src/cli.js +249 -17
  5. package/src/integrity.js +689 -0
  6. package/src/patterns.js +78 -5
  7. package/src/report.js +188 -8
  8. package/src/rotation.js +834 -0
  9. package/src/sources/agent-configs.js +308 -0
  10. package/src/sources/aider.js +361 -0
  11. package/src/sources/amazon-q.js +199 -0
  12. package/src/sources/antigravity-cli.js +155 -0
  13. package/src/sources/cline.js +208 -0
  14. package/src/sources/codebuff.js +295 -0
  15. package/src/sources/codex-cli.js +258 -0
  16. package/src/sources/cody.js +325 -0
  17. package/src/sources/continue.js +408 -0
  18. package/src/sources/copilot-chat.js +272 -0
  19. package/src/sources/copilot-cli.js +300 -0
  20. package/src/sources/crush.js +364 -0
  21. package/src/sources/cursor.js +374 -0
  22. package/src/sources/devin-cli.js +241 -0
  23. package/src/sources/factory-droid.js +153 -0
  24. package/src/sources/fx.js +136 -0
  25. package/src/sources/gemini-cli.js +242 -0
  26. package/src/sources/goose.js +366 -0
  27. package/src/sources/grok-cli.js +267 -0
  28. package/src/sources/hermes.js +282 -0
  29. package/src/sources/index.js +172 -8
  30. package/src/sources/jetbrains-ai-assistant.js +343 -0
  31. package/src/sources/jetbrains-junie.js +292 -0
  32. package/src/sources/kilo-code.js +430 -0
  33. package/src/sources/kimi-code.js +147 -0
  34. package/src/sources/kiro-cli.js +393 -0
  35. package/src/sources/kiro-ide.js +230 -0
  36. package/src/sources/llm.js +328 -0
  37. package/src/sources/mentat.js +143 -0
  38. package/src/sources/open-interpreter.js +224 -0
  39. package/src/sources/openclaw.js +218 -0
  40. package/src/sources/opencode.js +379 -0
  41. package/src/sources/openhands.js +181 -0
  42. package/src/sources/pearai.js +151 -0
  43. package/src/sources/pi-agent.js +130 -0
  44. package/src/sources/project-artifacts.js +355 -0
  45. package/src/sources/qodo-gen.js +189 -0
  46. package/src/sources/qwen-code.js +244 -0
  47. package/src/sources/roo-code.js +239 -0
  48. package/src/sources/trae.js +294 -0
  49. package/src/sources/void.js +273 -0
  50. package/src/sources/warp.js +395 -0
  51. package/src/sources/windsurf.js +256 -0
  52. package/src/sources/zed.js +374 -0
package/README.md CHANGED
@@ -1,13 +1,29 @@
1
- # residoo
1
+ <div align="center">
2
+
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="docs/logo-dark.svg">
5
+ <source media="(prefers-color-scheme: light)" srcset="docs/logo-light.svg">
6
+ <img src="docs/logo-light.svg" alt="residoo" width="280">
7
+ </picture>
2
8
 
3
9
  **Find secrets leaking through your AI coding agent's session history.**
4
10
 
11
+ [![npm version](https://img.shields.io/npm/v/residoo)](https://www.npmjs.com/package/residoo)
12
+ [![CI](https://github.com/dandovdub/residoo/actions/workflows/ci.yml/badge.svg)](https://github.com/dandovdub/residoo/actions/workflows/ci.yml)
13
+ [![license: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
14
+ [![node >=18](https://img.shields.io/badge/node-%3E%3D18-339933)](package.json)
15
+ [![runtime dependencies: 0](https://img.shields.io/badge/runtime_dependencies-0-brightgreen)](package.json)
16
+
17
+ <img src="docs/demo.svg" alt="residoo scan terminal output: 17 potential secrets found across 3 files, 87 files scanned (1.2 GB), values redacted to first/last 4 characters, no network calls" width="760">
18
+
19
+ </div>
20
+
5
21
  Every time Claude Code, Cursor, or a similar tool reads a file, runs a command, or
6
- browses a page on your behalf, it writes a transcript of the whole session to disk —
7
- including the contents of whatever it touched. If that ever included a `.env` file,
8
- a config with a real key in it, or a login token captured during testing, that
9
- credential is now sitting in plaintext, indefinitely, in a place almost nobody
10
- thinks to check.
22
+ browses a page on your behalf, it writes a transcript of the whole session to
23
+ disk, including the contents of whatever it touched. If that ever included a
24
+ `.env` file, a config with a real key in it, or a login token captured during
25
+ testing, that credential is now sitting in plaintext, indefinitely, in a place
26
+ almost nobody thinks to check.
11
27
 
12
28
  residoo scans those transcripts and tells you what's in them.
13
29
 
@@ -21,31 +37,163 @@ $ residoo scan
21
37
  16 [high] AWS Access Key ID (1 distinct value, re-exposed 15× across tool output)
22
38
  1 [high] Private key block
23
39
 
24
- Values are redacted in this report first/last 4 characters only. Nothing scanned
25
- here left your machine; residoo makes no network calls.
40
+ Values are redacted in this report (first/last 4 characters only). Nothing
41
+ scanned here left your machine; residoo makes no network calls.
26
42
  ```
27
43
 
28
44
  ## Why this, and not a git secret scanner
29
45
 
30
- Tools like `gitleaks` and `trufflehog` are excellent at what they do and what
46
+ Tools like `gitleaks` and `trufflehog` are excellent at what they do, and what
31
47
  they do is scan **commits**. That's a different, well-covered space. Nobody was
32
48
  looking at the **conversation transcripts** these agents leave behind, which
33
49
  contain a superset of everything a commit does: not just code, but file
34
50
  contents, terminal output, and whatever got pasted into a prompt.
35
51
 
52
+ Two newer categories are adjacent but solve a different problem, worth being
53
+ precise about rather than lumping together:
54
+
55
+ - **Real-time hooks** (e.g. GitGuardian's `ggshield` AI hook, GitHub's secret
56
+ scanning via its MCP server) intercept a prompt or a code change *as it
57
+ happens*, going forward, in the session that has the hook installed. They
58
+ do nothing for the months of transcripts already sitting on disk, or for
59
+ any session run without the hook active. residoo scans **retroactively, at
60
+ rest**: every file already there, from every past session.
61
+ - **agentsweep** is a genuine, welcome peer covering similar ground. Broader,
62
+ in fact: 31 agent sources and 209 detection rules to residoo's smaller set,
63
+ plus in-place redaction, SARIF output, and a pre-commit hook. The tradeoffs
64
+ are worth naming precisely rather than either dismissing it or copying it
65
+ blindly. It needs Python 3.11+ and three pip packages (all clean ones, on
66
+ inspection; no known CVEs), where residoo needs nothing beyond Node. Its
67
+ own README documents that its in-place redaction leaves the pre-redaction
68
+ original sitting in a **plaintext** `.bak` file, and its issue tracker shows
69
+ the real cost of that design: a merged fix
70
+ ([PR #13](https://github.com/Ishannaik/agent-sweep/pull/13)) for a case
71
+ where redacting a WAL-mode SQLite database left the secret recoverable from
72
+ a leftover journal file. residoo's `--seal` takes a different tradeoff
73
+ (encrypt a copy, touch nothing, never claim a file is "cleaned") precisely
74
+ to avoid that failure class. Its tracker also shows several real,
75
+ since-fixed false-*clean* reports: schema drift and malformed lines
76
+ silently skipped, `--root` pointed at a file scanning nothing and exiting
77
+ 0. That is the exact failure mode residoo's `broken`/`partial` status
78
+ contract (see `CONTRIBUTING.md`) exists to make structurally hard to
79
+ reproduce. None of this makes agentsweep bad; it makes for a legitimately
80
+ different set of choices, and its README is honest about its own tradeoffs
81
+ too. Worth a look if broader source coverage matters more to you than a
82
+ minimal dependency footprint.
83
+
84
+ This isn't a gap Anthropic is planning to close upstream, either: a
85
+ [request to scrub secrets from `~/.claude/projects` natively](https://github.com/anthropics/claude-code/issues/50014)
86
+ was filed and closed as **not planned**. Whatever scans this directory, it
87
+ won't be built into the tool that writes it.
88
+
36
89
  ## What it does
37
90
 
38
91
  - Scans your local AI-agent session transcripts for high-confidence secret
39
- patterns (cloud provider keys, private key blocks, OAuth/API tokens,
40
- database connection strings, and more see `src/patterns.js`).
92
+ patterns: cloud provider keys, private key blocks, OAuth/API tokens,
93
+ database connection strings, and more (see `src/patterns.js`).
41
94
  - Redacts everything in its own output. You get a shape and a first/last-4
42
- preview, never the real value including in `--json` mode.
95
+ preview, never the real value, including in `--json` mode.
43
96
  - Tells you how many **distinct** secrets it found versus how many times one
44
97
  got echoed back across tool calls, so the headline number reflects real
45
98
  exposure, not repetition.
46
99
  - Flags likely placeholder/example matches (an HTML form's
47
100
  `placeholder="AKIA..."` hint, a doc's example key) separately from real
48
101
  findings, rather than either hiding them or inflating the count with them.
102
+ - Scans agent **config** files too (settings, MCP server configs, memory
103
+ files), and checks the places the 2026 supply-chain campaigns planted
104
+ persistence: hooks, dropper scripts, auto-run tasks, invisible Unicode.
105
+ See the next section.
106
+ - Attaches a **rotation runbook** to every finding: the vendor's real
107
+ revocation path, verified against their own docs, plus a local
108
+ acknowledgement ledger so "found it" can become "closed it". See
109
+ [Rotation](#rotation-from-found-to-closed).
110
+ - Scans a **repository checkout** instead of the machine with
111
+ `--project <dir>`: committed transcripts, agent configs, and root `.env`
112
+ files, built for CI and pre-commit. See
113
+ [CI and pre-commit](#ci-and-pre-commit).
114
+
115
+ ## Beyond transcripts: configs and planted persistence
116
+
117
+ Transcripts leak what your agent *saw*. Config files leak what your agent was
118
+ *configured with*, and it turns out that is the better-measured problem.
119
+ GitGuardian counted 24,008 secrets inside MCP config files on public GitHub
120
+ (2,117 still valid when checked), and Lakera found live credentials inside
121
+ `.claude/settings.local.json` files shipped in roughly 30 published npm
122
+ packages, because Claude Code's approved-command cache quietly accumulates
123
+ tokens and no packaging tool ignores `.claude/` by default. So as of v0.2.0,
124
+ `residoo scan` includes an **agent config source** covering the home-level
125
+ config files of Claude Code, Claude Desktop, Cursor, Gemini CLI, Codex, and
126
+ Kiro. Every path is verified against a real install or published sources (one
127
+ disclosed exception, a stealer-target path backed by a single published
128
+ list, argued openly in the source header), with the full verification trail
129
+ written into `src/sources/agent-configs.js`.
130
+
131
+ The same files are also where the year's supply-chain campaigns planted
132
+ their persistence. Mini Shai-Hulud wrote a `SessionStart` hook into
133
+ `.claude/settings.json` and a `"runOn": "folderOpen"` task into
134
+ `.vscode/tasks.json`. Miasma reused both plants and added
135
+ `.gemini/settings.json` hooks and `.cursor/rules/setup.mdc` prompt-injection
136
+ files. The keyv/ChainDrop wave dropped a script literally named `setup.mjs`
137
+ into `.claude/` and `.vscode/`. And TrapDoor hid instructions in
138
+ `CLAUDE.md`/`.cursorrules` as zero-width Unicode: invisible in your editor,
139
+ fully visible to the agent. So every scan now also runs **integrity checks**
140
+ over those exact locations:
141
+
142
+ - Every auto-executing hook found in the checked locations is listed (hooks
143
+ run without asking; you should be able to vouch for each one). Only
144
+ commands matching a published campaign IOC (`setup.mjs`) or a
145
+ campaign-shaped behavior escalate to warnings: piping a download straight
146
+ into a shell, decoding base64 before executing, running repo-local scripts
147
+ out of dot-directories.
148
+ - Loose scripts in `.claude/`, and the exact planted filenames from the
149
+ published IOC lists, are flagged by name.
150
+ - `CLAUDE.md`, `.cursorrules`, and `.cursor/rules/*` are checked for
151
+ zero-width Unicode, with legitimate emoji/script joiners kept to an
152
+ informational tier so the warning count stays meaningful.
153
+ - `.vscode/tasks.json` is parsed (as JSONC, comments and all) for tasks that
154
+ execute on folder open.
155
+
156
+ The checks are read-only like everything else, warnings (not review items)
157
+ count toward `--fail-on-find`, project-level checks cover the directory you
158
+ run from, and `--no-integrity` skips the whole thing. A config that exists
159
+ but can't be read or parsed is reported as unverified, never silently
160
+ counted as clean.
161
+
162
+ ## How it works
163
+
164
+ ```
165
+ YOUR MACHINE · no network calls
166
+ ┌───────────────────────────────────────────────────────────────┐
167
+ │ │
168
+ │ 42 transcript sources agent config files │
169
+ │ ~/.claude, Cursor, Codex… settings · MCP · memory │
170
+ │ (--project <dir>: a repo checkout instead of the machine) │
171
+ │ │ │ │
172
+ │ ├──────────────┬───────────────┤ │
173
+ │ ▼ │ ▼ │
174
+ │ stream + match │ integrity checks │
175
+ │ 35 verified rules │ hooks · droppers · │
176
+ │ │ │ zero-width unicode │
177
+ │ ▼ ▼ │ │
178
+ │ redacted report (first/last 4 chars only) ◀────────────┤
179
+ │ │ │
180
+ │ ├─▶ rotation hints per finding · explain / ack │
181
+ │ │ ledger: ~/.residoo/rotations.json │
182
+ │ │ │
183
+ │ ▼ --seal (only if you ask) │
184
+ │ AES-256-GCM vault · scrypt key · encrypted manifest │
185
+ │ │ │ │
186
+ │ ▼ unseal --restore ▼ --upload-cloudroam
187
+ │ SHA-256 verified copy ciphertext only ┄┄┄┄┄┄┄┄┄▶
188
+ │ │
189
+ └───────────────────────────────────────────────────────────────┘
190
+ ```
191
+
192
+ The `--seal` and `--upload-cloudroam` legs never run unless you pass their
193
+ flag. Everything above the vault happens on every scan; nothing in the
194
+ diagram ever modifies or deletes an existing file. The one exception, stated
195
+ in the open: `residoo ack` writes residoo's own rotation ledger at
196
+ `~/.residoo/rotations.json` (atomic, redacted, never a user file).
49
197
 
50
198
  ## Sealing what it finds
51
199
 
@@ -57,29 +205,101 @@ residoo scan --seal
57
205
  ```
58
206
 
59
207
  Every transcript that carried a finding is encrypted into a local vault
60
- directory AES-256-GCM, key derived from your passphrase with scrypt, streamed
61
- (an 800MB transcript never touches memory whole). The vault's manifest — the
62
- mapping from numbered blobs back to real paths is itself encrypted, so the
63
- vault doesn't advertise what's inside it even by name. **Originals are never
64
- touched**: once you've verified a restore works (`residoo unseal <vault> --restore
65
- 0001.sealed --out /tmp/check` verified byte-identical via a recorded SHA-256),
66
- deleting the plaintext is your decision, made by you, not by this tool.
208
+ directory. AES-256-GCM, key derived from your passphrase with scrypt,
209
+ streamed, so an 800MB transcript never touches memory whole. The vault's
210
+ manifest (the mapping from numbered blobs back to real paths) is itself
211
+ encrypted, so the vault doesn't advertise what's inside it even by name.
212
+ **Originals are never touched.** Once you've verified a restore works
213
+ (`residoo unseal <vault> --restore 0001.sealed --out /tmp/check`, verified
214
+ byte-identical via a recorded SHA-256), deleting the plaintext is your
215
+ decision, made by you, not by this tool.
67
216
 
68
217
  Optionally, `--upload-cloudroam` (with `CLOUDROAM_API_KEY`, `--connector`,
69
218
  `--bucket`) copies the sealed vault to [CloudRoam](https://cloudroam.io) for
70
- durable, cross-cloud storage. **This is the only feature in residoo that touches
71
- the network, it never runs unless you pass the flag, and only ciphertext is
72
- transmitted** the vault is sealed before upload code ever executes.
219
+ durable, cross-cloud storage. **This is the only feature in residoo that
220
+ touches the network, it never runs unless you pass the flag, and only
221
+ ciphertext is transmitted.** The vault is sealed before upload code ever
222
+ executes.
223
+
224
+ ## Rotation: from found to closed
225
+
226
+ Detection without rotation is theater, and the field's own numbers say so:
227
+ 64% of secrets leaked publicly in 2022 were still valid years later, 88% of
228
+ re-verified leaked AWS keys still authenticated, and the median time to
229
+ remediate a GitHub-leaked secret is 94 days. A scanner that stops at "found
230
+ it" leaves all of that untouched. So every finding in a residoo report comes
231
+ with the way out:
232
+
233
+ - **A rotation hint per finding**, from a per-rule guidance map covering all
234
+ 35 detection rules (plus the opt-in noisy ones). Where a rotation URL is
235
+ shown, that exact URL was fetched and confirmed to document rotating or
236
+ revoking that credential type; where a vendor's docs are login-walled or
237
+ unfetchable, the report gives the console path in words instead of a link
238
+ it could not verify. Generic shapes (a JWT, a bearer header) get honest
239
+ generic guidance that says how to identify the issuer, never a pretend
240
+ vendor.
241
+ - **`residoo explain <rule-id>`** prints the full runbook for one credential
242
+ type: where to revoke, the steps, and what revocation actually does at
243
+ that vendor. `residoo explain --list` shows the whole catalogue.
244
+ - **`residoo ack <fingerprint>`** records that you rotated one finding.
245
+ Every finding carries a stable fingerprint (derived only from
246
+ already-redacted material, so the ledger can never leak), shown in the
247
+ report and in `--json`. Acknowledged findings are reported as such on the
248
+ next scan instead of re-alarming forever. The ledger lives at
249
+ `~/.residoo/rotations.json`: residoo's own file, written atomically, ack
250
+ notes redacted through the same pipeline as previews.
251
+ - **Order matters, and the report says so when it does.** The ChainDrop
252
+ campaign (Aug 2026) shipped a token monitor that fires an attacker payload
253
+ the moment the stolen GitHub token is revoked. When one scan finds both
254
+ integrity warnings and leaked credentials, the report tells you to remove
255
+ the planted persistence first and rotate second, because "rotate
256
+ everything now" advice can itself trigger the damage.
257
+
258
+ Acks change what the report says, never what CI does: `--fail-on-find`
259
+ fails on every finding, acknowledged or not, unless you explicitly pass
260
+ `--allow-acked` (integrity warnings always fail either way).
261
+
262
+ ## CI and pre-commit
263
+
264
+ `residoo scan --project <dir>` scans a repository checkout instead of the
265
+ machine it runs on: committed agent transcripts (Claude Code `.jsonl`
266
+ trees, Codex `rollout-*.jsonl`, SpecStory histories), agent config and
267
+ rules files at any depth, and root-level `.env` files, plus the integrity
268
+ checks anchored at that directory. It deliberately does not touch the
269
+ machine's home-level sources, so a clean CI run means the checkout is
270
+ clean and claims nothing about anyone's laptop.
271
+
272
+ As a GitHub Action (this repository doubles as a composite action):
273
+
274
+ ```yaml
275
+ steps:
276
+ - uses: actions/checkout@v4
277
+ - uses: dandovdub/residoo@v0.3.0
278
+ ```
279
+
280
+ As a pre-commit hook:
281
+
282
+ ```yaml
283
+ repos:
284
+ - repo: https://github.com/dandovdub/residoo
285
+ rev: v0.3.0
286
+ hooks:
287
+ - id: residoo
288
+ ```
289
+
290
+ Or with no integration at all: `npx --yes residoo scan --project . --fail-on-find`.
291
+ Exit codes, inputs, and exactly what project mode does and does not see are
292
+ documented in [docs/ci.md](docs/ci.md).
73
293
 
74
294
  ## What it does not do
75
295
 
76
- - **No network calls in the default path and none at all unless you
296
+ - **No network calls in the default path, and none at all unless you
77
297
  explicitly pass `--upload-cloudroam`.** A secret scanner that phones home is
78
298
  not a tool you should trust with your secrets. Verify this yourself: the one
79
299
  `fetch` call in the codebase is in `src/sealvault.js`, reachable only behind
80
300
  that flag, and sends only encrypted bytes.
81
301
  - **Nothing destructive, ever.** Scanning is read-only. Sealing creates *new*
82
- files and modifies or deletes nothing not even the plaintext it just
302
+ files and modifies or deletes nothing, not even the plaintext it just
83
303
  encrypted a copy of. That last step is deliberately left to a human.
84
304
  - **No telemetry, no analytics, no update-check ping.**
85
305
 
@@ -96,7 +316,24 @@ npm install -g residoo
96
316
  residoo scan
97
317
  ```
98
318
 
99
- Requires Node.js 18+. Zero runtime dependencies check `package.json`.
319
+ A Homebrew formula ships in this repo at `packaging/homebrew/`. It installs
320
+ the exact tarball published to npm (same bits, sha256 verified), so Homebrew
321
+ is a second door to the same release, not a second build. Once the tap
322
+ repository (`dandovdub/homebrew-residoo`) is published, installation is:
323
+
324
+ ```bash
325
+ brew tap dandovdub/residoo
326
+ brew install residoo
327
+ ```
328
+
329
+ Until the tap is up, npm above is the way in. The formula always points at
330
+ the latest *published* npm release (its sha256 is computed from the real
331
+ tarball), so it can lag a fresh tag by one publish cycle.
332
+
333
+ Requires Node.js 18+. The SQLite-backed sources listed below additionally
334
+ need 22.5+; residoo still runs and scans every line-delimited/JSON source,
335
+ including Claude Code, fine without it. Zero runtime dependencies, and you
336
+ can check `package.json` rather than take that on faith.
100
337
 
101
338
  ## Usage
102
339
 
@@ -104,9 +341,15 @@ Requires Node.js 18+. Zero runtime dependencies — check `package.json`.
104
341
  residoo scan [options]
105
342
 
106
343
  --json machine-readable output (full detail, still redacted)
344
+ --project [dir] scan a repository checkout instead of this machine
345
+ (committed transcripts, agent configs, root .env)
107
346
  --include-noisy also run broad, false-positive-prone rules
108
347
  --include-suppressed also show matches that looked like placeholder/example text
109
- --fail-on-find exit code 1 if anything is found (for CI)
348
+ --fail-on-find exit code 1 if anything is found (for CI): secret
349
+ findings and integrity warnings count, review items don't
350
+ --allow-acked with --fail-on-find: acknowledged findings no longer
351
+ fail the run (pending ones and warnings still do)
352
+ --no-integrity skip the integrity checks
110
353
  --no-color disable ANSI colour
111
354
 
112
355
  --seal encrypt every transcript with findings into a local vault
@@ -114,53 +357,98 @@ residoo scan [options]
114
357
  --upload-cloudroam also upload the sealed vault (needs CLOUDROAM_API_KEY,
115
358
  --connector <id>, --bucket <name>; ciphertext only)
116
359
 
360
+ residoo explain <rule-id> full rotation runbook for one rule
361
+ residoo explain --list every rule id and label
362
+ residoo ack <fingerprint> [--note <text>] mark one finding rotated
363
+
117
364
  residoo unseal <vault-dir> list a vault's contents
118
365
  residoo unseal <vault-dir> --restore <n> --out <p> restore one file, hash-verified
119
366
  ```
120
367
 
121
368
  The vault passphrase comes from `RESIDOO_PASSPHRASE` or a hidden interactive
122
- prompt. There is no recovery if you lose it that is the point of the design,
369
+ prompt. There is no recovery if you lose it. That is the point of the design,
123
370
  so pick one you keep.
124
371
 
125
372
  ## Sources supported today
126
373
 
127
- **Claude Code** (`~/.claude/projects/**/*.jsonl`) verified against real,
128
- populated transcript directories while building this.
129
-
130
- Cursor, GitHub Copilot, and Windsurf all keep local session history too, and
131
- support for them is very much wanted — but shipping a scanner that checks a
132
- guessed path and reports "all clear" when it simply didn't know where to
133
- look is worse than not supporting a tool at all. If you use one of these and
134
- want to add a verified adapter, see below — it's a small, self-contained
135
- file.
374
+ 43 sources as of this writing (42 transcript stores plus the agent-config
375
+ source described above), in two honestly-distinct tiers. Project mode
376
+ (`--project`) adds one more, opt-in source (`src/sources/project-artifacts.js`)
377
+ that scans a repository checkout rather than the machine and never
378
+ participates in a default scan. See
379
+ `src/sources/index.js` for the full list and grouping, and each source file's
380
+ own header for exactly what was and wasn't checked.
381
+
382
+ **Real-install-verified.** The adapter was run against an actual, populated
383
+ installation and confirmed to find real content:
384
+
385
+ - **Claude Code** (`~/.claude/projects/**/*.jsonl`)
386
+ - **Agent config files**, for its Claude-family paths (`~/.claude.json` and
387
+ its `.backup`, `~/.claude/settings*.json`, Claude Desktop's
388
+ `claude_desktop_config.json`). Its Cursor/Gemini/Codex/Kiro paths are in
389
+ the tier below; `src/sources/agent-configs.js` tracks verification per
390
+ path, not per file.
391
+
392
+ **Multi-source-corroborated-but-unverified.** The path/schema is backed by
393
+ 2+ independent, credible sources (official docs, the tool's own shipped
394
+ source code, a real community tool that reads the same files for a living,
395
+ or a real user's own reported install) but was **not** checked against a real
396
+ install of the tool on any machine this project was built on. Every adapter
397
+ in this tier is still built to fail loudly (`broken: true`, `status:
398
+ "failed"`) rather than silently report "all clear", but the path itself
399
+ could still be stale or wrong in a way only a real install can catch. If you
400
+ use one of these and can confirm `residoo scan`'s file counts look right for
401
+ what's actually on your disk, that report is exactly what moves a source out
402
+ of this tier:
403
+
404
+ Cursor, Codex CLI, OpenCode, Aider, Cline, Roo Code, Kilo Code, Windsurf,
405
+ PearAI, Trae, Void, Gemini CLI, Qwen Code, Continue, Open Interpreter, Goose,
406
+ GitHub Copilot Chat, GitHub Copilot CLI, `llm` (Simon Willison's Datasette-
407
+ adjacent CLI), Codebuff, Mentat, Hermes, OpenClaw, Warp, Crush, Grok Build,
408
+ Kiro CLI, Kiro IDE, Zed, JetBrains Junie, JetBrains AI Assistant, Sourcegraph
409
+ Cody, Amazon Q Developer, Qodo Gen, OpenHands, Factory Droid CLI, Devin CLI,
410
+ Pi, Google Antigravity, Kimi Code, and `fx`.
411
+
412
+ A few of these are SQLite-backed (Cursor, Crush, Cody, Devin CLI, Hermes,
413
+ Kiro CLI, `llm`, Trae, Void, Warp, Zed) and need Node.js 22.5+ for the
414
+ built-in `node:sqlite` module (not a dependency; see `package.json`). On an
415
+ older Node, `residoo scan` reports each of those as detected-but-not-scanned
416
+ rather than silently dropping it or crashing.
417
+
418
+ **Investigated and deliberately not included**, rather than guessed at:
419
+ Plandex (confirmed, from its own source, to be client-server with nothing
420
+ local to scan), CodeGPT and Augment Code (both account/cloud-based, no
421
+ evidence of a local transcript file), and Replit Agent (confirmed
422
+ cloud-only). Tabby, Tabnine, Zencoder, Tongyi Lingma, and Berd were
423
+ researched but didn't clear this project's 2-independent-source bar in the
424
+ time available. A verified adapter for any of these is a welcome PR.
136
425
 
137
426
  ## Adding a source
138
427
 
139
428
  A source is a small object with four methods: `id()`, `label()`,
140
429
  `available()`, `files()`, and `readLines(file)`. `src/sources/claude-code.js`
141
- is the reference implementation copy it, point it at the real local
430
+ is the reference implementation. Copy it, point it at the real local
142
431
  storage path for your tool, and open a PR. Two contracts scan.js actually
143
432
  depends on, worth getting right rather than guessing from a quick skim:
144
433
 
145
434
  - **`files()`** is a generator yielding `{ file, mtimeMs, sizeBytes, broken }`.
146
435
  Set `broken: true` (other fields can be omitted) for an entry that looked
147
- like it should be scannable but wasn't a dangling symlink is the main
148
- case. Don't just `continue` past it inside the generator: an early version
436
+ like it should be scannable but wasn't; a dangling symlink is the main
437
+ case. Don't just `continue` past it inside the generator. An early version
149
438
  of the Claude Code source did exactly that, and a real, non-hypothetical
150
439
  case (a project directory relocated via a symlink whose target no longer
151
- exists) went completely invisible not in the scan count, not in any
440
+ exists) went completely invisible: not in the scan count, not in any
152
441
  warning, nothing. Surfacing it as `broken` is what lets scan.js report it
153
442
  instead.
154
443
  - **`readLines(file)`** is `async`, returning `{ lines, status, bytesRead }`.
155
444
  `status` is `"complete"`, `"partial"` (some real lines WERE read before a
156
- failure partway through return them, don't discard real content because
445
+ failure partway through; return them, don't discard real content because
157
446
  the rest of the file didn't finish cleanly), `"too-large"`, or `"failed"`.
158
447
  Whatever you return in `lines` for a non-"complete" status still gets
159
448
  scanned normally.
160
449
 
161
450
  Please verify the path actually exists and holds real content before
162
- submitting see the note above on why
163
- that matters here specifically.
451
+ submitting. See the note above on why that matters here specifically.
164
452
 
165
453
  ## A known limitation, stated plainly
166
454
 
@@ -168,7 +456,7 @@ Shape-based detection can't tell a real secret from a realistic-looking
168
456
  example in a fetched web page or a piece of documentation your agent read
169
457
  aloud back to you. The `--include-suppressed`/placeholder-context heuristic
170
458
  catches the common UI-hint case, not every case. Treat every finding as a
171
- lead to check, not a certainty the same is true of every tool in this
459
+ lead to check, not a certainty. The same is true of every tool in this
172
460
  category, including the well-established ones.
173
461
 
174
462
  ## License
@@ -177,9 +465,9 @@ MIT. See `LICENSE`.
177
465
 
178
466
  ---
179
467
 
180
- Built and maintained by the team behind [CloudRoam](https://cloudroam.io)
468
+ Built and maintained by the team behind [CloudRoam](https://cloudroam.io),
181
469
  client-side encrypted, cross-cloud backup. residoo has no dependency on
182
- CloudRoam and never will need one to be useful; if a scan turns up something
470
+ CloudRoam and never will need one to be useful. If a scan turns up something
183
471
  you want stored somewhere durable and encrypted going forward, that's the
184
472
  kind of problem CloudRoam solves, but it's an entirely separate choice from
185
473
  running this tool.
package/SECURITY.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  ## Reporting a vulnerability
4
4
 
5
- If you find a security issue in residoo including "this finding shouldn't
5
+ If you find a security issue in residoo, including "this finding shouldn't
6
6
  have been suppressed," "this output leaked more than it should have," or
7
- anything in the redaction logic please report it privately rather than as
7
+ anything in the redaction logic, please report it privately rather than as
8
8
  a public issue. Open a [GitHub Security Advisory](../../security/advisories/new)
9
9
  on this repository, or email the maintainer listed in `package.json`.
10
10
 
@@ -13,45 +13,47 @@ Please include:
13
13
  - Whether real secret material was involved (if so, a redacted/synthetic
14
14
  reproduction is preferred over the real value)
15
15
 
16
- You'll get an acknowledgment within a few days. There's no bug bounty
17
- this is a small open-source tool, not a funded program but every report
16
+ You'll get an acknowledgment within a few days. There's no bug bounty
17
+ (this is a small open-source tool, not a funded program), but every report
18
18
  gets read and taken seriously, and credited in the fix unless you'd rather
19
19
  stay anonymous.
20
20
 
21
21
  ## What's already been checked, and how
22
22
 
23
23
  This isn't a claim taken on faith. Every property below was tested, not
24
- just asserted see the git history for the actual commands run:
24
+ just asserted. See the git history for the actual commands run:
25
25
 
26
26
  - **No network calls in the scan path.** Grepped for every network-capable
27
27
  primitive (`http`, `https`, `fetch`, `child_process`, etc.) across the
28
28
  scanning code. The codebase's single `fetch` lives in `src/sealvault.js`,
29
29
  is reachable only behind the explicit `--upload-cloudroam` flag, and
30
- transmits ciphertext only the vault is fully sealed before that code
30
+ transmits ciphertext only. The vault is fully sealed before that code
31
31
  can run.
32
32
  - **Scanning is read-only.** Grepped for every filesystem write/delete
33
33
  primitive in the scan path. Sealing (`--seal`) writes NEW files into a
34
34
  vault directory it creates; nothing in the codebase modifies or deletes
35
- an existing file, including the plaintext originals a seal just encrypted —
36
- removing those is deliberately left to the human.
35
+ an existing file, including the plaintext originals a seal just
36
+ encrypted. Removing those is deliberately left to the human.
37
37
  - **Output can't leak more than it shows.** The one raw matched value is
38
38
  used in exactly two places: an in-memory dedup count (never serialized)
39
39
  and the redaction function. Verified with a crafted input containing a
40
- raw ANSI escape sequence that a real terminal would execute confirmed
41
- it rendered live (a working clear-screen) before the fix, confirmed it
42
- doesn't after.
40
+ raw ANSI escape sequence that a real terminal would execute. It rendered
41
+ live (a working clear-screen) before the fix, and doesn't after. The
42
+ integrity checker applies the same discipline to attacker-controlled
43
+ config content: control characters are stripped and invisible Unicode is
44
+ rewritten as visible escapes before anything reaches your terminal.
43
45
  - **Not vulnerable to regex denial-of-service.** Every pattern checked
44
46
  against the nested-quantifier shape behind real, dated CVEs in adjacent
45
47
  tooling (e.g. CVE-2026-0621, a ReDoS in Anthropic's own MCP SDK from
46
48
  catastrophic backtracking on an exploded template pattern). Also stress-
47
49
  tested directly against multi-megabyte adversarial inputs.
48
50
  - **No supply-chain surface.** Zero runtime dependencies, zero
49
- pre/post-install lifecycle scripts check `package.json` yourself,
51
+ pre/post-install lifecycle scripts. Check `package.json` yourself;
50
52
  there's nothing to hide behind a `postinstall` hook.
51
53
 
52
54
  ## Verifying you have the real thing
53
55
 
54
- Fake clones of security tools are a real, active pattern not a
56
+ Fake clones of security tools are a real, active pattern, not a
55
57
  hypothetical. In the last year alone: a self-propagating npm worm that
56
58
  typosquatted common package names and injected malicious config into AI
57
59
  coding tools' own settings files; a fake installer for a well-known AI
@@ -61,15 +63,20 @@ history to smuggle malware behind a README download link. A tool whose
61
63
  entire premise is "trust me with what I find in your secrets" is exactly
62
64
  the kind of thing worth impersonating.
63
65
 
64
- - The only npm package is **`residoo`**, published from **this** GitHub
65
- repository via CI, not uploaded by hand from a maintainer's laptop.
66
- - The canonical repository is **`github.com/dandovdub/residoo`** the one
67
- named in this package's own `repository` field, which npm's provenance
68
- attestation cryptographically ties each release to. A GitHub account named
69
- "residoo" exists and is NOT this project. If you found residoo through a
70
- link, a blog post, or a search result rather than directly on npm, check
71
- the provenance badge on the npm page it names the exact repo and
72
- workflow that built the release.
66
+ - The canonical repository is **`github.com/dandovdub/residoo`**, the one
67
+ named in this package's own `repository` field. A GitHub account named
68
+ "residoo" exists and is NOT this project.
69
+ - The only npm package is **`residoo`**. Releases are published from this
70
+ repository through CI with npm's provenance attestation, which
71
+ cryptographically ties each release to the exact repo and workflow that
72
+ built it; check the provenance badge on the npm page. One honest
73
+ exception, stated rather than hidden: the very first release (v0.1.0)
74
+ was a manual upload to claim the name, so provenance starts at the first
75
+ CI-published version after it.
76
+ - The only PyPI package is **`residoo`**: a thin official launcher whose
77
+ entire job is running the npm CLI via `npx`. Its source lives in this
78
+ repository under `pypi/`. It exists partly so nobody else can hold the
79
+ name.
73
80
  - Nothing here needs a postinstall script, a config change to another
74
81
  tool, or elevated permissions. If a "residoo" you found asks for any of
75
82
  those, it isn't this project.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "residoo",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Find secrets leaking through your AI coding agent's session history. Zero network calls in the scan path, zero dependencies.",
5
5
  "license": "MIT",
6
6
  "author": "CloudRoam (https://cloudroam.io)",