clawhatch 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.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +348 -0
  3. package/dist/checks/cloud-sync.d.ts +10 -0
  4. package/dist/checks/cloud-sync.d.ts.map +1 -0
  5. package/dist/checks/cloud-sync.js +62 -0
  6. package/dist/checks/cloud-sync.js.map +1 -0
  7. package/dist/checks/data-protection.d.ts +9 -0
  8. package/dist/checks/data-protection.d.ts.map +1 -0
  9. package/dist/checks/data-protection.js +197 -0
  10. package/dist/checks/data-protection.js.map +1 -0
  11. package/dist/checks/identity.d.ts +14 -0
  12. package/dist/checks/identity.d.ts.map +1 -0
  13. package/dist/checks/identity.js +327 -0
  14. package/dist/checks/identity.js.map +1 -0
  15. package/dist/checks/model.d.ts +10 -0
  16. package/dist/checks/model.d.ts.map +1 -0
  17. package/dist/checks/model.js +337 -0
  18. package/dist/checks/model.js.map +1 -0
  19. package/dist/checks/network.d.ts +9 -0
  20. package/dist/checks/network.d.ts.map +1 -0
  21. package/dist/checks/network.js +177 -0
  22. package/dist/checks/network.js.map +1 -0
  23. package/dist/checks/operational.d.ts +9 -0
  24. package/dist/checks/operational.d.ts.map +1 -0
  25. package/dist/checks/operational.js +158 -0
  26. package/dist/checks/operational.js.map +1 -0
  27. package/dist/checks/sandbox.d.ts +9 -0
  28. package/dist/checks/sandbox.d.ts.map +1 -0
  29. package/dist/checks/sandbox.js +135 -0
  30. package/dist/checks/sandbox.js.map +1 -0
  31. package/dist/checks/secrets.d.ts +9 -0
  32. package/dist/checks/secrets.d.ts.map +1 -0
  33. package/dist/checks/secrets.js +816 -0
  34. package/dist/checks/secrets.js.map +1 -0
  35. package/dist/checks/skills.d.ts +9 -0
  36. package/dist/checks/skills.d.ts.map +1 -0
  37. package/dist/checks/skills.js +303 -0
  38. package/dist/checks/skills.js.map +1 -0
  39. package/dist/checks/tools.d.ts +9 -0
  40. package/dist/checks/tools.d.ts.map +1 -0
  41. package/dist/checks/tools.js +397 -0
  42. package/dist/checks/tools.js.map +1 -0
  43. package/dist/discover.d.ts +22 -0
  44. package/dist/discover.d.ts.map +1 -0
  45. package/dist/discover.js +281 -0
  46. package/dist/discover.js.map +1 -0
  47. package/dist/fixer.d.ts +16 -0
  48. package/dist/fixer.d.ts.map +1 -0
  49. package/dist/fixer.js +361 -0
  50. package/dist/fixer.js.map +1 -0
  51. package/dist/index.d.ts +16 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +230 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/init.d.ts +14 -0
  56. package/dist/init.d.ts.map +1 -0
  57. package/dist/init.js +108 -0
  58. package/dist/init.js.map +1 -0
  59. package/dist/notify.d.ts +28 -0
  60. package/dist/notify.d.ts.map +1 -0
  61. package/dist/notify.js +217 -0
  62. package/dist/notify.js.map +1 -0
  63. package/dist/parsers/config.d.ts +16 -0
  64. package/dist/parsers/config.d.ts.map +1 -0
  65. package/dist/parsers/config.js +54 -0
  66. package/dist/parsers/config.js.map +1 -0
  67. package/dist/parsers/env.d.ts +6 -0
  68. package/dist/parsers/env.d.ts.map +1 -0
  69. package/dist/parsers/env.js +35 -0
  70. package/dist/parsers/env.js.map +1 -0
  71. package/dist/parsers/jsonl.d.ts +12 -0
  72. package/dist/parsers/jsonl.d.ts.map +1 -0
  73. package/dist/parsers/jsonl.js +61 -0
  74. package/dist/parsers/jsonl.js.map +1 -0
  75. package/dist/parsers/markdown.d.ts +17 -0
  76. package/dist/parsers/markdown.d.ts.map +1 -0
  77. package/dist/parsers/markdown.js +57 -0
  78. package/dist/parsers/markdown.js.map +1 -0
  79. package/dist/reporter-html.d.ts +9 -0
  80. package/dist/reporter-html.d.ts.map +1 -0
  81. package/dist/reporter-html.js +581 -0
  82. package/dist/reporter-html.js.map +1 -0
  83. package/dist/reporter.d.ts +10 -0
  84. package/dist/reporter.d.ts.map +1 -0
  85. package/dist/reporter.js +133 -0
  86. package/dist/reporter.js.map +1 -0
  87. package/dist/sanitize.d.ts +17 -0
  88. package/dist/sanitize.d.ts.map +1 -0
  89. package/dist/sanitize.js +83 -0
  90. package/dist/sanitize.js.map +1 -0
  91. package/dist/scanner.d.ts +18 -0
  92. package/dist/scanner.d.ts.map +1 -0
  93. package/dist/scanner.js +236 -0
  94. package/dist/scanner.js.map +1 -0
  95. package/dist/scoring.d.ts +17 -0
  96. package/dist/scoring.d.ts.map +1 -0
  97. package/dist/scoring.js +47 -0
  98. package/dist/scoring.js.map +1 -0
  99. package/dist/telemetry.d.ts +16 -0
  100. package/dist/telemetry.d.ts.map +1 -0
  101. package/dist/telemetry.js +52 -0
  102. package/dist/telemetry.js.map +1 -0
  103. package/dist/threat-feed.d.ts +14 -0
  104. package/dist/threat-feed.d.ts.map +1 -0
  105. package/dist/threat-feed.js +133 -0
  106. package/dist/threat-feed.js.map +1 -0
  107. package/dist/types.d.ts +221 -0
  108. package/dist/types.d.ts.map +1 -0
  109. package/dist/types.js +11 -0
  110. package/dist/types.js.map +1 -0
  111. package/dist/utils.d.ts +12 -0
  112. package/dist/utils.d.ts.map +1 -0
  113. package/dist/utils.js +34 -0
  114. package/dist/utils.js.map +1 -0
  115. package/package.json +71 -0
