bashbros 0.1.2 → 0.1.4

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 (65) hide show
  1. package/README.md +727 -265
  2. package/dist/adapters-JAZGGNVP.js +9 -0
  3. package/dist/chunk-4XZ64P4V.js +47 -0
  4. package/dist/chunk-4XZ64P4V.js.map +1 -0
  5. package/dist/{chunk-XCZMQRSX.js → chunk-7OEWYFN3.js} +745 -541
  6. package/dist/chunk-7OEWYFN3.js.map +1 -0
  7. package/dist/{chunk-SQCP6IYB.js → chunk-CG6VEHJM.js} +3 -2
  8. package/dist/chunk-CG6VEHJM.js.map +1 -0
  9. package/dist/{chunk-DLP2O6PN.js → chunk-EMLEJVJZ.js} +102 -1
  10. package/dist/chunk-EMLEJVJZ.js.map +1 -0
  11. package/dist/chunk-IUUBCPMV.js +166 -0
  12. package/dist/chunk-IUUBCPMV.js.map +1 -0
  13. package/dist/chunk-J6ONXY6N.js +146 -0
  14. package/dist/chunk-J6ONXY6N.js.map +1 -0
  15. package/dist/chunk-KYDMPE4N.js +224 -0
  16. package/dist/chunk-KYDMPE4N.js.map +1 -0
  17. package/dist/chunk-LJE4EPIU.js +56 -0
  18. package/dist/chunk-LJE4EPIU.js.map +1 -0
  19. package/dist/chunk-LZYW7XQO.js +339 -0
  20. package/dist/chunk-LZYW7XQO.js.map +1 -0
  21. package/dist/{chunk-YUMNBQAY.js → chunk-RDNSS3ME.js} +587 -12
  22. package/dist/chunk-RDNSS3ME.js.map +1 -0
  23. package/dist/{chunk-BW6XCOJH.js → chunk-RTZ4QWG2.js} +2 -2
  24. package/dist/chunk-RTZ4QWG2.js.map +1 -0
  25. package/dist/chunk-SDN6TAGD.js +157 -0
  26. package/dist/chunk-SDN6TAGD.js.map +1 -0
  27. package/dist/chunk-T5ONCUHZ.js +198 -0
  28. package/dist/chunk-T5ONCUHZ.js.map +1 -0
  29. package/dist/cli.js +1182 -251
  30. package/dist/cli.js.map +1 -1
  31. package/dist/{config-JLLOTFLI.js → config-I5NCK3RJ.js} +2 -2
  32. package/dist/copilot-cli-5WJWK5YT.js +9 -0
  33. package/dist/{db-OBKEXRTP.js → db-ETWTBXAE.js} +2 -2
  34. package/dist/db-checks-2YOVECD4.js +133 -0
  35. package/dist/db-checks-2YOVECD4.js.map +1 -0
  36. package/dist/{display-6LZ2HBCU.js → display-UH7KEHOW.js} +3 -3
  37. package/dist/display-UH7KEHOW.js.map +1 -0
  38. package/dist/gemini-cli-3563EELZ.js +9 -0
  39. package/dist/gemini-cli-3563EELZ.js.map +1 -0
  40. package/dist/index.d.ts +195 -72
  41. package/dist/index.js +119 -398
  42. package/dist/index.js.map +1 -1
  43. package/dist/{ollama-HY35OHW4.js → ollama-5JVKNFOV.js} +2 -2
  44. package/dist/ollama-5JVKNFOV.js.map +1 -0
  45. package/dist/opencode-DRCY275R.js +9 -0
  46. package/dist/opencode-DRCY275R.js.map +1 -0
  47. package/dist/profiles-7CLN6TAT.js +9 -0
  48. package/dist/profiles-7CLN6TAT.js.map +1 -0
  49. package/dist/setup-YS27MOPE.js +124 -0
  50. package/dist/setup-YS27MOPE.js.map +1 -0
  51. package/dist/static/index.html +4815 -2007
  52. package/dist/store-WJ5Y7MOE.js +9 -0
  53. package/dist/store-WJ5Y7MOE.js.map +1 -0
  54. package/dist/writer-3NAVABN6.js +12 -0
  55. package/dist/writer-3NAVABN6.js.map +1 -0
  56. package/package.json +77 -68
  57. package/dist/chunk-BW6XCOJH.js.map +0 -1
  58. package/dist/chunk-DLP2O6PN.js.map +0 -1
  59. package/dist/chunk-SQCP6IYB.js.map +0 -1
  60. package/dist/chunk-XCZMQRSX.js.map +0 -1
  61. package/dist/chunk-YUMNBQAY.js.map +0 -1
  62. /package/dist/{config-JLLOTFLI.js.map → adapters-JAZGGNVP.js.map} +0 -0
  63. /package/dist/{db-OBKEXRTP.js.map → config-I5NCK3RJ.js.map} +0 -0
  64. /package/dist/{display-6LZ2HBCU.js.map → copilot-cli-5WJWK5YT.js.map} +0 -0
  65. /package/dist/{ollama-HY35OHW4.js.map → db-ETWTBXAE.js.map} +0 -0
