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.
- package/README.md +727 -265
- package/dist/adapters-JAZGGNVP.js +9 -0
- package/dist/chunk-4XZ64P4V.js +47 -0
- package/dist/chunk-4XZ64P4V.js.map +1 -0
- package/dist/{chunk-XCZMQRSX.js → chunk-7OEWYFN3.js} +745 -541
- package/dist/chunk-7OEWYFN3.js.map +1 -0
- package/dist/{chunk-SQCP6IYB.js → chunk-CG6VEHJM.js} +3 -2
- package/dist/chunk-CG6VEHJM.js.map +1 -0
- package/dist/{chunk-DLP2O6PN.js → chunk-EMLEJVJZ.js} +102 -1
- package/dist/chunk-EMLEJVJZ.js.map +1 -0
- package/dist/chunk-IUUBCPMV.js +166 -0
- package/dist/chunk-IUUBCPMV.js.map +1 -0
- package/dist/chunk-J6ONXY6N.js +146 -0
- package/dist/chunk-J6ONXY6N.js.map +1 -0
- package/dist/chunk-KYDMPE4N.js +224 -0
- package/dist/chunk-KYDMPE4N.js.map +1 -0
- package/dist/chunk-LJE4EPIU.js +56 -0
- package/dist/chunk-LJE4EPIU.js.map +1 -0
- package/dist/chunk-LZYW7XQO.js +339 -0
- package/dist/chunk-LZYW7XQO.js.map +1 -0
- package/dist/{chunk-YUMNBQAY.js → chunk-RDNSS3ME.js} +587 -12
- package/dist/chunk-RDNSS3ME.js.map +1 -0
- package/dist/{chunk-BW6XCOJH.js → chunk-RTZ4QWG2.js} +2 -2
- package/dist/chunk-RTZ4QWG2.js.map +1 -0
- package/dist/chunk-SDN6TAGD.js +157 -0
- package/dist/chunk-SDN6TAGD.js.map +1 -0
- package/dist/chunk-T5ONCUHZ.js +198 -0
- package/dist/chunk-T5ONCUHZ.js.map +1 -0
- package/dist/cli.js +1182 -251
- package/dist/cli.js.map +1 -1
- package/dist/{config-JLLOTFLI.js → config-I5NCK3RJ.js} +2 -2
- package/dist/copilot-cli-5WJWK5YT.js +9 -0
- package/dist/{db-OBKEXRTP.js → db-ETWTBXAE.js} +2 -2
- package/dist/db-checks-2YOVECD4.js +133 -0
- package/dist/db-checks-2YOVECD4.js.map +1 -0
- package/dist/{display-6LZ2HBCU.js → display-UH7KEHOW.js} +3 -3
- package/dist/display-UH7KEHOW.js.map +1 -0
- package/dist/gemini-cli-3563EELZ.js +9 -0
- package/dist/gemini-cli-3563EELZ.js.map +1 -0
- package/dist/index.d.ts +195 -72
- package/dist/index.js +119 -398
- package/dist/index.js.map +1 -1
- package/dist/{ollama-HY35OHW4.js → ollama-5JVKNFOV.js} +2 -2
- package/dist/ollama-5JVKNFOV.js.map +1 -0
- package/dist/opencode-DRCY275R.js +9 -0
- package/dist/opencode-DRCY275R.js.map +1 -0
- package/dist/profiles-7CLN6TAT.js +9 -0
- package/dist/profiles-7CLN6TAT.js.map +1 -0
- package/dist/setup-YS27MOPE.js +124 -0
- package/dist/setup-YS27MOPE.js.map +1 -0
- package/dist/static/index.html +4815 -2007
- package/dist/store-WJ5Y7MOE.js +9 -0
- package/dist/store-WJ5Y7MOE.js.map +1 -0
- package/dist/writer-3NAVABN6.js +12 -0
- package/dist/writer-3NAVABN6.js.map +1 -0
- package/package.json +77 -68
- package/dist/chunk-BW6XCOJH.js.map +0 -1
- package/dist/chunk-DLP2O6PN.js.map +0 -1
- package/dist/chunk-SQCP6IYB.js.map +0 -1
- package/dist/chunk-XCZMQRSX.js.map +0 -1
- package/dist/chunk-YUMNBQAY.js.map +0 -1
- /package/dist/{config-JLLOTFLI.js.map → adapters-JAZGGNVP.js.map} +0 -0
- /package/dist/{db-OBKEXRTP.js.map → config-I5NCK3RJ.js.map} +0 -0
- /package/dist/{display-6LZ2HBCU.js.map → copilot-cli-5WJWK5YT.js.map} +0 -0
- /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
|
-
|
|
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-
|
|
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-
|
|
32
|
+
} from "./chunk-EMLEJVJZ.js";
|
|
33
|
+
import "./chunk-4XZ64P4V.js";
|
|
34
|
+
import "./chunk-LJE4EPIU.js";
|
|
26
35
|
import {
|
|
27
36
|
loadConfig
|
|
28
|
-
} from "./chunk-
|
|
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/
|
|
52
|
-
var
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
*
|
|
75
|
+
* Record a command execution
|
|
134
76
|
*/
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
90
|
+
* Get current session metrics
|
|
207
91
|
*/
|
|
208
|
-
|
|
209
|
-
const
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
|
|
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
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
*
|
|
274
|
-
*/
|
|
275
|
-
endLearning() {
|
|
276
|
-
this.learningMode = false;
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Reset and restart learning
|
|
133
|
+
* Extract paths from a command
|
|
280
134
|
*/
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
}
|
|
143
|
+
}
|
|
144
|
+
return paths;
|
|
351
145
|
}
|
|
352
146
|
/**
|
|
353
|
-
*
|
|
147
|
+
* Check if command modifies files
|
|
354
148
|
*/
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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
|
-
*
|
|
163
|
+
* Get recent commands
|
|
395
164
|
*/
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
*
|
|
169
|
+
* Get blocked commands
|
|
416
170
|
*/
|
|
417
|
-
|
|
418
|
-
|
|
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
|
-
*
|
|
175
|
+
* Get high-risk commands
|
|
438
176
|
*/
|
|
439
|
-
|
|
440
|
-
|
|
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
|
-
*
|
|
181
|
+
* Format duration for display
|
|
451
182
|
*/
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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
|
-
*
|
|
190
|
+
* Reset collector
|
|
462
191
|
*/
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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
|
|