whatileaked 0.2.0 → 0.3.1
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 +8 -1
- package/dist/cli.js +88 -25
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# whatileaked
|
|
2
2
|
|
|
3
|
+
[](https://github.com/selan-ai/whatileaked/actions/workflows/ci.yml)
|
|
4
|
+
|
|
3
5
|
Your coding agent saves every conversation to your disk. If you ever pasted an
|
|
4
6
|
API key into a chat, or asked the agent to read a `.env` file, that key is now
|
|
5
7
|
sitting in a log file on your laptop — and it was uploaded to Anthropic or
|
|
@@ -193,7 +195,12 @@ npx whatileaked scan || echo "found something"
|
|
|
193
195
|
| Agent | Folder |
|
|
194
196
|
| --- | --- |
|
|
195
197
|
| Claude Code | `~/.claude/projects/` · `~/.claude/CLAUDE.md` |
|
|
196
|
-
| Codex | `~/.codex/sessions/` · `~/.codex/AGENTS.md` |
|
|
198
|
+
| Codex | `~/.codex/sessions/` · `~/.codex/archived_sessions/` · `~/.codex/AGENTS.md` |
|
|
199
|
+
| Cursor | `~/.cursor/projects/*/agent-transcripts/` |
|
|
200
|
+
|
|
201
|
+
Cursor keeps its older Ask-mode chats in SQLite rather than jsonl, and those are
|
|
202
|
+
not scanned. Reading them needs `node:sqlite`, which arrived in Node 22.5, and
|
|
203
|
+
this runs on Node 20. Agent transcripts are covered; Ask history is not.
|
|
197
204
|
|
|
198
205
|
Transcripts record credentials that were already sent. Instruction and memory
|
|
199
206
|
files are worse: they are read again at the start of the next session, so a
|
package/dist/cli.js
CHANGED
|
@@ -2524,7 +2524,8 @@ async function walkDirectories(root) {
|
|
|
2524
2524
|
// src/transcript/entry.ts
|
|
2525
2525
|
var SourceName = {
|
|
2526
2526
|
"claude-code": "claude-code",
|
|
2527
|
-
codex: "codex"
|
|
2527
|
+
codex: "codex",
|
|
2528
|
+
cursor: "cursor"
|
|
2528
2529
|
};
|
|
2529
2530
|
|
|
2530
2531
|
// src/transcript/shape.ts
|
|
@@ -2556,16 +2557,18 @@ var ClaudeCodeSource = class {
|
|
|
2556
2557
|
for (const slug of await listDirectories(root)) {
|
|
2557
2558
|
const dir = join2(root, slug);
|
|
2558
2559
|
let project = slug;
|
|
2559
|
-
for (const
|
|
2560
|
-
const
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2560
|
+
for (const nested of await walkDirectories(dir)) {
|
|
2561
|
+
for (const file of await listFiles(nested, FileExtension.jsonl)) {
|
|
2562
|
+
const path = join2(nested, file);
|
|
2563
|
+
const cwd = await firstCwd(path);
|
|
2564
|
+
if (cwd !== null) project = basename(cwd);
|
|
2565
|
+
yield {
|
|
2566
|
+
source: this.name,
|
|
2567
|
+
kind: FileKind.transcript,
|
|
2568
|
+
path,
|
|
2569
|
+
project: cwd === null ? slug : basename(cwd)
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2569
2572
|
}
|
|
2570
2573
|
for (const nested of await walkDirectories(join2(dir, "memory"))) {
|
|
2571
2574
|
for (const file of await listFiles(nested, FileExtension.markdown)) {
|
|
@@ -2603,17 +2606,18 @@ var CodexSource = class {
|
|
|
2603
2606
|
if (instructions !== null) {
|
|
2604
2607
|
yield { source: this.name, kind: FileKind.memory, path: instructions, project: this.name };
|
|
2605
2608
|
}
|
|
2606
|
-
const root
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2609
|
+
for (const root of ["sessions", "archived_sessions"]) {
|
|
2610
|
+
for (const dir of await walkDirectories(join3(this.#home, ".codex", root))) {
|
|
2611
|
+
for (const file of await listFiles(dir, FileExtension.jsonl)) {
|
|
2612
|
+
const path = join3(dir, file);
|
|
2613
|
+
const cwd = await firstCwd2(path);
|
|
2614
|
+
yield {
|
|
2615
|
+
source: this.name,
|
|
2616
|
+
kind: FileKind.transcript,
|
|
2617
|
+
path,
|
|
2618
|
+
project: cwd === null ? basename2(dir) : basename2(cwd)
|
|
2619
|
+
};
|
|
2620
|
+
}
|
|
2617
2621
|
}
|
|
2618
2622
|
}
|
|
2619
2623
|
}
|
|
@@ -2630,6 +2634,61 @@ async function firstCwd2(path) {
|
|
|
2630
2634
|
return null;
|
|
2631
2635
|
}
|
|
2632
2636
|
|
|
2637
|
+
// src/sources/cursor-source.ts
|
|
2638
|
+
import { stat as stat2 } from "fs/promises";
|
|
2639
|
+
import { basename as basename3, join as join4 } from "path";
|
|
2640
|
+
var CursorSource = class {
|
|
2641
|
+
name = SourceName.cursor;
|
|
2642
|
+
#home;
|
|
2643
|
+
constructor(home2) {
|
|
2644
|
+
this.#home = home2;
|
|
2645
|
+
}
|
|
2646
|
+
async *discover() {
|
|
2647
|
+
const root = join4(this.#home, ".cursor", "projects");
|
|
2648
|
+
for (const slug of await listDirectories(root)) {
|
|
2649
|
+
const project = await projectName(slug);
|
|
2650
|
+
for (const dir of await walkDirectories(join4(root, slug, "agent-transcripts"))) {
|
|
2651
|
+
for (const file of await listFiles(dir, FileExtension.jsonl)) {
|
|
2652
|
+
yield {
|
|
2653
|
+
source: this.name,
|
|
2654
|
+
kind: FileKind.transcript,
|
|
2655
|
+
path: join4(dir, file),
|
|
2656
|
+
project
|
|
2657
|
+
};
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
};
|
|
2663
|
+
async function projectName(slug) {
|
|
2664
|
+
const path = await resolveSlug(slug);
|
|
2665
|
+
return path === null ? slug : basename3(path);
|
|
2666
|
+
}
|
|
2667
|
+
async function resolveSlug(slug) {
|
|
2668
|
+
const parts = slug.split("-");
|
|
2669
|
+
let path = "";
|
|
2670
|
+
let index = 0;
|
|
2671
|
+
while (index < parts.length) {
|
|
2672
|
+
let end = index;
|
|
2673
|
+
let candidate = `${path}/${parts[index]}`;
|
|
2674
|
+
while (!await isDirectory(candidate) && end + 1 < parts.length) {
|
|
2675
|
+
end++;
|
|
2676
|
+
candidate = `${candidate}-${parts[end]}`;
|
|
2677
|
+
}
|
|
2678
|
+
if (!await isDirectory(candidate)) return null;
|
|
2679
|
+
path = candidate;
|
|
2680
|
+
index = end + 1;
|
|
2681
|
+
}
|
|
2682
|
+
return path === "" ? null : path;
|
|
2683
|
+
}
|
|
2684
|
+
async function isDirectory(path) {
|
|
2685
|
+
try {
|
|
2686
|
+
return (await stat2(path)).isDirectory();
|
|
2687
|
+
} catch {
|
|
2688
|
+
return false;
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2633
2692
|
// src/wipe/confirm.ts
|
|
2634
2693
|
import { createInterface as createInterface3 } from "readline/promises";
|
|
2635
2694
|
var CONFIRMATION = "wipe";
|
|
@@ -2646,7 +2705,7 @@ var TerminalPrompt = class {
|
|
|
2646
2705
|
};
|
|
2647
2706
|
|
|
2648
2707
|
// src/wipe/wipe-run.ts
|
|
2649
|
-
import { readFile, rename, stat as
|
|
2708
|
+
import { readFile, rename, stat as stat3, writeFile } from "fs/promises";
|
|
2650
2709
|
|
|
2651
2710
|
// src/wipe/redact.ts
|
|
2652
2711
|
function placeholder(rule) {
|
|
@@ -2734,7 +2793,7 @@ var WipeRun = class {
|
|
|
2734
2793
|
redacted += secrets.size;
|
|
2735
2794
|
return redactLine(line, secrets);
|
|
2736
2795
|
});
|
|
2737
|
-
const { mode } = await
|
|
2796
|
+
const { mode } = await stat3(file);
|
|
2738
2797
|
const temporary = `${file}.whatileaked-tmp`;
|
|
2739
2798
|
await writeFile(temporary, rewritten.join("\n"), { encoding: "utf8", mode });
|
|
2740
2799
|
await rename(temporary, file);
|
|
@@ -2774,7 +2833,11 @@ async function main(argv) {
|
|
|
2774
2833
|
for (const line of home()) write(line);
|
|
2775
2834
|
return 0;
|
|
2776
2835
|
}
|
|
2777
|
-
const sources = [
|
|
2836
|
+
const sources = [
|
|
2837
|
+
new ClaudeCodeSource(homedir()),
|
|
2838
|
+
new CodexSource(homedir()),
|
|
2839
|
+
new CursorSource(homedir())
|
|
2840
|
+
];
|
|
2778
2841
|
const scanner = new Scanner(new KeywordIndex(compileRules(SECRET_RULE_SOURCES)));
|
|
2779
2842
|
return command === Command.wipe ? await wipe(sources, scanner, write) : await scan(sources, scanner, write);
|
|
2780
2843
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "whatileaked",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Scan your local Claude Code and
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Scan your local Claude Code, Codex and Cursor transcripts for credentials you already sent to a model provider.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"imports": {
|
|
7
7
|
"#scripts/*": "./scripts/*.ts",
|