hazmat-cli 0.1.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 +39 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +239 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/codex.d.ts +3 -0
- package/dist/src/codex.js +135 -0
- package/dist/src/codex.js.map +1 -0
- package/dist/src/detectors.d.ts +20 -0
- package/dist/src/detectors.js +120 -0
- package/dist/src/detectors.js.map +1 -0
- package/dist/src/jsonl.d.ts +4 -0
- package/dist/src/jsonl.js +136 -0
- package/dist/src/jsonl.js.map +1 -0
- package/dist/src/publish.d.ts +16 -0
- package/dist/src/publish.js +121 -0
- package/dist/src/publish.js.map +1 -0
- package/dist/src/report.d.ts +6 -0
- package/dist/src/report.js +192 -0
- package/dist/src/report.js.map +1 -0
- package/dist/src/scanner.d.ts +15 -0
- package/dist/src/scanner.js +43 -0
- package/dist/src/scanner.js.map +1 -0
- package/dist/src/scoring.d.ts +4 -0
- package/dist/src/scoring.js +27 -0
- package/dist/src/scoring.js.map +1 -0
- package/dist/src/scrub.d.ts +8 -0
- package/dist/src/scrub.js +61 -0
- package/dist/src/scrub.js.map +1 -0
- package/dist/src/sources.d.ts +8 -0
- package/dist/src/sources.js +101 -0
- package/dist/src/sources.js.map +1 -0
- package/dist/src/sqlite.d.ts +2 -0
- package/dist/src/sqlite.js +104 -0
- package/dist/src/sqlite.js.map +1 -0
- package/dist/src/sqliteScrub.d.ts +9 -0
- package/dist/src/sqliteScrub.js +128 -0
- package/dist/src/sqliteScrub.js.map +1 -0
- package/dist/src/state.d.ts +5 -0
- package/dist/src/state.js +21 -0
- package/dist/src/state.js.map +1 -0
- package/dist/src/types.d.ts +63 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/ui.d.ts +19 -0
- package/dist/src/ui.js +65 -0
- package/dist/src/ui.js.map +1 -0
- package/package.json +32 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createReadStream, createWriteStream } from "node:fs";
|
|
2
|
+
import { rename, rm } from "node:fs/promises";
|
|
3
|
+
import { createInterface } from "node:readline";
|
|
4
|
+
import { hasDetectorHint, redactText } from "./detectors.js";
|
|
5
|
+
export async function dryRunScrubTextFile(filePath) {
|
|
6
|
+
let linesScanned = 0;
|
|
7
|
+
let linesChanged = 0;
|
|
8
|
+
let redactions = 0;
|
|
9
|
+
const rl = createInterface({ input: createReadStream(filePath, { encoding: "utf8" }), crlfDelay: Infinity });
|
|
10
|
+
for await (const line of rl) {
|
|
11
|
+
linesScanned += 1;
|
|
12
|
+
if (!hasDetectorHint(line))
|
|
13
|
+
continue;
|
|
14
|
+
const result = redactText(line);
|
|
15
|
+
redactions += result.redactions;
|
|
16
|
+
if (result.text !== line)
|
|
17
|
+
linesChanged += 1;
|
|
18
|
+
}
|
|
19
|
+
return { path: filePath, linesScanned, linesChanged, redactions };
|
|
20
|
+
}
|
|
21
|
+
export async function scrubTextFileInPlace(filePath) {
|
|
22
|
+
const tempPath = `${filePath}.hazmat-tmp-${process.pid}`;
|
|
23
|
+
let linesScanned = 0;
|
|
24
|
+
let linesChanged = 0;
|
|
25
|
+
let redactions = 0;
|
|
26
|
+
const rl = createInterface({ input: createReadStream(filePath, { encoding: "utf8" }), crlfDelay: Infinity });
|
|
27
|
+
const out = createWriteStream(tempPath, { encoding: "utf8", flags: "wx" });
|
|
28
|
+
try {
|
|
29
|
+
for await (const line of rl) {
|
|
30
|
+
linesScanned += 1;
|
|
31
|
+
let nextLine = line;
|
|
32
|
+
if (hasDetectorHint(line)) {
|
|
33
|
+
const result = redactText(line);
|
|
34
|
+
nextLine = result.text;
|
|
35
|
+
redactions += result.redactions;
|
|
36
|
+
if (nextLine !== line)
|
|
37
|
+
linesChanged += 1;
|
|
38
|
+
}
|
|
39
|
+
if (!out.write(`${nextLine}\n`))
|
|
40
|
+
await onceDrain(out);
|
|
41
|
+
}
|
|
42
|
+
await closeWriteStream(out);
|
|
43
|
+
await rename(tempPath, filePath);
|
|
44
|
+
return { path: filePath, linesScanned, linesChanged, redactions };
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
out.destroy();
|
|
48
|
+
await rm(tempPath, { force: true });
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function onceDrain(stream) {
|
|
53
|
+
return new Promise((resolve) => stream.once("drain", resolve));
|
|
54
|
+
}
|
|
55
|
+
function closeWriteStream(stream) {
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
stream.end(() => resolve());
|
|
58
|
+
stream.once("error", reject);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=scrub.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrub.js","sourceRoot":"","sources":["../../src/scrub.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Harness } from "./types.js";
|
|
2
|
+
export type ScanSource = {
|
|
3
|
+
harness: Harness;
|
|
4
|
+
kind: "file" | "sqlite";
|
|
5
|
+
path: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function discoverDefaultSources(): Promise<ScanSource[]>;
|
|
8
|
+
export declare function discoverPathSources(inputPaths: string[]): Promise<ScanSource[]>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { access, readdir, stat } from "node:fs/promises";
|
|
4
|
+
import { discoverCodexFiles } from "./codex.js";
|
|
5
|
+
import { discoverFiles } from "./jsonl.js";
|
|
6
|
+
export async function discoverDefaultSources() {
|
|
7
|
+
const sources = [];
|
|
8
|
+
for (const file of await discoverCodexFiles())
|
|
9
|
+
sources.push({ harness: "codex", kind: "file", path: file });
|
|
10
|
+
for (const file of await discoverFiles([
|
|
11
|
+
path.join(homedir(), ".claude", "projects"),
|
|
12
|
+
path.join(homedir(), ".claude", "history.jsonl"),
|
|
13
|
+
]))
|
|
14
|
+
sources.push({ harness: "claude", kind: "file", path: file });
|
|
15
|
+
for (const file of await discoverFiles([path.join(homedir(), ".pi", "agent", "sessions")])) {
|
|
16
|
+
sources.push({ harness: "pi", kind: "file", path: file });
|
|
17
|
+
}
|
|
18
|
+
const opencodeDb = path.join(homedir(), ".local", "share", "opencode", "opencode.db");
|
|
19
|
+
if (await exists(opencodeDb))
|
|
20
|
+
sources.push({ harness: "opencode", kind: "sqlite", path: opencodeDb });
|
|
21
|
+
for (const cursorPath of await cursorDbCandidates())
|
|
22
|
+
sources.push({ harness: "cursor", kind: "sqlite", path: cursorPath });
|
|
23
|
+
return sources;
|
|
24
|
+
}
|
|
25
|
+
export async function discoverPathSources(inputPaths) {
|
|
26
|
+
const sqliteSources = [];
|
|
27
|
+
const textRoots = [];
|
|
28
|
+
for (const input of inputPaths) {
|
|
29
|
+
const expanded = expandTilde(input);
|
|
30
|
+
if (await exists(expanded)) {
|
|
31
|
+
const s = await stat(expanded);
|
|
32
|
+
if (s.isFile() && isSqlitePath(expanded))
|
|
33
|
+
sqliteSources.push({ harness: inferHarness(expanded), kind: "sqlite", path: expanded });
|
|
34
|
+
else
|
|
35
|
+
textRoots.push(input);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const files = await discoverFiles(textRoots);
|
|
39
|
+
return [...sqliteSources, ...files.map((file) => ({ harness: inferHarness(file), kind: isSqlitePath(file) ? "sqlite" : "file", path: file }))];
|
|
40
|
+
}
|
|
41
|
+
async function cursorDbCandidates() {
|
|
42
|
+
const userDir = process.platform === "darwin"
|
|
43
|
+
? path.join(homedir(), "Library", "Application Support", "Cursor", "User")
|
|
44
|
+
: process.platform === "win32"
|
|
45
|
+
? path.join(process.env.APPDATA || path.join(homedir(), "AppData", "Roaming"), "Cursor", "User")
|
|
46
|
+
: path.join(process.env.XDG_CONFIG_HOME || path.join(homedir(), ".config"), "Cursor", "User");
|
|
47
|
+
const candidates = new Set();
|
|
48
|
+
const globalState = path.join(userDir, "globalStorage", "state.vscdb");
|
|
49
|
+
if (await exists(globalState))
|
|
50
|
+
candidates.add(globalState);
|
|
51
|
+
const workspaceStorage = path.join(userDir, "workspaceStorage");
|
|
52
|
+
if (await exists(workspaceStorage)) {
|
|
53
|
+
for await (const file of walk(workspaceStorage))
|
|
54
|
+
if (file.endsWith("state.vscdb"))
|
|
55
|
+
candidates.add(file);
|
|
56
|
+
}
|
|
57
|
+
return [...candidates];
|
|
58
|
+
}
|
|
59
|
+
function expandTilde(input) {
|
|
60
|
+
if (input === "~")
|
|
61
|
+
return homedir();
|
|
62
|
+
if (input.startsWith("~/"))
|
|
63
|
+
return path.join(homedir(), input.slice(2));
|
|
64
|
+
return path.resolve(input);
|
|
65
|
+
}
|
|
66
|
+
function inferHarness(file) {
|
|
67
|
+
if (file.includes(`${path.sep}.claude${path.sep}`))
|
|
68
|
+
return "claude";
|
|
69
|
+
if (file.includes(`${path.sep}.pi${path.sep}`))
|
|
70
|
+
return "pi";
|
|
71
|
+
if (file.includes(`${path.sep}.codex${path.sep}`))
|
|
72
|
+
return "codex";
|
|
73
|
+
if (file.includes(`opencode`))
|
|
74
|
+
return "opencode";
|
|
75
|
+
if (file.includes(`Cursor`))
|
|
76
|
+
return "cursor";
|
|
77
|
+
return "unknown";
|
|
78
|
+
}
|
|
79
|
+
async function* walk(dir) {
|
|
80
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
81
|
+
for (const entry of entries) {
|
|
82
|
+
const fullPath = path.join(dir, entry.name);
|
|
83
|
+
if (entry.isDirectory())
|
|
84
|
+
yield* walk(fullPath);
|
|
85
|
+
else if (entry.isFile())
|
|
86
|
+
yield fullPath;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async function exists(filePath) {
|
|
90
|
+
try {
|
|
91
|
+
await access(filePath);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function isSqlitePath(file) {
|
|
99
|
+
return file.endsWith(".db") || file.endsWith(".vscdb");
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=sources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sources.js","sourceRoot":"","sources":["../../src/sources.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAS3C,MAAM,CAAC,KAAK,UAAU,sBAAsB;IAC1C,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,MAAM,kBAAkB,EAAE;QAAE,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5G,KAAK,MAAM,IAAI,IAAI,MAAM,aAAa,CAAC;QACrC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC;QAC3C,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,eAAe,CAAC;KACjD,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAElE,KAAK,MAAM,IAAI,IAAI,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3F,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;IACtF,IAAI,MAAM,MAAM,CAAC,UAAU,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IAEtG,KAAK,MAAM,UAAU,IAAI,MAAM,kBAAkB,EAAE;QAAE,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IAC3H,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,UAAoB;IAC5D,MAAM,aAAa,GAAiB,EAAE,CAAC;IACvC,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3B,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC/B,IAAI,CAAC,CAAC,MAAM,EAAE,IAAI,YAAY,CAAC,QAAQ,CAAC;gBAAE,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;;gBAC7H,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,SAAS,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,aAAa,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAiB,CAAC,CAAC,CAAC,MAAe,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACnK,CAAC;AAED,KAAK,UAAU,kBAAkB;IAC/B,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAC3C,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,CAAC;QAC1E,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;YAC5B,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC;YAChG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAElG,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC;IACvE,IAAI,MAAM,MAAM,CAAC,WAAW,CAAC;QAAE,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC3D,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;IAChE,IAAI,MAAM,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACnC,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,IAAI,CAAC,gBAAgB,CAAC;YAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC;gBAAE,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC1G,CAAC;IACD,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC;AACzB,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,IAAI,KAAK,KAAK,GAAG;QAAE,OAAO,OAAO,EAAE,CAAC;IACpC,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,UAAU,IAAI,CAAC,GAAG,EAAE,CAAC;QAAE,OAAO,QAAQ,CAAC;IACpE,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5D,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,SAAS,IAAI,CAAC,GAAG,EAAE,CAAC;QAAE,OAAO,OAAO,CAAC;IAClE,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC;IACjD,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC7C,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,GAAW;IAC9B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,KAAK,CAAC,WAAW,EAAE;YAAE,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;aAC1C,IAAI,KAAK,CAAC,MAAM,EAAE;YAAE,MAAM,QAAQ,CAAC;IAC1C,CAAC;AACH,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,QAAgB;IACpC,IAAI,CAAC;QAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;AACtE,CAAC;AAED,SAAS,YAAY,CAAC,IAAY;IAChC,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { buildSourceReport, createFindingState, scanCandidateText } from "./scanner.js";
|
|
4
|
+
const SQL_HINTS = [
|
|
5
|
+
".env", "PRIVATE KEY", "sk-", "sk-ant-", "ghp_", "gho_", "ghu_", "ghs_", "ghr_",
|
|
6
|
+
"postgres://", "postgresql://", "mysql://", "mongodb://", "mongodb+srv://", "redis://",
|
|
7
|
+
"eyJ", "webhook", "hooks.slack", "discord.com/api/webhooks", "discordapp.com/api/webhooks",
|
|
8
|
+
"KEY", "TOKEN", "SECRET", "PASSWORD", "DATABASE_URL", "DB_URL", "AUTH",
|
|
9
|
+
"id_rsa", "id_ed25519", ".pem", ".p12", ".pfx", "wallet", "keypair",
|
|
10
|
+
];
|
|
11
|
+
export async function scanSQLiteSource(filePath, harness) {
|
|
12
|
+
if (!(await exists(filePath)))
|
|
13
|
+
return null;
|
|
14
|
+
if (harness === "opencode")
|
|
15
|
+
return scanOpenCodeDb(filePath);
|
|
16
|
+
if (harness === "cursor")
|
|
17
|
+
return scanCursorDb(filePath);
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
async function scanOpenCodeDb(filePath) {
|
|
21
|
+
const state = createFindingState();
|
|
22
|
+
let snippetsScanned = 0;
|
|
23
|
+
const targets = [
|
|
24
|
+
{ table: "message", column: "data" },
|
|
25
|
+
{ table: "part", column: "data" },
|
|
26
|
+
{ table: "session_message", column: "data" },
|
|
27
|
+
{ table: "session_input", column: "prompt" },
|
|
28
|
+
];
|
|
29
|
+
for (const target of targets) {
|
|
30
|
+
for (const hint of SQL_HINTS) {
|
|
31
|
+
const snippets = await sqliteSnippetRows(filePath, target.table, target.column, hint).catch(() => []);
|
|
32
|
+
for (const snippet of snippets) {
|
|
33
|
+
snippetsScanned += 1;
|
|
34
|
+
scanCandidateText(snippet, state);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return buildSourceReport({ harness: "opencode", path: filePath, linesScanned: snippetsScanned, state });
|
|
39
|
+
}
|
|
40
|
+
async function scanCursorDb(filePath) {
|
|
41
|
+
const state = createFindingState();
|
|
42
|
+
let snippetsScanned = 0;
|
|
43
|
+
const targets = [
|
|
44
|
+
{ table: "ItemTable", column: "value" },
|
|
45
|
+
{ table: "cursorDiskKV", column: "value" },
|
|
46
|
+
];
|
|
47
|
+
for (const target of targets) {
|
|
48
|
+
for (const hint of SQL_HINTS) {
|
|
49
|
+
const snippets = await sqliteSnippetRows(filePath, target.table, target.column, hint).catch(() => []);
|
|
50
|
+
for (const snippet of snippets) {
|
|
51
|
+
snippetsScanned += 1;
|
|
52
|
+
scanCandidateText(snippet, state);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return buildSourceReport({ harness: "cursor", path: filePath, linesScanned: snippetsScanned, state });
|
|
57
|
+
}
|
|
58
|
+
function sqliteSnippetRows(dbPath, table, column, hint) {
|
|
59
|
+
const safeHint = hint.replaceAll("'", "''");
|
|
60
|
+
const safeTable = quoteIdent(table);
|
|
61
|
+
const safeColumn = quoteIdent(column);
|
|
62
|
+
const query = `SELECT substr(${safeColumn}, max(instr(${safeColumn}, '${safeHint}') - 1000, 1), 4000) AS text FROM ${safeTable} WHERE typeof(${safeColumn}) = 'text' AND ${safeColumn} LIKE '%${safeHint}%'`;
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
const child = spawn("sqlite3", ["-readonly", "-json", dbPath, query], { stdio: ["ignore", "pipe", "pipe"] });
|
|
65
|
+
let stdout = "";
|
|
66
|
+
let stderr = "";
|
|
67
|
+
child.stdout.setEncoding("utf8");
|
|
68
|
+
child.stderr.setEncoding("utf8");
|
|
69
|
+
child.stdout.on("data", (chunk) => { stdout += chunk; });
|
|
70
|
+
child.stderr.on("data", (chunk) => { stderr += chunk; });
|
|
71
|
+
child.on("error", reject);
|
|
72
|
+
child.on("close", (code) => {
|
|
73
|
+
if (code !== 0) {
|
|
74
|
+
reject(new Error(stderr.trim() || `sqlite3 exited with ${code}`));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const trimmed = stdout.trim();
|
|
78
|
+
if (!trimmed) {
|
|
79
|
+
resolve([]);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const rows = JSON.parse(trimmed);
|
|
84
|
+
resolve(rows.map((row) => typeof row.text === "string" ? row.text : "").filter(Boolean));
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
reject(error);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function quoteIdent(identifier) {
|
|
93
|
+
return `"${identifier.replaceAll('"', '""')}"`;
|
|
94
|
+
}
|
|
95
|
+
async function exists(filePath) {
|
|
96
|
+
try {
|
|
97
|
+
await access(filePath);
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=sqlite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/sqlite.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Harness } from "./types.js";
|
|
2
|
+
export type DbScrubResult = {
|
|
3
|
+
path: string;
|
|
4
|
+
rowsScanned: number;
|
|
5
|
+
rowsChanged: number;
|
|
6
|
+
redactions: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function dryRunSQLiteScrub(filePath: string, harness: Harness): Promise<DbScrubResult | null>;
|
|
9
|
+
export declare function scrubSQLiteInPlace(filePath: string, harness: Harness): Promise<DbScrubResult | null>;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { redactText } from "./detectors.js";
|
|
3
|
+
const SQL_HINTS = [
|
|
4
|
+
".env", "PRIVATE KEY", "sk-", "sk-ant-", "ghp_", "gho_", "ghu_", "ghs_", "ghr_",
|
|
5
|
+
"postgres://", "postgresql://", "mysql://", "mongodb://", "mongodb+srv://", "redis://",
|
|
6
|
+
"eyJ", "webhook", "hooks.slack", "discord.com/api/webhooks", "discordapp.com/api/webhooks",
|
|
7
|
+
"KEY", "TOKEN", "SECRET", "PASSWORD", "DATABASE_URL", "DB_URL", "AUTH",
|
|
8
|
+
];
|
|
9
|
+
export async function dryRunSQLiteScrub(filePath, harness) {
|
|
10
|
+
return scrubSQLite(filePath, harness, { dryRun: true });
|
|
11
|
+
}
|
|
12
|
+
export async function scrubSQLiteInPlace(filePath, harness) {
|
|
13
|
+
return scrubSQLite(filePath, harness, { dryRun: false });
|
|
14
|
+
}
|
|
15
|
+
async function scrubSQLite(filePath, harness, options) {
|
|
16
|
+
const targets = targetsForHarness(harness);
|
|
17
|
+
if (targets.length === 0)
|
|
18
|
+
return null;
|
|
19
|
+
const db = openDatabase(filePath, !options.dryRun);
|
|
20
|
+
let rowsScanned = 0;
|
|
21
|
+
let rowsChanged = 0;
|
|
22
|
+
let redactions = 0;
|
|
23
|
+
try {
|
|
24
|
+
for (const target of targets) {
|
|
25
|
+
if (!tableHasColumn(db, target.table, target.column) || !tableHasColumn(db, target.table, target.idColumn))
|
|
26
|
+
continue;
|
|
27
|
+
const candidateIds = collectCandidateIds(db, target);
|
|
28
|
+
const select = db.prepare(`SELECT ${quoteIdent(target.idColumn)} AS id, ${quoteIdent(target.column)} AS value FROM ${quoteIdent(target.table)} WHERE ${quoteIdent(target.idColumn)} = ?`);
|
|
29
|
+
const update = options.dryRun
|
|
30
|
+
? null
|
|
31
|
+
: db.prepare(`UPDATE ${quoteIdent(target.table)} SET ${quoteIdent(target.column)} = ? WHERE ${quoteIdent(target.idColumn)} = ?`);
|
|
32
|
+
for (const id of candidateIds) {
|
|
33
|
+
const rows = select.all(id);
|
|
34
|
+
const row = rows[0];
|
|
35
|
+
const value = row?.value;
|
|
36
|
+
if (typeof value !== "string")
|
|
37
|
+
continue;
|
|
38
|
+
rowsScanned += 1;
|
|
39
|
+
const result = redactText(value);
|
|
40
|
+
if (result.text === value)
|
|
41
|
+
continue;
|
|
42
|
+
rowsChanged += 1;
|
|
43
|
+
redactions += result.redactions;
|
|
44
|
+
update?.run(result.text, id);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (!options.dryRun && rowsChanged > 0) {
|
|
48
|
+
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
49
|
+
db.exec("VACUUM");
|
|
50
|
+
}
|
|
51
|
+
return { path: filePath, rowsScanned, rowsChanged, redactions };
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
db.close();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function targetsForHarness(harness) {
|
|
58
|
+
if (harness === "opencode") {
|
|
59
|
+
return [
|
|
60
|
+
{ table: "message", column: "data", idColumn: "id" },
|
|
61
|
+
{ table: "part", column: "data", idColumn: "id" },
|
|
62
|
+
{ table: "session_message", column: "data", idColumn: "id" },
|
|
63
|
+
{ table: "session_input", column: "prompt", idColumn: "id" },
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
if (harness === "cursor") {
|
|
67
|
+
return [
|
|
68
|
+
{ table: "ItemTable", column: "value", idColumn: "key" },
|
|
69
|
+
{ table: "cursorDiskKV", column: "value", idColumn: "key" },
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
function collectCandidateIds(db, target) {
|
|
75
|
+
const ids = new Set();
|
|
76
|
+
const idCol = quoteIdent(target.idColumn);
|
|
77
|
+
const valueCol = quoteIdent(target.column);
|
|
78
|
+
const table = quoteIdent(target.table);
|
|
79
|
+
for (const hint of SQL_HINTS) {
|
|
80
|
+
const rows = db.prepare(`SELECT ${idCol} AS id FROM ${table} WHERE typeof(${valueCol}) = 'text' AND ${valueCol} LIKE ?`).all(`%${hint}%`);
|
|
81
|
+
for (const row of rows)
|
|
82
|
+
ids.add(row.id);
|
|
83
|
+
}
|
|
84
|
+
return [...ids];
|
|
85
|
+
}
|
|
86
|
+
function tableHasColumn(db, table, column) {
|
|
87
|
+
try {
|
|
88
|
+
const rows = db.prepare(`PRAGMA table_info(${quoteIdent(table)})`).all();
|
|
89
|
+
return rows.some((row) => row.name === column);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function openDatabase(filePath, writable) {
|
|
96
|
+
const sqlite = requireNodeSqlite();
|
|
97
|
+
return new sqlite.DatabaseSync(filePath, writable ? {} : { readOnly: true });
|
|
98
|
+
}
|
|
99
|
+
function requireNodeSqlite() {
|
|
100
|
+
if (!supportsStableNodeSqlite()) {
|
|
101
|
+
throw new Error(`OpenCode/Cursor DB scrub requires Node >= 22.13.0 because it uses node:sqlite. ` +
|
|
102
|
+
`Your Node: ${process.version}. JSONL/text scrub still works.`);
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const require = createRequire(import.meta.url);
|
|
106
|
+
return require("node:sqlite");
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
throw new Error(`OpenCode/Cursor DB scrub requires Node >= 22.13.0 because it uses node:sqlite. ` +
|
|
110
|
+
`Your Node: ${process.version}. JSONL/text scrub still works.`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function supportsStableNodeSqlite() {
|
|
114
|
+
const match = process.versions.node.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
115
|
+
if (!match)
|
|
116
|
+
return false;
|
|
117
|
+
const major = Number(match[1]);
|
|
118
|
+
const minor = Number(match[2]);
|
|
119
|
+
if (major > 22)
|
|
120
|
+
return true;
|
|
121
|
+
if (major < 22)
|
|
122
|
+
return false;
|
|
123
|
+
return minor >= 13;
|
|
124
|
+
}
|
|
125
|
+
function quoteIdent(identifier) {
|
|
126
|
+
return `"${identifier.replaceAll('"', '""')}"`;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=sqliteScrub.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqliteScrub.js","sourceRoot":"","sources":["../../src/sqliteScrub.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ScanReport } from "./types.js";
|
|
2
|
+
export declare const stateDir: string;
|
|
3
|
+
export declare const latestScanPath: string;
|
|
4
|
+
export declare function saveLatestScan(report: ScanReport): Promise<void>;
|
|
5
|
+
export declare function loadLatestScan(): Promise<ScanReport | null>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
export const stateDir = path.join(homedir(), ".hazmat");
|
|
5
|
+
export const latestScanPath = path.join(stateDir, "latest-scan.json");
|
|
6
|
+
export async function saveLatestScan(report) {
|
|
7
|
+
await mkdir(stateDir, { recursive: true });
|
|
8
|
+
await writeFile(latestScanPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
|
|
9
|
+
}
|
|
10
|
+
export async function loadLatestScan() {
|
|
11
|
+
try {
|
|
12
|
+
const raw = await readFile(latestScanPath, "utf8");
|
|
13
|
+
return JSON.parse(raw);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
17
|
+
return null;
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AACxD,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;AAEtE,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAkB;IACrD,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,SAAS,CAAC,cAAc,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAe,CAAC;IACvC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACtF,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export type Risk = "low" | "medium" | "high";
|
|
2
|
+
export type Harness = "codex" | "claude" | "pi" | "opencode" | "cursor" | "unknown";
|
|
3
|
+
export type SecretFinding = {
|
|
4
|
+
kind: "secret";
|
|
5
|
+
label: string;
|
|
6
|
+
fingerprint: string;
|
|
7
|
+
count: number;
|
|
8
|
+
};
|
|
9
|
+
export type FileRefFinding = {
|
|
10
|
+
kind: "file-reference";
|
|
11
|
+
label: string;
|
|
12
|
+
value: string;
|
|
13
|
+
count: number;
|
|
14
|
+
};
|
|
15
|
+
export type SourceReport = {
|
|
16
|
+
harness: Harness;
|
|
17
|
+
path: string;
|
|
18
|
+
linesScanned: number;
|
|
19
|
+
risk: Risk;
|
|
20
|
+
secrets: SecretFinding[];
|
|
21
|
+
fileReferences: FileRefFinding[];
|
|
22
|
+
projectPath?: string;
|
|
23
|
+
sessionId?: string;
|
|
24
|
+
};
|
|
25
|
+
export type ScanReport = {
|
|
26
|
+
version: 1;
|
|
27
|
+
generatedAt: string;
|
|
28
|
+
sources: SourceReport[];
|
|
29
|
+
summary: {
|
|
30
|
+
filesScanned: number;
|
|
31
|
+
linesScanned: number;
|
|
32
|
+
sensitiveFiles: number;
|
|
33
|
+
highRiskFiles: number;
|
|
34
|
+
mediumRiskFiles: number;
|
|
35
|
+
secretFindings: number;
|
|
36
|
+
fileReferenceFindings: number;
|
|
37
|
+
score: number;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export type SharePayload = {
|
|
41
|
+
version: 1;
|
|
42
|
+
generatedAt: string;
|
|
43
|
+
score: number;
|
|
44
|
+
sources: string[];
|
|
45
|
+
totals: {
|
|
46
|
+
filesScanned: number;
|
|
47
|
+
sensitiveFiles: number;
|
|
48
|
+
highRiskFiles: number;
|
|
49
|
+
mediumRiskFiles: number;
|
|
50
|
+
secretFindings: number;
|
|
51
|
+
fileReferenceFindings: number;
|
|
52
|
+
};
|
|
53
|
+
secretClasses: Array<{
|
|
54
|
+
label: string;
|
|
55
|
+
count: number;
|
|
56
|
+
unique: number;
|
|
57
|
+
}>;
|
|
58
|
+
fileReferenceGroups: Array<{
|
|
59
|
+
label: string;
|
|
60
|
+
count: number;
|
|
61
|
+
unique: number;
|
|
62
|
+
}>;
|
|
63
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
|
package/dist/src/ui.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ScanReport } from "./types.js";
|
|
2
|
+
export declare const c: {
|
|
3
|
+
title: import("chalk").ChalkInstance;
|
|
4
|
+
heading: import("chalk").ChalkInstance;
|
|
5
|
+
muted: import("chalk").ChalkInstance;
|
|
6
|
+
good: import("chalk").ChalkInstance;
|
|
7
|
+
warn: import("chalk").ChalkInstance;
|
|
8
|
+
danger: import("chalk").ChalkInstance;
|
|
9
|
+
accent: import("chalk").ChalkInstance;
|
|
10
|
+
value: import("chalk").ChalkInstance;
|
|
11
|
+
};
|
|
12
|
+
export declare function banner(): string;
|
|
13
|
+
export declare function formatScore(score: number): string;
|
|
14
|
+
export declare function riskColor(risk: string): (value: string) => string;
|
|
15
|
+
export declare function printBanner(): void;
|
|
16
|
+
export declare function progressLine(status: "start" | "done", index: number, total: number, harness: string, path: string): string;
|
|
17
|
+
export declare function savedLine(path: string): string;
|
|
18
|
+
export declare function scrubSummary(changedFiles: number, sources: number, redactions: number, changedLines: number, dryRun: boolean, skipped?: number): string;
|
|
19
|
+
export declare function reportHeader(report: ScanReport): string;
|
package/dist/src/ui.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
export const c = {
|
|
3
|
+
title: chalk.hex("#f2c94c").bold,
|
|
4
|
+
heading: chalk.bold,
|
|
5
|
+
muted: chalk.gray,
|
|
6
|
+
good: chalk.green,
|
|
7
|
+
warn: chalk.yellow,
|
|
8
|
+
danger: chalk.red,
|
|
9
|
+
accent: chalk.cyan,
|
|
10
|
+
value: chalk.white.bold,
|
|
11
|
+
};
|
|
12
|
+
export function banner() {
|
|
13
|
+
const art = [
|
|
14
|
+
"██╗ ██╗ █████╗ ███████╗███╗ ███╗ █████╗ ████████╗",
|
|
15
|
+
"██║ ██║██╔══██╗╚══███╔╝████╗ ████║██╔══██╗╚══██╔══╝",
|
|
16
|
+
"███████║███████║ ███╔╝ ██╔████╔██║███████║ ██║ ",
|
|
17
|
+
"██╔══██║██╔══██║ ███╔╝ ██║╚██╔╝██║██╔══██║ ██║ ",
|
|
18
|
+
"██║ ██║██║ ██║███████╗██║ ╚═╝ ██║██║ ██║ ██║ ",
|
|
19
|
+
"╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ",
|
|
20
|
+
];
|
|
21
|
+
return art.map((line) => c.title(line)).join("\n");
|
|
22
|
+
}
|
|
23
|
+
export function formatScore(score) {
|
|
24
|
+
const text = `${score}/100`;
|
|
25
|
+
if (score >= 80)
|
|
26
|
+
return c.good(text);
|
|
27
|
+
if (score >= 40)
|
|
28
|
+
return c.warn(text);
|
|
29
|
+
return c.danger(text);
|
|
30
|
+
}
|
|
31
|
+
export function riskColor(risk) {
|
|
32
|
+
if (risk === "high")
|
|
33
|
+
return c.danger;
|
|
34
|
+
if (risk === "medium")
|
|
35
|
+
return c.warn;
|
|
36
|
+
return c.good;
|
|
37
|
+
}
|
|
38
|
+
export function printBanner() {
|
|
39
|
+
console.log(banner());
|
|
40
|
+
console.log(c.muted("Local exposure scanner for AI coding-agent transcripts"));
|
|
41
|
+
console.log("");
|
|
42
|
+
}
|
|
43
|
+
export function progressLine(status, index, total, harness, path) {
|
|
44
|
+
const label = status === "start" ? c.warn("start") : c.good("done ");
|
|
45
|
+
return `${c.muted("[")}${label} ${c.muted(`${index}/${total}`)}${c.muted("]")} ${c.accent(harness)} ${c.muted(path)}`;
|
|
46
|
+
}
|
|
47
|
+
export function savedLine(path) {
|
|
48
|
+
return `${c.good("✓")} Saved latest scan: ${c.muted(path)}`;
|
|
49
|
+
}
|
|
50
|
+
export function scrubSummary(changedFiles, sources, redactions, changedLines, dryRun, skipped = 0) {
|
|
51
|
+
const verb = dryRun ? "Would scrub" : "Scrubbed";
|
|
52
|
+
const redacted = dryRun ? "Would redact" : "Redacted";
|
|
53
|
+
const lines = [
|
|
54
|
+
`${c.good("✓")} ${verb} ${c.value(String(changedFiles))}/${sources} sources.`,
|
|
55
|
+
`${redacted} ${c.value(String(redactions))} secret-like values across ${c.value(String(changedLines))} lines/rows.`,
|
|
56
|
+
];
|
|
57
|
+
if (skipped > 0)
|
|
58
|
+
lines.push(`${c.warn("!")} Skipped ${c.value(String(skipped))} unreadable/unsupported sources.`);
|
|
59
|
+
lines.push(c.muted("No raw secret values were printed."));
|
|
60
|
+
return lines.join("\n");
|
|
61
|
+
}
|
|
62
|
+
export function reportHeader(report) {
|
|
63
|
+
return `${c.heading("Generated:")} ${c.muted(report.generatedAt)}`;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=ui.js.map
|