whatileaked 0.5.3 → 0.6.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.
- package/README.md +10 -4
- package/dist/cli.js +80 -18
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
Your coding agent saves every conversation to your disk. If you ever pasted an
|
|
6
6
|
API key into a chat, or asked the agent to read a `.env` file, that key is now
|
|
7
|
-
sitting in a log file on your laptop — and it was uploaded to Anthropic
|
|
8
|
-
OpenAI the moment you sent the message.
|
|
7
|
+
sitting in a log file on your laptop — and it was uploaded to Anthropic,
|
|
8
|
+
OpenAI or Google the moment you sent the message.
|
|
9
9
|
|
|
10
10
|
Secret scanners point at repositories and CI. You can aim one at `~/.claude` by
|
|
11
11
|
hand, but nobody does — it is not a repo, it is not in CI, and it does not look
|
|
@@ -211,6 +211,7 @@ npx whatileaked scan || echo "found something"
|
|
|
211
211
|
| Codex | `~/.codex/sessions/` · `~/.codex/archived_sessions/` · `~/.codex/history.jsonl` · `~/.codex/AGENTS.md` · `~/.codex/AGENTS.override.md` |
|
|
212
212
|
| Cursor | `~/.cursor/projects/*/agent-transcripts/` |
|
|
213
213
|
| opencode | `~/.local/share/opencode/opencode.db` |
|
|
214
|
+
| Gemini CLI | `~/.gemini/tmp/*/chats/` · `~/.gemini/tmp/*/memory/` · `~/.gemini/GEMINI.md` |
|
|
214
215
|
|
|
215
216
|
opencode keeps its sessions in a SQLite database. Reading it needs Node's
|
|
216
217
|
built-in `node:sqlite`, which loads without a flag from Node 22.13. On an older
|
|
@@ -224,6 +225,11 @@ scanned yet. Agent transcripts are covered; Ask history is not.
|
|
|
224
225
|
|
|
225
226
|
Claude Code transcripts it set aside (`*.jsonl.superseded-*`) are scanned too.
|
|
226
227
|
|
|
228
|
+
If you moved Claude Code's folder with `CLAUDE_CONFIG_DIR`, Codex's with
|
|
229
|
+
`CODEX_HOME`, or Gemini CLI's with `GEMINI_CLI_HOME`, the folder it names is
|
|
230
|
+
scanned as well as the default one, which still holds every session from before
|
|
231
|
+
the move.
|
|
232
|
+
|
|
227
233
|
Transcripts record credentials that were already sent. So does prompt history,
|
|
228
234
|
which keeps every prompt you typed and every long paste after the session itself
|
|
229
235
|
has been cleaned up. Instruction and memory files are worse: they are read again
|
|
@@ -231,8 +237,8 @@ at the start of the next session, so a credential in one keeps leaking until the
|
|
|
231
237
|
file is edited. All of them are scanned, and `wipe` rewrites all of them.
|
|
232
238
|
|
|
233
239
|
Nowhere else. It does not read your repos, your shell history, or your
|
|
234
|
-
environment
|
|
235
|
-
welcome.
|
|
240
|
+
environment — beyond those three variables, to know where to look. Adding
|
|
241
|
+
support for another agent is one small file — pull requests welcome.
|
|
236
242
|
|
|
237
243
|
---
|
|
238
244
|
|
package/dist/cli.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/main.ts
|
|
4
4
|
import { homedir } from "os";
|
|
5
|
+
import { join as join8 } from "path";
|
|
5
6
|
|
|
6
7
|
// src/commands.ts
|
|
7
8
|
var Command = {
|
|
@@ -2432,6 +2433,7 @@ async function listDirectories(root) {
|
|
|
2432
2433
|
}
|
|
2433
2434
|
var FileExtension = {
|
|
2434
2435
|
jsonl: ".jsonl",
|
|
2436
|
+
json: ".json",
|
|
2435
2437
|
markdown: ".md",
|
|
2436
2438
|
text: ".txt",
|
|
2437
2439
|
database: ".db"
|
|
@@ -2607,6 +2609,7 @@ var SourceName = {
|
|
|
2607
2609
|
"claude-code": "claude-code",
|
|
2608
2610
|
codex: "codex",
|
|
2609
2611
|
cursor: "cursor",
|
|
2612
|
+
"gemini-cli": "gemini-cli",
|
|
2610
2613
|
opencode: "opencode"
|
|
2611
2614
|
};
|
|
2612
2615
|
|
|
@@ -2626,20 +2629,22 @@ function objectField(source, key) {
|
|
|
2626
2629
|
// src/sources/claude-code-source.ts
|
|
2627
2630
|
var ClaudeCodeSource = class {
|
|
2628
2631
|
name = SourceName["claude-code"];
|
|
2629
|
-
#
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
+
#dir;
|
|
2633
|
+
/** `dir` is where Claude Code keeps its state: `~/.claude`, or wherever
|
|
2634
|
+
* `CLAUDE_CONFIG_DIR` moved it. */
|
|
2635
|
+
constructor(home2, dir = join2(home2, ".claude")) {
|
|
2636
|
+
this.#dir = dir;
|
|
2632
2637
|
}
|
|
2633
2638
|
async *discover() {
|
|
2634
|
-
const instructions = await resolveFile(join2(this.#
|
|
2639
|
+
const instructions = await resolveFile(join2(this.#dir, "CLAUDE.md"));
|
|
2635
2640
|
if (instructions !== null) {
|
|
2636
2641
|
yield { source: this.name, kind: FileKind.memory, path: instructions, project: this.name };
|
|
2637
2642
|
}
|
|
2638
|
-
const history = await resolveFile(join2(this.#
|
|
2643
|
+
const history = await resolveFile(join2(this.#dir, "history.jsonl"));
|
|
2639
2644
|
if (history !== null) {
|
|
2640
2645
|
yield { source: this.name, kind: FileKind.transcript, path: history, project: this.name };
|
|
2641
2646
|
}
|
|
2642
|
-
const pastes = join2(this.#
|
|
2647
|
+
const pastes = join2(this.#dir, "paste-cache");
|
|
2643
2648
|
for (const file of await listFiles(pastes, FileExtension.text)) {
|
|
2644
2649
|
yield {
|
|
2645
2650
|
source: this.name,
|
|
@@ -2648,7 +2653,7 @@ var ClaudeCodeSource = class {
|
|
|
2648
2653
|
project: this.name
|
|
2649
2654
|
};
|
|
2650
2655
|
}
|
|
2651
|
-
const root = join2(this.#
|
|
2656
|
+
const root = join2(this.#dir, "projects");
|
|
2652
2657
|
for (const slug of await listDirectories(root)) {
|
|
2653
2658
|
const dir = join2(root, slug);
|
|
2654
2659
|
let project = slug;
|
|
@@ -2692,23 +2697,25 @@ async function firstCwd(path) {
|
|
|
2692
2697
|
import { basename as basename2, join as join3 } from "path";
|
|
2693
2698
|
var CodexSource = class {
|
|
2694
2699
|
name = SourceName.codex;
|
|
2695
|
-
#
|
|
2696
|
-
|
|
2697
|
-
|
|
2700
|
+
#dir;
|
|
2701
|
+
/** `dir` is where Codex keeps its state: `~/.codex`, or wherever
|
|
2702
|
+
* `CODEX_HOME` moved it. */
|
|
2703
|
+
constructor(home2, dir = join3(home2, ".codex")) {
|
|
2704
|
+
this.#dir = dir;
|
|
2698
2705
|
}
|
|
2699
2706
|
async *discover() {
|
|
2700
2707
|
for (const name of ["AGENTS.override.md", "AGENTS.md"]) {
|
|
2701
|
-
const instructions = await resolveFile(join3(this.#
|
|
2708
|
+
const instructions = await resolveFile(join3(this.#dir, name));
|
|
2702
2709
|
if (instructions !== null) {
|
|
2703
2710
|
yield { source: this.name, kind: FileKind.memory, path: instructions, project: this.name };
|
|
2704
2711
|
}
|
|
2705
2712
|
}
|
|
2706
|
-
const history = await resolveFile(join3(this.#
|
|
2713
|
+
const history = await resolveFile(join3(this.#dir, "history.jsonl"));
|
|
2707
2714
|
if (history !== null) {
|
|
2708
2715
|
yield { source: this.name, kind: FileKind.transcript, path: history, project: this.name };
|
|
2709
2716
|
}
|
|
2710
2717
|
for (const root of ["sessions", "archived_sessions"]) {
|
|
2711
|
-
for (const dir of await walkDirectories(join3(this.#
|
|
2718
|
+
for (const dir of await walkDirectories(join3(this.#dir, root))) {
|
|
2712
2719
|
for (const file of await listFiles(dir, FileExtension.jsonl)) {
|
|
2713
2720
|
const path = join3(dir, file);
|
|
2714
2721
|
const cwd = await firstCwd2(path);
|
|
@@ -2790,8 +2797,42 @@ async function isDirectory(path) {
|
|
|
2790
2797
|
}
|
|
2791
2798
|
}
|
|
2792
2799
|
|
|
2800
|
+
// src/sources/gemini-cli-source.ts
|
|
2801
|
+
import { join as join5 } from "path";
|
|
2802
|
+
var GeminiCliSource = class {
|
|
2803
|
+
name = SourceName["gemini-cli"];
|
|
2804
|
+
#dir;
|
|
2805
|
+
/** `dir` is `~/.gemini`, or `$GEMINI_CLI_HOME/.gemini` when that is set. */
|
|
2806
|
+
constructor(home2, dir = join5(home2, ".gemini")) {
|
|
2807
|
+
this.#dir = dir;
|
|
2808
|
+
}
|
|
2809
|
+
async *discover() {
|
|
2810
|
+
const instructions = await resolveFile(join5(this.#dir, "GEMINI.md"));
|
|
2811
|
+
if (instructions !== null) {
|
|
2812
|
+
yield { source: this.name, kind: FileKind.memory, path: instructions, project: this.name };
|
|
2813
|
+
}
|
|
2814
|
+
const root = join5(this.#dir, "tmp");
|
|
2815
|
+
for (const id of await listDirectories(root)) {
|
|
2816
|
+
const project = legacyHash.test(id) ? this.name : id;
|
|
2817
|
+
for (const dir of await walkDirectories(join5(root, id, "chats"))) {
|
|
2818
|
+
for (const extension of [FileExtension.jsonl, FileExtension.json]) {
|
|
2819
|
+
for (const file of await listFiles(dir, extension)) {
|
|
2820
|
+
yield { source: this.name, kind: FileKind.transcript, path: join5(dir, file), project };
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
for (const dir of await walkDirectories(join5(root, id, "memory"))) {
|
|
2825
|
+
for (const file of await listFiles(dir, FileExtension.markdown)) {
|
|
2826
|
+
yield { source: this.name, kind: FileKind.memory, path: join5(dir, file), project };
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
};
|
|
2832
|
+
var legacyHash = /^[0-9a-f]{64}$/;
|
|
2833
|
+
|
|
2793
2834
|
// src/sources/opencode-source.ts
|
|
2794
|
-
import { basename as basename4, join as
|
|
2835
|
+
import { basename as basename4, join as join6 } from "path";
|
|
2795
2836
|
var OpencodeSource = class {
|
|
2796
2837
|
name = SourceName.opencode;
|
|
2797
2838
|
#home;
|
|
@@ -2801,9 +2842,9 @@ var OpencodeSource = class {
|
|
|
2801
2842
|
this.#load = load;
|
|
2802
2843
|
}
|
|
2803
2844
|
async *discover() {
|
|
2804
|
-
const dir =
|
|
2845
|
+
const dir = join6(this.#home, ".local", "share", "opencode");
|
|
2805
2846
|
for (const file of await listFiles(dir, FileExtension.database)) {
|
|
2806
|
-
const path =
|
|
2847
|
+
const path = join6(dir, file);
|
|
2807
2848
|
const sqlite = await this.#load();
|
|
2808
2849
|
yield {
|
|
2809
2850
|
source: this.name,
|
|
@@ -2857,6 +2898,17 @@ async function importSqlite() {
|
|
|
2857
2898
|
}
|
|
2858
2899
|
}
|
|
2859
2900
|
|
|
2901
|
+
// src/sources/state-dirs.ts
|
|
2902
|
+
import { join as join7, resolve } from "path";
|
|
2903
|
+
function stateDirs(home2, folder, moved) {
|
|
2904
|
+
const dirs = [join7(home2, folder)];
|
|
2905
|
+
if (moved !== void 0 && moved !== "") {
|
|
2906
|
+
const dir = resolve(moved);
|
|
2907
|
+
if (!dirs.includes(dir)) dirs.push(dir);
|
|
2908
|
+
}
|
|
2909
|
+
return dirs;
|
|
2910
|
+
}
|
|
2911
|
+
|
|
2860
2912
|
// src/wipe/confirm.ts
|
|
2861
2913
|
import { createInterface as createInterface3 } from "readline/promises";
|
|
2862
2914
|
var CONFIRMATION = "wipe";
|
|
@@ -3021,9 +3073,19 @@ async function main(argv) {
|
|
|
3021
3073
|
return 0;
|
|
3022
3074
|
}
|
|
3023
3075
|
const sources = [
|
|
3024
|
-
|
|
3025
|
-
|
|
3076
|
+
...stateDirs(homedir(), ".claude", process.env.CLAUDE_CONFIG_DIR).map(
|
|
3077
|
+
(dir) => new ClaudeCodeSource(homedir(), dir)
|
|
3078
|
+
),
|
|
3079
|
+
...stateDirs(homedir(), ".codex", process.env.CODEX_HOME).map(
|
|
3080
|
+
(dir) => new CodexSource(homedir(), dir)
|
|
3081
|
+
),
|
|
3026
3082
|
new CursorSource(homedir()),
|
|
3083
|
+
// GEMINI_CLI_HOME stands in for the home directory, not for `.gemini`.
|
|
3084
|
+
...stateDirs(
|
|
3085
|
+
homedir(),
|
|
3086
|
+
".gemini",
|
|
3087
|
+
process.env.GEMINI_CLI_HOME ? join8(process.env.GEMINI_CLI_HOME, ".gemini") : void 0
|
|
3088
|
+
).map((dir) => new GeminiCliSource(homedir(), dir)),
|
|
3027
3089
|
new OpencodeSource(homedir())
|
|
3028
3090
|
];
|
|
3029
3091
|
const scanner = new Scanner(new KeywordIndex(compileRules(SECRET_RULE_SOURCES)));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "whatileaked",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Scan your local Claude Code, Codex, Cursor and
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Scan your local Claude Code, Codex, Cursor, opencode and Gemini CLI transcripts for credentials you already sent to a model provider.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"imports": {
|
|
7
7
|
"#scripts/*": "./scripts/*.ts",
|