whatileaked 0.2.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.
- package/README.md +5 -0
- package/dist/cli.js +76 -14
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -194,6 +194,11 @@ npx whatileaked scan || echo "found something"
|
|
|
194
194
|
| --- | --- |
|
|
195
195
|
| Claude Code | `~/.claude/projects/` · `~/.claude/CLAUDE.md` |
|
|
196
196
|
| Codex | `~/.codex/sessions/` · `~/.codex/AGENTS.md` |
|
|
197
|
+
| Cursor | `~/.cursor/projects/*/agent-transcripts/` |
|
|
198
|
+
|
|
199
|
+
Cursor keeps its older Ask-mode chats in SQLite rather than jsonl, and those are
|
|
200
|
+
not scanned. Reading them needs `node:sqlite`, which arrived in Node 22.5, and
|
|
201
|
+
this runs on Node 20. Agent transcripts are covered; Ask history is not.
|
|
197
202
|
|
|
198
203
|
Transcripts record credentials that were already sent. Instruction and memory
|
|
199
204
|
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)) {
|
|
@@ -2630,6 +2633,61 @@ async function firstCwd2(path) {
|
|
|
2630
2633
|
return null;
|
|
2631
2634
|
}
|
|
2632
2635
|
|
|
2636
|
+
// src/sources/cursor-source.ts
|
|
2637
|
+
import { stat as stat2 } from "fs/promises";
|
|
2638
|
+
import { basename as basename3, join as join4 } from "path";
|
|
2639
|
+
var CursorSource = class {
|
|
2640
|
+
name = SourceName.cursor;
|
|
2641
|
+
#home;
|
|
2642
|
+
constructor(home2) {
|
|
2643
|
+
this.#home = home2;
|
|
2644
|
+
}
|
|
2645
|
+
async *discover() {
|
|
2646
|
+
const root = join4(this.#home, ".cursor", "projects");
|
|
2647
|
+
for (const slug of await listDirectories(root)) {
|
|
2648
|
+
const project = await projectName(slug);
|
|
2649
|
+
for (const dir of await walkDirectories(join4(root, slug, "agent-transcripts"))) {
|
|
2650
|
+
for (const file of await listFiles(dir, FileExtension.jsonl)) {
|
|
2651
|
+
yield {
|
|
2652
|
+
source: this.name,
|
|
2653
|
+
kind: FileKind.transcript,
|
|
2654
|
+
path: join4(dir, file),
|
|
2655
|
+
project
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
};
|
|
2662
|
+
async function projectName(slug) {
|
|
2663
|
+
const path = await resolveSlug(slug);
|
|
2664
|
+
return path === null ? slug : basename3(path);
|
|
2665
|
+
}
|
|
2666
|
+
async function resolveSlug(slug) {
|
|
2667
|
+
const parts = slug.split("-");
|
|
2668
|
+
let path = "";
|
|
2669
|
+
let index = 0;
|
|
2670
|
+
while (index < parts.length) {
|
|
2671
|
+
let end = index;
|
|
2672
|
+
let candidate = `${path}/${parts[index]}`;
|
|
2673
|
+
while (!await isDirectory(candidate) && end + 1 < parts.length) {
|
|
2674
|
+
end++;
|
|
2675
|
+
candidate = `${candidate}-${parts[end]}`;
|
|
2676
|
+
}
|
|
2677
|
+
if (!await isDirectory(candidate)) return null;
|
|
2678
|
+
path = candidate;
|
|
2679
|
+
index = end + 1;
|
|
2680
|
+
}
|
|
2681
|
+
return path === "" ? null : path;
|
|
2682
|
+
}
|
|
2683
|
+
async function isDirectory(path) {
|
|
2684
|
+
try {
|
|
2685
|
+
return (await stat2(path)).isDirectory();
|
|
2686
|
+
} catch {
|
|
2687
|
+
return false;
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
|
|
2633
2691
|
// src/wipe/confirm.ts
|
|
2634
2692
|
import { createInterface as createInterface3 } from "readline/promises";
|
|
2635
2693
|
var CONFIRMATION = "wipe";
|
|
@@ -2646,7 +2704,7 @@ var TerminalPrompt = class {
|
|
|
2646
2704
|
};
|
|
2647
2705
|
|
|
2648
2706
|
// src/wipe/wipe-run.ts
|
|
2649
|
-
import { readFile, rename, stat as
|
|
2707
|
+
import { readFile, rename, stat as stat3, writeFile } from "fs/promises";
|
|
2650
2708
|
|
|
2651
2709
|
// src/wipe/redact.ts
|
|
2652
2710
|
function placeholder(rule) {
|
|
@@ -2734,7 +2792,7 @@ var WipeRun = class {
|
|
|
2734
2792
|
redacted += secrets.size;
|
|
2735
2793
|
return redactLine(line, secrets);
|
|
2736
2794
|
});
|
|
2737
|
-
const { mode } = await
|
|
2795
|
+
const { mode } = await stat3(file);
|
|
2738
2796
|
const temporary = `${file}.whatileaked-tmp`;
|
|
2739
2797
|
await writeFile(temporary, rewritten.join("\n"), { encoding: "utf8", mode });
|
|
2740
2798
|
await rename(temporary, file);
|
|
@@ -2774,7 +2832,11 @@ async function main(argv) {
|
|
|
2774
2832
|
for (const line of home()) write(line);
|
|
2775
2833
|
return 0;
|
|
2776
2834
|
}
|
|
2777
|
-
const sources = [
|
|
2835
|
+
const sources = [
|
|
2836
|
+
new ClaudeCodeSource(homedir()),
|
|
2837
|
+
new CodexSource(homedir()),
|
|
2838
|
+
new CursorSource(homedir())
|
|
2839
|
+
];
|
|
2778
2840
|
const scanner = new Scanner(new KeywordIndex(compileRules(SECRET_RULE_SOURCES)));
|
|
2779
2841
|
return command === Command.wipe ? await wipe(sources, scanner, write) : await scan(sources, scanner, write);
|
|
2780
2842
|
}
|
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.0",
|
|
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",
|