package/dist/index.js CHANGED
@@ -1,31 +1,40 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ AnomalyDetector,
3
4
  BackgroundWorker,
4
5
  BashBro,
5
6
  BashBros,
6
7
  BashgymIntegration,
7
- ClaudeCodeHooks,
8
8
  CommandSuggester,
9
9
  CostEstimator,
10
10
  LoopDetector,
11
- MetricsCollector,
11
+ OutputScanner,
12
12
  ReportGenerator,
13
13
  SystemProfiler,
14
14
  TaskRouter,
15
15
  UndoStack,
16
- gateCommand,
17
16
  getBashgymIntegration,
18
17
  resetBashgymIntegration
19
- } from "./chunk-XCZMQRSX.js";
18
+ } from "./chunk-7OEWYFN3.js";
19
+ import {
20
+ ClaudeCodeHooks,
21
+ gateCommand
22
+ } from "./chunk-LZYW7XQO.js";
23
+ import {
24
+ MoltbotHooks,
25
+ getMoltbotHooks
26
+ } from "./chunk-J37RHCFJ.js";
20
27
  import {
21
28
  AuditLogger
22
29
  } from "./chunk-SG752FZC.js";
23
30
  import {
24
31
  OllamaClient
25
- } from "./chunk-DLP2O6PN.js";
32
+ } from "./chunk-EMLEJVJZ.js";
33
+ import "./chunk-4XZ64P4V.js";
34
+ import "./chunk-LJE4EPIU.js";
26
35
  import {
27
36
  loadConfig
28
- } from "./chunk-BW6XCOJH.js";
37
+ } from "./chunk-RTZ4QWG2.js";
29
38
  import {
30
39
  CommandFilter,
31
40
  PathSandbox,
@@ -42,438 +51,150 @@ import {
42
51
  import {
43
52
  RiskScorer
44
53
  } from "./chunk-DEAF6PYM.js";
45
- import {
46
- MoltbotHooks,
47
- getMoltbotHooks
48
- } from "./chunk-J37RHCFJ.js";
49
54
  import "./chunk-7OCVIDC7.js";
50
55
 
51
- // src/policy/anomaly-detector.ts
52
- var DEFAULT_CONFIG = {
53
- workingHours: [6, 22],
54
- // 6 AM to 10 PM
55
- typicalCommandsPerMinute: 30,
56
- knownPaths: [],
57
- suspiciousPatterns: [],
58
- enabled: true
59
- };
60
- var DEFAULT_SUSPICIOUS_PATTERNS = [
61
- /\bpasswd\b/,
62
- /\bshadow\b/,
63
- /\/root\//,
64
- /\.ssh\//,
65
- /\.gnupg\//,
66
- /\.aws\//,
67
- /\.kube\//,
68
- /wallet/i,
69
- /crypto/i,
70
- /bitcoin/i,
71
- /ethereum/i,
72
- /private.*key/i
73
- ];
74
- var AnomalyDetector = class {
75
- config;
56
+ // src/observability/metrics.ts
57
+ var MetricsCollector = class {
58
+ sessionId;
59
+ startTime;
76
60
  commands = [];
77
- baselinePaths = /* @__PURE__ */ new Set();
78
- baselineCommands = /* @__PURE__ */ new Set();
79
- learningMode = true;
80
- learningCount = 0;
81
- LEARNING_THRESHOLD = 50;
82
- constructor(config = {}) {
83
- this.config = { ...DEFAULT_CONFIG, ...config };
84
- }
85
- /**
86
- * Check a command for anomalies
87
- */
88
- check(command, cwd) {
89
- if (!this.config.enabled) return [];
90
- const anomalies = [];
91
- const now = Date.now();
92
- this.commands.push({ command, timestamp: now, path: cwd });
93
- if (this.commands.length > 1e3) {
94
- this.commands = this.commands.slice(-500);
95
- }
96
- if (this.learningMode) {
97
- this.learn(command, cwd);
98
- if (this.learningCount >= this.LEARNING_THRESHOLD) {
99
- this.learningMode = false;
100
- }
101
- return anomalies;
102
- }
103
- const timingAnomaly = this.checkTiming(now);
104
- if (timingAnomaly) anomalies.push(timingAnomaly);
105
- const freqAnomaly = this.checkFrequency(now);
106
- if (freqAnomaly) anomalies.push(freqAnomaly);
107
- if (cwd) {
108
- const pathAnomaly = this.checkPath(cwd);
109
- if (pathAnomaly) anomalies.push(pathAnomaly);
110
- }
111
- const patternAnomalies = this.checkPatterns(command);
112
- anomalies.push(...patternAnomalies);
113
- const behaviorAnomaly = this.checkBehavior(command);
114
- if (behaviorAnomaly) anomalies.push(behaviorAnomaly);
115
- return anomalies;
61
+ filesModified = /* @__PURE__ */ new Set();
62
+ pathsAccessed = /* @__PURE__ */ new Set();
63
+ constructor() {
64
+ this.sessionId = this.generateSessionId();
65
+ this.startTime = /* @__PURE__ */ new Date();
116
66
  }
117
- /**
118
- * Learn from command (build baseline)
119
- */
120
- learn(command, cwd) {
121
- this.learningCount++;
122
- const baseCmd = command.split(/\s+/)[0];
123
- this.baselineCommands.add(baseCmd);
124
- if (cwd) {
125
- this.baselinePaths.add(cwd);
126
- const parts = cwd.split(/[/\\]/);
127
- for (let i = 1; i <= parts.length; i++) {
128
- this.baselinePaths.add(parts.slice(0, i).join("/"));
129
- }
130
- }
67
+ generateSessionId() {
68
+ const now = /* @__PURE__ */ new Date();
69
+ const date = now.toISOString().slice(0, 10).replace(/-/g, "");
70
+ const time = now.toTimeString().slice(0, 8).replace(/:/g, "");
71
+ const rand = Math.random().toString(36).slice(2, 6);
72
+ return `${date}-${time}-${rand}`;
131
73
  }
132
74
  /**
133
- * Check for timing anomalies
75
+ * Record a command execution
134
76
  */
135
- checkTiming(now) {
136
- const hour = new Date(now).getHours();
137
- const [start, end] = this.config.workingHours;
138
- if (hour < start || hour >= end) {
139
- return {
140
- type: "timing",
141
- severity: "info",
142
- message: `Activity outside normal hours (${hour}:00)`,
143
- details: { hour, workingHours: this.config.workingHours }
144
- };
77
+ record(metric) {
78
+ this.commands.push(metric);
79
+ const paths = this.extractPaths(metric.command);
80
+ for (const path of paths) {
81
+ this.pathsAccessed.add(path);
145
82
  }
146
- return null;
147
- }
148
- /**
149
- * Check for frequency anomalies
150
- */
151
- checkFrequency(now) {
152
- const oneMinuteAgo = now - 6e4;
153
- const recentCommands = this.commands.filter((c) => c.timestamp > oneMinuteAgo);
154
- const rate = recentCommands.length;
155
- if (rate > this.config.typicalCommandsPerMinute * 2) {
156
- return {
157
- type: "frequency",
158
- severity: "warning",
159
- message: `High command rate: ${rate}/min (typical: ${this.config.typicalCommandsPerMinute})`,
160
- details: { rate, typical: this.config.typicalCommandsPerMinute }
161
- };
162
- }
163
- const fiveSecondsAgo = now - 5e3;
164
- const burstCommands = this.commands.filter((c) => c.timestamp > fiveSecondsAgo);
165
- if (burstCommands.length > 10) {
166
- return {
167
- type: "frequency",
168
- severity: "alert",
169
- message: `Burst detected: ${burstCommands.length} commands in 5 seconds`,
170
- details: { count: burstCommands.length, window: "5s" }
171
- };
172
- }
173
- return null;
174
- }
175
- /**
176
- * Check for unusual path access
177
- */
178
- checkPath(path) {
179
- if (this.config.knownPaths.length > 0) {
180
- const isKnown = this.config.knownPaths.some(
181
- (p) => path.startsWith(p) || path.includes(p)
182
- );
183
- if (!isKnown) {
184
- return {
185
- type: "path",
186
- severity: "warning",
187
- message: `Access to unexpected path: ${path}`,
188
- details: { path, knownPaths: this.config.knownPaths }
189
- };
190
- }
191
- }
192
- if (!this.learningMode && this.baselinePaths.size > 0) {
193
- const isBaseline = this.baselinePaths.has(path) || [...this.baselinePaths].some((p) => path.startsWith(p));
194
- if (!isBaseline) {
195
- return {
196
- type: "path",
197
- severity: "info",
198
- message: `New path accessed: ${path}`,
199
- details: { path, isNew: true }
200
- };
83
+ if (this.isWriteCommand(metric.command)) {
84
+ for (const path of paths) {
85
+ this.filesModified.add(path);
201
86
  }
202
87
  }
203
- return null;
204
88
  }
205
89
  /**
206
- * Check for suspicious patterns
90
+ * Get current session metrics
207
91
  */
208
- checkPatterns(command) {
209
- const anomalies = [];
210
- const allPatterns = [...DEFAULT_SUSPICIOUS_PATTERNS, ...this.config.suspiciousPatterns];
211
- for (const pattern of allPatterns) {
212
- if (pattern.test(command)) {
213
- anomalies.push({
214
- type: "pattern",
215
- severity: "warning",
216
- message: `Suspicious pattern detected: ${pattern.source}`,
217
- details: { command: command.slice(0, 100), pattern: pattern.source }
218
- });
219
- }
92
+ getMetrics() {
93
+ const now = /* @__PURE__ */ new Date();
94
+ const duration = now.getTime() - this.startTime.getTime();
95
+ const riskDist = { safe: 0, caution: 0, dangerous: 0, critical: 0 };
96
+ let totalRisk = 0;
97
+ for (const cmd of this.commands) {
98
+ riskDist[cmd.riskScore.level]++;
99
+ totalRisk += cmd.riskScore.score;
220
100
  }
221
- return anomalies;
222
- }
223
- /**
224
- * Check for behavioral anomalies
225
- */
226
- checkBehavior(command) {
227
- const baseCmd = command.split(/\s+/)[0];
228
- if (!this.learningMode && this.baselineCommands.size > 0) {
229
- if (!this.baselineCommands.has(baseCmd)) {
230
- const sensitiveCommands = /* @__PURE__ */ new Set([
231
- "curl",
232
- "wget",
233
- "nc",
234
- "netcat",
235
- "ssh",
236
- "scp",
237
- "rsync",
238
- "sudo",
239
- "su",
240
- "chmod",
241
- "chown",
242
- "mount",
243
- "umount"
244
- ]);
245
- if (sensitiveCommands.has(baseCmd)) {
246
- return {
247
- type: "behavior",
248
- severity: "warning",
249
- message: `New sensitive command type: ${baseCmd}`,
250
- details: { command: baseCmd, isNew: true }
251
- };
252
- }
101
+ const cmdFreq = /* @__PURE__ */ new Map();
102
+ for (const cmd of this.commands) {
103
+ const base = cmd.command.split(/\s+/)[0];
104
+ cmdFreq.set(base, (cmdFreq.get(base) || 0) + 1);
105
+ }
106
+ const topCommands = [...cmdFreq.entries()].sort((a, b) => b[1] - a[1]).slice(0, 10);
107
+ const violationsByType = {};
108
+ for (const cmd of this.commands) {
109
+ for (const v of cmd.violations) {
110
+ violationsByType[v.type] = (violationsByType[v.type] || 0) + 1;
253
111
  }
254
112
  }
255
- return null;
256
- }
257
- /**
258
- * Get anomaly stats
259
- */
260
- getStats() {
261
- const now = Date.now();
262
- const oneMinuteAgo = now - 6e4;
263
- const recentRate = this.commands.filter((c) => c.timestamp > oneMinuteAgo).length;
113
+ const totalExecTime = this.commands.reduce((sum, c) => sum + c.duration, 0);
114
+ const avgExecTime = this.commands.length > 0 ? totalExecTime / this.commands.length : 0;
264
115
  return {
265
- learningMode: this.learningMode,
266
- learningProgress: Math.min(100, Math.round(this.learningCount / this.LEARNING_THRESHOLD * 100)),
267
- baselineCommands: this.baselineCommands.size,
268
- baselinePaths: this.baselinePaths.size,
269
- recentCommandRate: recentRate
116
+ sessionId: this.sessionId,
117
+ startTime: this.startTime,
118
+ duration,
119
+ commandCount: this.commands.length,
120
+ blockedCount: this.commands.filter((c) => !c.allowed).length,
121
+ uniqueCommands: cmdFreq.size,
122
+ topCommands,
123
+ riskDistribution: riskDist,
124
+ avgRiskScore: this.commands.length > 0 ? totalRisk / this.commands.length : 0,
125
+ avgExecutionTime: avgExecTime,
126
+ totalExecutionTime: totalExecTime,
127
+ filesModified: [...this.filesModified],
128
+ pathsAccessed: [...this.pathsAccessed],
129
+ violationsByType
270
130
  };
271
131
  }
272
132
  /**
273
- * Force end learning mode
274
- */
275
- endLearning() {
276
- this.learningMode = false;
277
- }
278
- /**
279
- * Reset and restart learning
133
+ * Extract paths from a command
280
134
  */
281
- reset() {
282
- this.commands = [];
283
- this.baselinePaths.clear();
284
- this.baselineCommands.clear();
285
- this.learningMode = true;
286
- this.learningCount = 0;
287
- }
288
- };
289
-
290
- // src/policy/output-scanner.ts
291
- var SECRET_PATTERNS = [
292
- // API Keys
293
- { pattern: /sk-[A-Za-z0-9]{20,}/, name: "OpenAI API Key" },
294
- { pattern: /sk-ant-[A-Za-z0-9\-]{20,}/, name: "Anthropic API Key" },
295
- { pattern: /ghp_[A-Za-z0-9]{36}/, name: "GitHub Token" },
296
- { pattern: /gho_[A-Za-z0-9]{36}/, name: "GitHub OAuth Token" },
297
- { pattern: /github_pat_[A-Za-z0-9_]{22,}/, name: "GitHub PAT" },
298
- { pattern: /glpat-[A-Za-z0-9\-]{20,}/, name: "GitLab Token" },
299
- { pattern: /xox[baprs]-[A-Za-z0-9\-]{10,}/, name: "Slack Token" },
300
- { pattern: /sk_live_[A-Za-z0-9]{24,}/, name: "Stripe Secret Key" },
301
- { pattern: /sq0atp-[A-Za-z0-9\-_]{22,}/, name: "Square Token" },
302
- { pattern: /AKIA[A-Z0-9]{16}/, name: "AWS Access Key" },
303
- { pattern: /amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/, name: "Amazon MWS Key" },
304
- // OAuth/JWT
305
- { pattern: /Bearer\s+[A-Za-z0-9\-._~+/]+=*/, name: "Bearer Token" },
306
- { pattern: /eyJ[A-Za-z0-9\-_]+\.eyJ[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+/, name: "JWT Token" },
307
- // Credentials in output
308
- { pattern: /password\s*[=:]\s*['"]?[^\s'"]{4,}['"]?/i, name: "Password" },
309
- { pattern: /passwd\s*[=:]\s*['"]?[^\s'"]{4,}['"]?/i, name: "Password" },
310
- { pattern: /api[_-]?key\s*[=:]\s*['"]?[^\s'"]{8,}['"]?/i, name: "API Key" },
311
- { pattern: /secret\s*[=:]\s*['"]?[^\s'"]{8,}['"]?/i, name: "Secret" },
312
- { pattern: /token\s*[=:]\s*['"]?[^\s'"]{8,}['"]?/i, name: "Token" },
313
- // Private keys
314
- { pattern: /-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----/, name: "Private Key" },
315
- { pattern: /-----BEGIN\s+EC\s+PRIVATE\s+KEY-----/, name: "EC Private Key" },
316
- { pattern: /-----BEGIN\s+OPENSSH\s+PRIVATE\s+KEY-----/, name: "SSH Private Key" },
317
- { pattern: /-----BEGIN\s+PGP\s+PRIVATE\s+KEY\s+BLOCK-----/, name: "PGP Private Key" },
318
- // Database URLs
319
- { pattern: /mongodb(\+srv)?:\/\/[^:]+:[^@]+@/, name: "MongoDB Connection String" },
320
- { pattern: /postgres(ql)?:\/\/[^:]+:[^@]+@/, name: "PostgreSQL Connection String" },
321
- { pattern: /mysql:\/\/[^:]+:[^@]+@/, name: "MySQL Connection String" },
322
- { pattern: /redis:\/\/[^:]+:[^@]+@/, name: "Redis Connection String" },
323
- // SSH
324
- { pattern: /ssh-rsa\s+[A-Za-z0-9+/]+[=]{0,2}/, name: "SSH Public Key" },
325
- { pattern: /ssh-ed25519\s+[A-Za-z0-9+/]+/, name: "SSH ED25519 Key" }
326
- ];
327
- var ERROR_PATTERNS = [
328
- { pattern: /EACCES|EPERM|permission denied/i, name: "Permission Error" },
329
- { pattern: /ENOENT|no such file|not found/i, name: "File Not Found" },
330
- { pattern: /ECONNREFUSED|connection refused/i, name: "Connection Refused" },
331
- { pattern: /ETIMEDOUT|timed out/i, name: "Timeout Error" },
332
- { pattern: /segmentation fault|core dumped/i, name: "Crash" },
333
- { pattern: /out of memory|OOM|cannot allocate/i, name: "Memory Error" },
334
- { pattern: /stack trace|traceback|at\s+\S+:\d+:\d+/i, name: "Stack Trace" },
335
- { pattern: /error:|fatal:|failed:/i, name: "Error Message" }
336
- ];
337
- var OutputScanner = class {
338
- secretPatterns;
339
- redactPatterns;
340
- policy;
341
- constructor(policy) {
342
- this.policy = policy;
343
- this.secretPatterns = SECRET_PATTERNS.map((p) => p.pattern);
344
- this.redactPatterns = (policy.redactPatterns || []).map((p) => {
345
- try {
346
- return new RegExp(p, "gi");
347
- } catch {
348
- return null;
135
+ extractPaths(command) {
136
+ const paths = [];
137
+ const tokens = command.split(/\s+/);
138
+ for (const token of tokens) {
139
+ if (token.startsWith("-")) continue;
140
+ if (token.startsWith("/") || token.startsWith("./") || token.startsWith("../") || token.startsWith("~/") || token.includes(".")) {
141
+ paths.push(token);
349
142
  }
350
- }).filter((p) => p !== null);
143
+ }
144
+ return paths;
351
145
  }
352
146
  /**
353
- * Scan output for secrets and sensitive data
147
+ * Check if command modifies files
354
148
  */
355
- scan(output) {
356
- if (!this.policy.enabled) {
357
- return {
358
- hasSecrets: false,
359
- hasErrors: false,
360
- redactedOutput: output,
361
- findings: []
362
- };
363
- }
364
- const findings = [];
365
- let hasSecrets = false;
366
- let hasErrors = false;
367
- let processedOutput = output;
368
- if (output.length > this.policy.maxOutputLength) {
369
- processedOutput = output.slice(0, this.policy.maxOutputLength) + "\n... [truncated]";
370
- }
371
- if (this.policy.scanForSecrets) {
372
- const secretFindings = this.scanForSecrets(processedOutput);
373
- if (secretFindings.length > 0) {
374
- hasSecrets = true;
375
- findings.push(...secretFindings);
376
- }
377
- }
378
- if (this.policy.scanForErrors) {
379
- const errorFindings = this.scanForErrors(processedOutput);
380
- if (errorFindings.length > 0) {
381
- hasErrors = true;
382
- findings.push(...errorFindings);
383
- }
384
- }
385
- const redactedOutput = this.redact(processedOutput);
386
- return {
387
- hasSecrets,
388
- hasErrors,
389
- redactedOutput,
390
- findings
391
- };
149
+ isWriteCommand(command) {
150
+ const writePatterns = [
151
+ /^(vim|vi|nano|emacs|code)\s/,
152
+ /^(touch|mkdir|cp|mv|rm)\s/,
153
+ /^(echo|cat|printf).*>/,
154
+ /^(git\s+(add|commit|checkout|reset))/,
155
+ /^(npm|yarn|pnpm)\s+(install|uninstall)/,
156
+ /^(pip|pip3)\s+(install|uninstall)/,
157
+ /^chmod\s/,
158
+ /^chown\s/
159
+ ];
160
+ return writePatterns.some((p) => p.test(command));
392
161
  }
393
162
  /**
394
- * Scan for secrets in output
163
+ * Get recent commands
395
164
  */
396
- scanForSecrets(output) {
397
- const findings = [];
398
- const lines = output.split("\n");
399
- for (let i = 0; i < lines.length; i++) {
400
- const line = lines[i];
401
- for (const { pattern, name } of SECRET_PATTERNS) {
402
- if (pattern.test(line)) {
403
- findings.push({
404
- type: "secret",
405
- pattern: name,
406
- message: `Potential ${name} found in output`,
407
- line: i + 1
408
- });
409
- }
410
- }
411
- }
412
- return findings;
165
+ getRecentCommands(n = 10) {
166
+ return this.commands.slice(-n);
413
167
  }
414
168
  /**
415
- * Scan for error patterns in output
169
+ * Get blocked commands
416
170
  */
417
- scanForErrors(output) {
418
- const findings = [];
419
- const lines = output.split("\n");
420
- for (let i = 0; i < lines.length; i++) {
421
- const line = lines[i];
422
- for (const { pattern, name } of ERROR_PATTERNS) {
423
- if (pattern.test(line)) {
424
- findings.push({
425
- type: "error",
426
- pattern: name,
427
- message: `${name} detected`,
428
- line: i + 1
429
- });
430
- break;
431
- }
432
- }
433
- }
434
- return findings;
171
+ getBlockedCommands() {
172
+ return this.commands.filter((c) => !c.allowed);
435
173
  }
436
174
  /**
437
- * Redact sensitive data from output
175
+ * Get high-risk commands
438
176
  */
439
- redact(output) {
440
- let redacted = output;
441
- for (const { pattern, name } of SECRET_PATTERNS) {
442
- redacted = redacted.replace(new RegExp(pattern.source, "g"), `[REDACTED ${name}]`);
443
- }
444
- for (const pattern of this.redactPatterns) {
445
- redacted = redacted.replace(pattern, "[REDACTED]");
446
- }
447
- return redacted;
177
+ getHighRiskCommands(threshold = 6) {
178
+ return this.commands.filter((c) => c.riskScore.score >= threshold);
448
179
  }
449
180
  /**
450
- * Check if output contains any secrets
181
+ * Format duration for display
451
182
  */
452
- hasSecrets(output) {
453
- for (const pattern of this.secretPatterns) {
454
- if (pattern.test(output)) {
455
- return true;
456
- }
457
- }
458
- return false;
183
+ static formatDuration(ms) {
184
+ if (ms < 1e3) return `${ms}ms`;
185
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
186
+ if (ms < 36e5) return `${Math.floor(ms / 6e4)}m ${Math.floor(ms % 6e4 / 1e3)}s`;
187
+ return `${Math.floor(ms / 36e5)}h ${Math.floor(ms % 36e5 / 6e4)}m`;
459
188
  }
460
189
  /**
461
- * Get summary of findings
190
+ * Reset collector
462
191
  */
463
- static summarize(findings) {
464
- if (findings.length === 0) {
465
- return "No issues found";
466
- }
467
- const secrets = findings.filter((f) => f.type === "secret");
468
- const errors = findings.filter((f) => f.type === "error");
469
- const parts = [];
470
- if (secrets.length > 0) {
471
- parts.push(`${secrets.length} potential secret(s)`);
472
- }
473
- if (errors.length > 0) {
474
- parts.push(`${errors.length} error(s)`);
475
- }
476
- return parts.join(", ");
192
+ reset() {
193
+ this.sessionId = this.generateSessionId();
194
+ this.startTime = /* @__PURE__ */ new Date();
195
+ this.commands = [];
196
+ this.filesModified.clear();
197
+ this.pathsAccessed.clear();
477
198
  }
478
199
  };
479
200