package/dist/notify.js ADDED
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Notification system for Clawhatch security scanner.
3
+ * Supports Discord, Slack, and generic webhook alerts.
4
+ */
5
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
6
+ import { join, dirname } from "node:path";
7
+ const SEVERITY_ORDER = {
8
+ CRITICAL: 0,
9
+ HIGH: 1,
10
+ MEDIUM: 2,
11
+ LOW: 3,
12
+ };
13
+ /**
14
+ * Load clawhatch.json from the openclaw directory.
15
+ */
16
+ export async function loadNotifyConfig(openclawDir) {
17
+ try {
18
+ const raw = await readFile(join(openclawDir, "clawhatch.json"), "utf-8");
19
+ return JSON.parse(raw);
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ /**
26
+ * Save (merge) clawhatch.json in the openclaw directory.
27
+ */
28
+ export async function saveNotifyConfig(openclawDir, config) {
29
+ const filePath = join(openclawDir, "clawhatch.json");
30
+ let existing = {};
31
+ try {
32
+ const raw = await readFile(filePath, "utf-8");
33
+ existing = JSON.parse(raw);
34
+ }
35
+ catch {
36
+ // File doesn't exist or is invalid — start fresh
37
+ }
38
+ const merged = {
39
+ ...existing,
40
+ ...config,
41
+ notify: {
42
+ ...existing.notify,
43
+ ...config.notify,
44
+ },
45
+ };
46
+ await mkdir(dirname(filePath), { recursive: true });
47
+ await writeFile(filePath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
48
+ }
49
+ /**
50
+ * Build webhook alerts from scan results, filtered by severity threshold.
51
+ */
52
+ export function buildAlerts(result, feed, threshold) {
53
+ const thresholdLevel = SEVERITY_ORDER[threshold.toUpperCase()] ?? 0;
54
+ const filtered = result.findings.filter((f) => (SEVERITY_ORDER[f.severity] ?? 3) <= thresholdLevel);
55
+ const alerts = filtered.map((f) => {
56
+ const alert = {
57
+ checkId: f.id,
58
+ severity: f.severity,
59
+ title: f.title,
60
+ description: f.description,
61
+ };
62
+ if (feed) {
63
+ const match = feed.topThreats.find((t) => t.id === f.id) ??
64
+ feed.newThreats.find((t) => t.id === f.id);
65
+ if (match) {
66
+ alert.communityFrequency = match.frequency;
67
+ alert.trending = match.trending;
68
+ }
69
+ }
70
+ return alert;
71
+ });
72
+ alerts.sort((a, b) => (SEVERITY_ORDER[a.severity] ?? 3) - (SEVERITY_ORDER[b.severity] ?? 3));
73
+ return alerts;
74
+ }
75
+ function isDiscord(url) {
76
+ return url.includes("discord.com/api/webhooks");
77
+ }
78
+ function isSlack(url) {
79
+ return url.includes("hooks.slack.com");
80
+ }
81
+ function severityEmoji(severity) {
82
+ switch (severity) {
83
+ case "CRITICAL":
84
+ return ":rotating_light:";
85
+ case "HIGH":
86
+ return ":warning:";
87
+ case "MEDIUM":
88
+ return ":large_yellow_circle:";
89
+ default:
90
+ return ":white_circle:";
91
+ }
92
+ }
93
+ function severityColor(severity) {
94
+ switch (severity) {
95
+ case "CRITICAL":
96
+ return 16711680; // red
97
+ case "HIGH":
98
+ return 16776960; // yellow
99
+ case "MEDIUM":
100
+ return 16744448; // orange
101
+ default:
102
+ return 8421504; // grey
103
+ }
104
+ }
105
+ function worstSeverity(alerts) {
106
+ if (alerts.length === 0)
107
+ return "LOW";
108
+ return alerts[0].severity; // already sorted CRITICAL first
109
+ }
110
+ function buildDiscordPayload(alerts, score) {
111
+ return {
112
+ embeds: [
113
+ {
114
+ title: "Clawhatch Security Alert",
115
+ description: `Score: ${score}/100`,
116
+ color: severityColor(worstSeverity(alerts)),
117
+ fields: alerts.map((a) => ({
118
+ name: `${a.severity}: ${a.title}`,
119
+ value: a.description,
120
+ inline: false,
121
+ })),
122
+ footer: { text: "Clawhatch Community Threat Intelligence" },
123
+ },
124
+ ],
125
+ };
126
+ }
127
+ function buildSlackPayload(alerts, score) {
128
+ const blocks = [
129
+ {
130
+ type: "header",
131
+ text: { type: "plain_text", text: "Clawhatch Security Alert" },
132
+ },
133
+ {
134
+ type: "section",
135
+ text: { type: "mrkdwn", text: `*Score:* ${score}/100` },
136
+ },
137
+ ...alerts.map((a) => ({
138
+ type: "section",
139
+ text: {
140
+ type: "mrkdwn",
141
+ text: `${severityEmoji(a.severity)} *${a.severity}: ${a.title}*\n${a.description}`,
142
+ },
143
+ })),
144
+ ];
145
+ return { blocks };
146
+ }
147
+ function buildGenericPayload(alerts, score) {
148
+ return {
149
+ alerts,
150
+ score,
151
+ timestamp: new Date().toISOString(),
152
+ };
153
+ }
154
+ /**
155
+ * Send webhook alerts to a Discord, Slack, or generic endpoint.
156
+ * Returns true on 2xx, false otherwise. Never throws.
157
+ */
158
+ export async function sendWebhookAlert(webhookUrl, alerts, score) {
159
+ let payload;
160
+ if (isDiscord(webhookUrl)) {
161
+ payload = buildDiscordPayload(alerts, score);
162
+ }
163
+ else if (isSlack(webhookUrl)) {
164
+ payload = buildSlackPayload(alerts, score);
165
+ }
166
+ else {
167
+ payload = buildGenericPayload(alerts, score);
168
+ }
169
+ try {
170
+ const controller = new AbortController();
171
+ const timeout = setTimeout(() => controller.abort(), 5000);
172
+ const res = await fetch(webhookUrl, {
173
+ method: "POST",
174
+ headers: { "Content-Type": "application/json" },
175
+ body: JSON.stringify(payload),
176
+ signal: controller.signal,
177
+ });
178
+ clearTimeout(timeout);
179
+ return res.ok;
180
+ }
181
+ catch {
182
+ return false;
183
+ }
184
+ }
185
+ /**
186
+ * Send a test message to verify the webhook is working.
187
+ * Returns true on 2xx, false otherwise. Never throws.
188
+ */
189
+ export async function sendTestAlert(webhookUrl) {
190
+ const message = "Clawhatch alert subscription confirmed. You will receive security alerts here.";
191
+ let payload;
192
+ if (isDiscord(webhookUrl)) {
193
+ payload = { content: message };
194
+ }
195
+ else if (isSlack(webhookUrl)) {
196
+ payload = { text: message };
197
+ }
198
+ else {
199
+ payload = { type: "test", message };
200
+ }
201
+ try {
202
+ const controller = new AbortController();
203
+ const timeout = setTimeout(() => controller.abort(), 5000);
204
+ const res = await fetch(webhookUrl, {
205
+ method: "POST",
206
+ headers: { "Content-Type": "application/json" },
207
+ body: JSON.stringify(payload),
208
+ signal: controller.signal,
209
+ });
210
+ clearTimeout(timeout);
211
+ return res.ok;
212
+ }
213
+ catch {
214
+ return false;
215
+ }
216
+ }
217
+ //# sourceMappingURL=notify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.js","sourceRoot":"","sources":["../src/notify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Parser for openclaw.json (JSON5 format).
3
+ */
4
+ import type { OpenClawConfig } from "../types.js";
5
+ /**
6
+ * Check for exotic JSON5 values that could be unexpected.
7
+ * JSON5 allows Infinity, -Infinity, NaN, and hexadecimal literals.
8
+ * These are usually unintentional and may cause issues.
9
+ */
10
+ export declare function checkExoticValues(raw: string): string[];
11
+ export declare function parseConfig(configPath: string): Promise<OpenClawConfig | null>;
12
+ /**
13
+ * Read raw config text (for regex-based secret scanning).
14
+ */
15
+ export declare function readConfigRaw(configPath: string): Promise<string | null>;
16
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/parsers/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAmBvD;AAED,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAchC;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMxB"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Parser for openclaw.json (JSON5 format).
3
+ */
4
+ import { readFile } from "node:fs/promises";
5
+ import JSON5 from "json5";
6
+ /**
7
+ * Check for exotic JSON5 values that could be unexpected.
8
+ * JSON5 allows Infinity, -Infinity, NaN, and hexadecimal literals.
9
+ * These are usually unintentional and may cause issues.
10
+ */
11
+ export function checkExoticValues(raw) {
12
+ const warnings = [];
13
+ // Check for Infinity, -Infinity, NaN (as unquoted values)
14
+ if (/:\s*Infinity\b/.test(raw)) {
15
+ warnings.push("Config contains 'Infinity' value — this may be unintentional");
16
+ }
17
+ if (/:\s*-Infinity\b/.test(raw)) {
18
+ warnings.push("Config contains '-Infinity' value — this may be unintentional");
19
+ }
20
+ if (/:\s*NaN\b/.test(raw)) {
21
+ warnings.push("Config contains 'NaN' value — this may be unintentional");
22
+ }
23
+ // Check for hexadecimal literals (0x...)
24
+ if (/:\s*0x[0-9a-fA-F]+/.test(raw)) {
25
+ warnings.push("Config contains hexadecimal literal — consider using decimal notation for clarity");
26
+ }
27
+ return warnings;
28
+ }
29
+ export async function parseConfig(configPath) {
30
+ try {
31
+ const raw = await readFile(configPath, "utf-8");
32
+ // FIX: Check for exotic JSON5 values and log warnings
33
+ const warnings = checkExoticValues(raw);
34
+ for (const warning of warnings) {
35
+ console.error(` Warning: ${warning}`);
36
+ }
37
+ return JSON5.parse(raw);
38
+ }
39
+ catch {
40
+ return null;
41
+ }
42
+ }
43
+ /**
44
+ * Read raw config text (for regex-based secret scanning).
45
+ */
46
+ export async function readConfigRaw(configPath) {
47
+ try {
48
+ return await readFile(configPath, "utf-8");
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ }
54
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/parsers/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,0DAA0D;IAC1D,IAAI,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/B,QAAQ,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;IAChF,CAAC;IACD,IAAI,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAChC,QAAQ,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;IACjF,CAAC;IACD,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,QAAQ,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;IAC3E,CAAC;IACD,yCAAyC;IACzC,IAAI,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACnC,QAAQ,CAAC,IAAI,CAAC,mFAAmF,CAAC,CAAC;IACrG,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,UAAkB;IAElB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAEhD,sDAAsD;QACtD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;QACxC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,OAAO,CAAC,KAAK,CAAC,cAAc,OAAO,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,UAAkB;IAElB,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Parser for .env files.
3
+ */
4
+ import type { EnvVars } from "../types.js";
5
+ export declare function parseEnv(envPath: string): Promise<EnvVars | null>;
6
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/parsers/env.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,wBAAsB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAkCvE"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Parser for .env files.
3
+ */
4
+ import { readFile } from "node:fs/promises";
5
+ export async function parseEnv(envPath) {
6
+ try {
7
+ const raw = await readFile(envPath, "utf-8");
8
+ const vars = {};
9
+ for (const line of raw.split("\n")) {
10
+ const trimmed = line.trim();
11
+ if (!trimmed || trimmed.startsWith("#"))
12
+ continue;
13
+ const eqIndex = trimmed.indexOf("=");
14
+ if (eqIndex === -1)
15
+ continue;
16
+ // FIX: Strip leading "export " prefix common in .env files (e.g., "export KEY=value")
17
+ let key = trimmed.slice(0, eqIndex).trim();
18
+ if (key.startsWith("export ")) {
19
+ key = key.slice(7).trim();
20
+ }
21
+ let value = trimmed.slice(eqIndex + 1).trim();
22
+ // Strip surrounding quotes
23
+ if ((value.startsWith('"') && value.endsWith('"')) ||
24
+ (value.startsWith("'") && value.endsWith("'"))) {
25
+ value = value.slice(1, -1);
26
+ }
27
+ vars[key] = value;
28
+ }
29
+ return vars;
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
35
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/parsers/env.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAe;IAC5C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAY,EAAE,CAAC;QAEzB,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAS;YAElD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACrC,IAAI,OAAO,KAAK,CAAC,CAAC;gBAAE,SAAS;YAE7B,sFAAsF;YACtF,IAAI,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;YAC3C,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC9B,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC5B,CAAC;YACD,IAAI,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAE9C,2BAA2B;YAC3B,IACE,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBAC9C,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAC9C,CAAC;gBACD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC7B,CAAC;YAED,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACpB,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Parser for JSONL session log files.
3
+ * Caps reading at 1MB or 1000 lines to prevent scanner hanging on large files.
4
+ */
5
+ import type { SessionEntry } from "../types.js";
6
+ export interface JsonlParseResult {
7
+ entries: SessionEntry[];
8
+ truncated: boolean;
9
+ totalSizeBytes: number;
10
+ }
11
+ export declare function parseJsonl(filePath: string, deep?: boolean): Promise<JsonlParseResult>;
12
+ //# sourceMappingURL=jsonl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonl.d.ts","sourceRoot":"","sources":["../../src/parsers/jsonl.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAOhD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,OAAe,GACpB,OAAO,CAAC,gBAAgB,CAAC,CAoD3B"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Parser for JSONL session log files.
3
+ * Caps reading at 1MB or 1000 lines to prevent scanner hanging on large files.
4
+ */
5
+ import { readFile, stat } from "node:fs/promises";
6
+ const MAX_BYTES = 1_048_576; // 1MB
7
+ const MAX_LINES = 1000;
8
+ // FIX: Hard cap for deep mode to prevent OOM on enormous session logs
9
+ const DEEP_MAX_BYTES = 50 * 1_048_576; // 50MB
10
+ export async function parseJsonl(filePath, deep = false) {
11
+ const fileStat = await stat(filePath);
12
+ const totalSizeBytes = fileStat.size;
13
+ // FIX: Even in deep mode, cap at 50MB to prevent OOM
14
+ const byteLimit = deep ? DEEP_MAX_BYTES : MAX_BYTES;
15
+ let raw;
16
+ if (totalSizeBytes > byteLimit) {
17
+ // Read only the first 1MB
18
+ // FIX: Don't set encoding on stream — read as Buffer for accurate byte counting,
19
+ // then convert to string at the end. With encoding: "utf-8", chunk.length gives
20
+ // character count not byte count, which could over-read for multi-byte content.
21
+ const { createReadStream } = await import("node:fs");
22
+ raw = await new Promise((resolve, reject) => {
23
+ const chunks = [];
24
+ let bytesRead = 0;
25
+ const stream = createReadStream(filePath, {
26
+ highWaterMark: 64 * 1024,
27
+ });
28
+ stream.on("data", (chunk) => {
29
+ const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
30
+ bytesRead += buf.length;
31
+ chunks.push(buf);
32
+ if (bytesRead >= byteLimit) {
33
+ stream.destroy();
34
+ }
35
+ });
36
+ stream.on("close", () => resolve(Buffer.concat(chunks).toString("utf-8")));
37
+ stream.on("error", reject);
38
+ });
39
+ }
40
+ else {
41
+ raw = await readFile(filePath, "utf-8");
42
+ }
43
+ const lines = raw.split("\n");
44
+ const maxLines = deep ? lines.length : MAX_LINES;
45
+ const entries = [];
46
+ // FIX: Also flag truncation in deep mode if we hit the 50MB cap
47
+ const truncated = totalSizeBytes > byteLimit || (!deep && lines.length > MAX_LINES);
48
+ for (let i = 0; i < Math.min(lines.length, maxLines); i++) {
49
+ const line = lines[i].trim();
50
+ if (!line)
51
+ continue;
52
+ try {
53
+ entries.push(JSON.parse(line));
54
+ }
55
+ catch {
56
+ // Skip malformed lines
57
+ }
58
+ }
59
+ return { entries, truncated, totalSizeBytes };
60
+ }
61
+ //# sourceMappingURL=jsonl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonl.js","sourceRoot":"","sources":["../../src/parsers/jsonl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Scanner for markdown files (SOUL.md, AGENTS.md, TOOLS.md, etc.)
3
+ * Looks for secrets, API keys, and sensitive patterns.
4
+ */
5
+ export interface MarkdownScanResult {
6
+ filePath: string;
7
+ content: string;
8
+ secretMatches: SecretMatch[];
9
+ }
10
+ export interface SecretMatch {
11
+ pattern: string;
12
+ line: number;
13
+ /** Masked preview — never the full secret */
14
+ preview: string;
15
+ }
16
+ export declare function scanMarkdown(filePath: string): Promise<MarkdownScanResult>;
17
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/parsers/markdown.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,WAAW,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;CACjB;AA8BD,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,CAAC,CA6B7B"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Scanner for markdown files (SOUL.md, AGENTS.md, TOOLS.md, etc.)
3
+ * Looks for secrets, API keys, and sensitive patterns.
4
+ */
5
+ import { readFile } from "node:fs/promises";
6
+ /**
7
+ * Regex patterns for common API keys and secrets.
8
+ * Each pattern has a name and a regex that matches likely secret values.
9
+ */
10
+ const SECRET_PATTERNS = [
11
+ // Generic API key patterns
12
+ { name: "Generic API Key", regex: /(?:api[_-]?key|apikey)\s*[:=]\s*["']?([a-zA-Z0-9_\-]{20,})["']?/gi },
13
+ { name: "Generic Secret", regex: /(?:secret|password|passwd|token)\s*[:=]\s*["']?([a-zA-Z0-9_\-]{16,})["']?/gi },
14
+ // Specific provider patterns
15
+ { name: "OpenAI Key", regex: /sk-[a-zA-Z0-9]{32,}/g },
16
+ { name: "Anthropic Key", regex: /sk-ant-[a-zA-Z0-9\-]{32,}/g },
17
+ { name: "Google AI Key", regex: /AIza[a-zA-Z0-9_\-]{35}/g },
18
+ { name: "Gemini Key", regex: /(?:gemini|google)[_-]?(?:api[_-]?)?key\s*[:=]\s*["']?([a-zA-Z0-9_\-]{20,})["']?/gi },
19
+ { name: "AWS Access Key", regex: /AKIA[A-Z0-9]{16}/g },
20
+ { name: "AWS Secret Key", regex: /(?:aws[_-]?secret|aws[_-]?secret[_-]?access[_-]?key)\s*[:=]\s*["']?([a-zA-Z0-9/+=]{40})["']?/gi },
21
+ { name: "Stripe Key", regex: /(?:sk|pk)_(?:live|test)_[a-zA-Z0-9]{20,}/g },
22
+ { name: "GitHub Token", regex: /(?:ghp|gho|ghu|ghs|ghr)_[a-zA-Z0-9]{36,}/g },
23
+ { name: "Slack Token", regex: /xox[bpras]-[a-zA-Z0-9\-]{10,}/g },
24
+ { name: "Discord Token", regex: /[MN][a-zA-Z0-9]{23,}\.[a-zA-Z0-9_-]{6}\.[a-zA-Z0-9_-]{27,}/g },
25
+ { name: "Telegram Bot Token", regex: /\d{8,10}:[a-zA-Z0-9_-]{35}/g },
26
+ { name: "Private Key Block", regex: /-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----/g },
27
+ { name: "Bearer Token", regex: /(?:bearer|authorization)\s*[:=]\s*["']?bearer\s+([a-zA-Z0-9_\-.]{20,})["']?/gi },
28
+ // High-entropy hex strings (potential keys)
29
+ { name: "Hex Secret (64 chars)", regex: /(?:key|secret|token|hash)\s*[:=]\s*["']?([0-9a-f]{64})["']?/gi },
30
+ ];
31
+ export async function scanMarkdown(filePath) {
32
+ const content = await readFile(filePath, "utf-8");
33
+ const lines = content.split("\n");
34
+ const secretMatches = [];
35
+ for (let i = 0; i < lines.length; i++) {
36
+ const line = lines[i];
37
+ for (const { name, regex } of SECRET_PATTERNS) {
38
+ // Reset regex lastIndex for global patterns
39
+ regex.lastIndex = 0;
40
+ let match;
41
+ while ((match = regex.exec(line)) !== null) {
42
+ const fullMatch = match[0];
43
+ // Mask the match — show first 8 chars + "..."
44
+ const preview = fullMatch.length > 12
45
+ ? fullMatch.slice(0, 8) + "..." + fullMatch.slice(-4)
46
+ : fullMatch.slice(0, 4) + "****";
47
+ secretMatches.push({
48
+ pattern: name,
49
+ line: i + 1,
50
+ preview,
51
+ });
52
+ }
53
+ }
54
+ }
55
+ return { filePath, content, secretMatches };
56
+ }
57
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/parsers/markdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * HTML report generator.
3
+ *
4
+ * Produces a self-contained HTML file with inline CSS.
5
+ * No external dependencies — single file, works offline.
6
+ */
7
+ import { type ScanResult } from "./types.js";
8
+ export declare function generateHtmlReport(result: ScanResult): string;
9
+ //# sourceMappingURL=reporter-html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter-html.d.ts","sourceRoot":"","sources":["../src/reporter-html.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAA0B,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAoErE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA0gB7D"}