@defai.digital/ax-cli 3.5.4 → 3.6.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/.ax-cli/memory.json +8 -8
- package/README.md +7 -0
- package/config-defaults/messages.yaml +75 -0
- package/config-defaults/models.yaml +66 -0
- package/config-defaults/prompts.yaml +156 -0
- package/config-defaults/settings.yaml +86 -0
- package/dist/agent/chat-history-manager.d.ts +56 -0
- package/dist/agent/chat-history-manager.js +150 -0
- package/dist/agent/chat-history-manager.js.map +1 -0
- package/dist/agent/llm-agent.js +1 -1
- package/dist/agent/llm-agent.js.map +1 -1
- package/dist/agent/tool-manager.d.ts +39 -0
- package/dist/agent/tool-manager.js +76 -0
- package/dist/agent/tool-manager.js.map +1 -0
- package/dist/commands/memory.js +1 -1
- package/dist/commands/memory.js.map +1 -1
- package/dist/commands/setup.js +19 -6
- package/dist/commands/setup.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/client.d.ts +1 -0
- package/dist/llm/client.js +44 -0
- package/dist/llm/client.js.map +1 -1
- package/dist/mcp/health.js +4 -2
- package/dist/mcp/health.js.map +1 -1
- package/dist/mcp/ssrf-protection.d.ts +86 -0
- package/dist/mcp/ssrf-protection.js +313 -0
- package/dist/mcp/ssrf-protection.js.map +1 -0
- package/dist/mcp/validation.d.ts +4 -0
- package/dist/mcp/validation.js +122 -11
- package/dist/mcp/validation.js.map +1 -1
- package/dist/schemas/settings-schemas.d.ts +30 -0
- package/dist/schemas/settings-schemas.js +30 -0
- package/dist/schemas/settings-schemas.js.map +1 -1
- package/dist/tools/bash.d.ts +3 -2
- package/dist/tools/bash.js +31 -2
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/search.d.ts +1 -1
- package/dist/tools/search.js +121 -128
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/text-editor.js +52 -15
- package/dist/tools/text-editor.js.map +1 -1
- package/dist/ui/components/chat-history.js +1 -1
- package/dist/ui/components/chat-history.js.map +1 -1
- package/dist/ui/components/chat-interface.js +3 -2
- package/dist/ui/components/chat-interface.js.map +1 -1
- package/dist/ui/components/confirmation-dialog.js +1 -1
- package/dist/ui/components/confirmation-dialog.js.map +1 -1
- package/dist/ui/components/status-bar.js +2 -2
- package/dist/ui/components/status-bar.js.map +1 -1
- package/dist/{hooks → ui/hooks}/use-chat-reducer.d.ts +1 -1
- package/dist/ui/hooks/use-chat-reducer.js.map +1 -0
- package/dist/{hooks → ui/hooks}/use-enhanced-input.js +8 -3
- package/dist/ui/hooks/use-enhanced-input.js.map +1 -0
- package/dist/{hooks → ui/hooks}/use-input-handler.d.ts +1 -1
- package/dist/{hooks → ui/hooks}/use-input-handler.js +28 -24
- package/dist/ui/hooks/use-input-handler.js.map +1 -0
- package/dist/utils/audit-logger.d.ts +247 -0
- package/dist/utils/audit-logger.js +374 -0
- package/dist/utils/audit-logger.js.map +1 -0
- package/dist/utils/command-security.d.ts +85 -0
- package/dist/utils/command-security.js +200 -0
- package/dist/utils/command-security.js.map +1 -0
- package/dist/utils/config-loader.js +3 -3
- package/dist/utils/config-loader.js.map +1 -1
- package/dist/utils/encryption.d.ts +78 -0
- package/dist/utils/encryption.js +216 -0
- package/dist/utils/encryption.js.map +1 -0
- package/dist/utils/error-sanitizer.d.ts +119 -0
- package/dist/utils/error-sanitizer.js +253 -0
- package/dist/utils/error-sanitizer.js.map +1 -0
- package/dist/utils/input-sanitizer.d.ts +210 -0
- package/dist/utils/input-sanitizer.js +362 -0
- package/dist/utils/input-sanitizer.js.map +1 -0
- package/dist/utils/json-utils.d.ts +13 -0
- package/dist/utils/json-utils.js +55 -1
- package/dist/utils/json-utils.js.map +1 -1
- package/dist/utils/path-security.d.ts +90 -0
- package/dist/utils/path-security.js +328 -0
- package/dist/utils/path-security.js.map +1 -0
- package/dist/utils/process-pool.d.ts +105 -0
- package/dist/utils/process-pool.js +326 -0
- package/dist/utils/process-pool.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +207 -0
- package/dist/utils/rate-limiter.js +303 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/settings-manager.js +99 -6
- package/dist/utils/settings-manager.js.map +1 -1
- package/dist/utils/streaming-analyzer.js +9 -21
- package/dist/utils/streaming-analyzer.js.map +1 -1
- package/package.json +1 -1
- package/dist/hooks/use-chat-reducer.js.map +0 -1
- package/dist/hooks/use-enhanced-input.js.map +0 -1
- package/dist/hooks/use-input-handler.js.map +0 -1
- package/dist/hooks/use-input-history.d.ts +0 -9
- package/dist/hooks/use-input-history.js +0 -112
- package/dist/hooks/use-input-history.js.map +0 -1
- /package/dist/{hooks → ui/hooks}/use-chat-reducer.js +0 -0
- /package/dist/{hooks → ui/hooks}/use-enhanced-input.d.ts +0 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Audit Logging System (REQ-SEC-008)
|
|
3
|
+
*
|
|
4
|
+
* Provides tamper-proof logging for security-critical events
|
|
5
|
+
* Implements:
|
|
6
|
+
* - Cryptographic log integrity verification
|
|
7
|
+
* - Automatic log retention (90 days)
|
|
8
|
+
* - SIEM-ready structured logging
|
|
9
|
+
* - Critical event alerting
|
|
10
|
+
*
|
|
11
|
+
* Security: CVSS 6.1 (Medium Priority)
|
|
12
|
+
*/
|
|
13
|
+
import { createHash } from 'crypto';
|
|
14
|
+
import { writeFileSync, readFileSync, existsSync, mkdirSync, readdirSync, unlinkSync, statSync } from 'fs';
|
|
15
|
+
import { join } from 'path';
|
|
16
|
+
import { homedir } from 'os';
|
|
17
|
+
/**
|
|
18
|
+
* Security event severity levels
|
|
19
|
+
*/
|
|
20
|
+
export var AuditSeverity;
|
|
21
|
+
(function (AuditSeverity) {
|
|
22
|
+
AuditSeverity["INFO"] = "INFO";
|
|
23
|
+
AuditSeverity["WARNING"] = "WARNING";
|
|
24
|
+
AuditSeverity["ERROR"] = "ERROR";
|
|
25
|
+
AuditSeverity["CRITICAL"] = "CRITICAL";
|
|
26
|
+
})(AuditSeverity || (AuditSeverity = {}));
|
|
27
|
+
/**
|
|
28
|
+
* Security event categories
|
|
29
|
+
*/
|
|
30
|
+
export var AuditCategory;
|
|
31
|
+
(function (AuditCategory) {
|
|
32
|
+
AuditCategory["AUTHENTICATION"] = "AUTHENTICATION";
|
|
33
|
+
AuditCategory["AUTHORIZATION"] = "AUTHORIZATION";
|
|
34
|
+
AuditCategory["DATA_ACCESS"] = "DATA_ACCESS";
|
|
35
|
+
AuditCategory["DATA_MODIFICATION"] = "DATA_MODIFICATION";
|
|
36
|
+
AuditCategory["COMMAND_EXECUTION"] = "COMMAND_EXECUTION";
|
|
37
|
+
AuditCategory["API_CALL"] = "API_CALL";
|
|
38
|
+
AuditCategory["RATE_LIMIT"] = "RATE_LIMIT";
|
|
39
|
+
AuditCategory["INPUT_VALIDATION"] = "INPUT_VALIDATION";
|
|
40
|
+
AuditCategory["ENCRYPTION"] = "ENCRYPTION";
|
|
41
|
+
AuditCategory["MCP_OPERATION"] = "MCP_OPERATION";
|
|
42
|
+
AuditCategory["FILE_OPERATION"] = "FILE_OPERATION";
|
|
43
|
+
AuditCategory["SYSTEM_EVENT"] = "SYSTEM_EVENT";
|
|
44
|
+
})(AuditCategory || (AuditCategory = {}));
|
|
45
|
+
/**
|
|
46
|
+
* Default configuration
|
|
47
|
+
*/
|
|
48
|
+
const DEFAULT_CONFIG = {
|
|
49
|
+
logDirectory: join(homedir(), '.ax-cli', 'audit-logs'),
|
|
50
|
+
retentionDays: 90,
|
|
51
|
+
enableAlerts: true,
|
|
52
|
+
enableChaining: true,
|
|
53
|
+
maxFileSize: 10 * 1024 * 1024, // 10MB
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Audit Logger
|
|
57
|
+
*
|
|
58
|
+
* Provides tamper-proof audit logging with cryptographic verification
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* const logger = AuditLogger.getInstance();
|
|
63
|
+
*
|
|
64
|
+
* // Log a security event
|
|
65
|
+
* logger.log({
|
|
66
|
+
* severity: AuditSeverity.WARNING,
|
|
67
|
+
* category: AuditCategory.RATE_LIMIT,
|
|
68
|
+
* action: 'rate_limit_exceeded',
|
|
69
|
+
* actor: 'user-123',
|
|
70
|
+
* outcome: 'failure',
|
|
71
|
+
* details: { limit: 20, attempts: 25 },
|
|
72
|
+
* });
|
|
73
|
+
*
|
|
74
|
+
* // Log critical event (triggers alert)
|
|
75
|
+
* logger.logCritical({
|
|
76
|
+
* category: AuditCategory.COMMAND_EXECUTION,
|
|
77
|
+
* action: 'shell_injection_attempt',
|
|
78
|
+
* outcome: 'failure',
|
|
79
|
+
* details: { command: 'ls; rm -rf /' },
|
|
80
|
+
* });
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export class AuditLogger {
|
|
84
|
+
static instance = null;
|
|
85
|
+
config;
|
|
86
|
+
currentLogFile;
|
|
87
|
+
lastHash = null;
|
|
88
|
+
eventCount = 0;
|
|
89
|
+
alertCallbacks = [];
|
|
90
|
+
constructor(config = {}) {
|
|
91
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
92
|
+
// Ensure log directory exists
|
|
93
|
+
if (!existsSync(this.config.logDirectory)) {
|
|
94
|
+
mkdirSync(this.config.logDirectory, { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
// Initialize log file
|
|
97
|
+
this.currentLogFile = this.getCurrentLogFile();
|
|
98
|
+
// Load last hash for chaining
|
|
99
|
+
if (this.config.enableChaining) {
|
|
100
|
+
this.lastHash = this.loadLastHash();
|
|
101
|
+
}
|
|
102
|
+
// Start retention cleanup (run once on init)
|
|
103
|
+
this.cleanupOldLogs();
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get singleton instance
|
|
107
|
+
*/
|
|
108
|
+
static getInstance(config) {
|
|
109
|
+
if (!AuditLogger.instance) {
|
|
110
|
+
AuditLogger.instance = new AuditLogger(config);
|
|
111
|
+
}
|
|
112
|
+
return AuditLogger.instance;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Reset singleton (for testing)
|
|
116
|
+
*/
|
|
117
|
+
static resetInstance() {
|
|
118
|
+
AuditLogger.instance = null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Register alert callback for critical events
|
|
122
|
+
*/
|
|
123
|
+
onCriticalEvent(callback) {
|
|
124
|
+
this.alertCallbacks.push(callback);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Log an audit event
|
|
128
|
+
*/
|
|
129
|
+
log(event) {
|
|
130
|
+
const fullEvent = {
|
|
131
|
+
id: this.generateEventId(),
|
|
132
|
+
timestamp: new Date().toISOString(),
|
|
133
|
+
...event,
|
|
134
|
+
};
|
|
135
|
+
// Add hash chain for tamper detection
|
|
136
|
+
if (this.config.enableChaining) {
|
|
137
|
+
fullEvent.previousHash = this.lastHash || undefined;
|
|
138
|
+
fullEvent.hash = this.calculateHash(fullEvent);
|
|
139
|
+
this.lastHash = fullEvent.hash;
|
|
140
|
+
}
|
|
141
|
+
// Write to log file
|
|
142
|
+
this.writeEvent(fullEvent);
|
|
143
|
+
// Emit alert for critical events
|
|
144
|
+
if (this.config.enableAlerts && fullEvent.severity === AuditSeverity.CRITICAL) {
|
|
145
|
+
this.emitAlert(fullEvent);
|
|
146
|
+
}
|
|
147
|
+
// Check if we need to rotate log file
|
|
148
|
+
this.checkLogRotation();
|
|
149
|
+
this.eventCount++;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Log critical security event (convenience method)
|
|
153
|
+
*/
|
|
154
|
+
logCritical(event) {
|
|
155
|
+
this.log({
|
|
156
|
+
...event,
|
|
157
|
+
severity: AuditSeverity.CRITICAL,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Log warning event (convenience method)
|
|
162
|
+
*/
|
|
163
|
+
logWarning(event) {
|
|
164
|
+
this.log({
|
|
165
|
+
...event,
|
|
166
|
+
severity: AuditSeverity.WARNING,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Log error event (convenience method)
|
|
171
|
+
*/
|
|
172
|
+
logError(event) {
|
|
173
|
+
this.log({
|
|
174
|
+
...event,
|
|
175
|
+
severity: AuditSeverity.ERROR,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Log info event (convenience method)
|
|
180
|
+
*/
|
|
181
|
+
logInfo(event) {
|
|
182
|
+
this.log({
|
|
183
|
+
...event,
|
|
184
|
+
severity: AuditSeverity.INFO,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Verify log integrity
|
|
189
|
+
*
|
|
190
|
+
* Checks the hash chain to detect tampering
|
|
191
|
+
*/
|
|
192
|
+
verifyIntegrity(logFile) {
|
|
193
|
+
const file = logFile || this.currentLogFile;
|
|
194
|
+
if (!existsSync(file)) {
|
|
195
|
+
return { valid: false, errors: ['Log file does not exist'] };
|
|
196
|
+
}
|
|
197
|
+
const errors = [];
|
|
198
|
+
const lines = readFileSync(file, 'utf8').split('\n').filter(l => l.trim());
|
|
199
|
+
let previousHash = null;
|
|
200
|
+
for (let i = 0; i < lines.length; i++) {
|
|
201
|
+
try {
|
|
202
|
+
const event = JSON.parse(lines[i]);
|
|
203
|
+
// Verify hash chain (normalize undefined to null for comparison)
|
|
204
|
+
const eventPrevHash = event.previousHash || null;
|
|
205
|
+
if (eventPrevHash !== previousHash) {
|
|
206
|
+
errors.push(`Event ${event.id} (line ${i + 1}): Hash chain broken. ` +
|
|
207
|
+
`Expected previous hash: ${previousHash}, got: ${eventPrevHash}`);
|
|
208
|
+
}
|
|
209
|
+
// Verify event hash
|
|
210
|
+
const calculatedHash = this.calculateHash(event);
|
|
211
|
+
if (event.hash !== calculatedHash) {
|
|
212
|
+
errors.push(`Event ${event.id} (line ${i + 1}): Hash mismatch. ` +
|
|
213
|
+
`Event may have been tampered with.`);
|
|
214
|
+
}
|
|
215
|
+
previousHash = event.hash || null;
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
errors.push(`Line ${i + 1}: Invalid JSON - ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
valid: errors.length === 0,
|
|
223
|
+
errors,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Get audit statistics
|
|
228
|
+
*/
|
|
229
|
+
getStats() {
|
|
230
|
+
return {
|
|
231
|
+
totalEvents: this.eventCount,
|
|
232
|
+
logDirectory: this.config.logDirectory,
|
|
233
|
+
currentLogFile: this.currentLogFile,
|
|
234
|
+
retentionDays: this.config.retentionDays,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Generate unique event ID
|
|
239
|
+
*/
|
|
240
|
+
generateEventId() {
|
|
241
|
+
const timestamp = Date.now();
|
|
242
|
+
const random = Math.random().toString(36).substring(2, 11);
|
|
243
|
+
return `evt_${timestamp}_${random}`;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Calculate SHA-256 hash of event (excluding hash field itself)
|
|
247
|
+
*/
|
|
248
|
+
calculateHash(event) {
|
|
249
|
+
// Create copy without hash field
|
|
250
|
+
const { hash, ...eventWithoutHash } = event;
|
|
251
|
+
// Sort keys for consistent hashing, filter out undefined values
|
|
252
|
+
const sortedKeys = Object.keys(eventWithoutHash)
|
|
253
|
+
.filter(key => eventWithoutHash[key] !== undefined)
|
|
254
|
+
.sort();
|
|
255
|
+
const data = sortedKeys.map(key => {
|
|
256
|
+
const value = eventWithoutHash[key];
|
|
257
|
+
return `${key}:${JSON.stringify(value)}`;
|
|
258
|
+
}).join('|');
|
|
259
|
+
return createHash('sha256').update(data).digest('hex');
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Write event to log file
|
|
263
|
+
*/
|
|
264
|
+
writeEvent(event) {
|
|
265
|
+
const logLine = JSON.stringify(event) + '\n';
|
|
266
|
+
try {
|
|
267
|
+
writeFileSync(this.currentLogFile, logLine, { flag: 'a' });
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
// Fallback: If current log file is locked/corrupted, create new one
|
|
271
|
+
console.error('Failed to write to audit log:', error);
|
|
272
|
+
this.rotateLogFile();
|
|
273
|
+
writeFileSync(this.currentLogFile, logLine, { flag: 'a' });
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Get current log file path
|
|
278
|
+
*/
|
|
279
|
+
getCurrentLogFile() {
|
|
280
|
+
const date = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
|
|
281
|
+
return join(this.config.logDirectory, `audit-${date}.jsonl`);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Load last hash from current log file
|
|
285
|
+
*/
|
|
286
|
+
loadLastHash() {
|
|
287
|
+
if (!existsSync(this.currentLogFile)) {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
const lines = readFileSync(this.currentLogFile, 'utf8').split('\n').filter(l => l.trim());
|
|
292
|
+
if (lines.length === 0)
|
|
293
|
+
return null;
|
|
294
|
+
const lastEvent = JSON.parse(lines[lines.length - 1]);
|
|
295
|
+
return lastEvent.hash || null;
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Check if log file needs rotation
|
|
303
|
+
*/
|
|
304
|
+
checkLogRotation() {
|
|
305
|
+
try {
|
|
306
|
+
const stats = statSync(this.currentLogFile);
|
|
307
|
+
if (stats.size >= this.config.maxFileSize) {
|
|
308
|
+
this.rotateLogFile();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
// File doesn't exist or can't be read - will be created on next write
|
|
313
|
+
}
|
|
314
|
+
// Check if date changed (new day)
|
|
315
|
+
const expectedFile = this.getCurrentLogFile();
|
|
316
|
+
if (expectedFile !== this.currentLogFile) {
|
|
317
|
+
this.rotateLogFile();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Rotate log file
|
|
322
|
+
*/
|
|
323
|
+
rotateLogFile() {
|
|
324
|
+
this.currentLogFile = this.getCurrentLogFile();
|
|
325
|
+
this.lastHash = this.loadLastHash();
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Clean up old log files (retention policy)
|
|
329
|
+
*/
|
|
330
|
+
cleanupOldLogs() {
|
|
331
|
+
try {
|
|
332
|
+
if (!existsSync(this.config.logDirectory)) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
const files = readdirSync(this.config.logDirectory);
|
|
336
|
+
const now = Date.now();
|
|
337
|
+
const retentionMs = this.config.retentionDays * 24 * 60 * 60 * 1000;
|
|
338
|
+
for (const file of files) {
|
|
339
|
+
if (!file.startsWith('audit-') || !file.endsWith('.jsonl')) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
const filePath = join(this.config.logDirectory, file);
|
|
343
|
+
const stats = statSync(filePath);
|
|
344
|
+
const age = now - stats.mtimeMs;
|
|
345
|
+
if (age > retentionMs) {
|
|
346
|
+
unlinkSync(filePath);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
console.error('Failed to clean up old audit logs:', error);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Emit alert for critical events
|
|
356
|
+
*/
|
|
357
|
+
emitAlert(event) {
|
|
358
|
+
for (const callback of this.alertCallbacks) {
|
|
359
|
+
try {
|
|
360
|
+
callback(event);
|
|
361
|
+
}
|
|
362
|
+
catch (error) {
|
|
363
|
+
console.error('Alert callback failed:', error);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Get audit logger singleton
|
|
370
|
+
*/
|
|
371
|
+
export function getAuditLogger(config) {
|
|
372
|
+
return AuditLogger.getInstance(config);
|
|
373
|
+
}
|
|
374
|
+
//# sourceMappingURL=audit-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-logger.js","sourceRoot":"","sources":["../../src/utils/audit-logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command Security Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides secure command execution with whitelisting and validation.
|
|
5
|
+
* Prevents command injection vulnerabilities (REQ-SEC-001).
|
|
6
|
+
*
|
|
7
|
+
* @module command-security
|
|
8
|
+
*/
|
|
9
|
+
import type { ToolResult } from '../types/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* Whitelist of safe commands allowed for execution.
|
|
12
|
+
* Only these commands can be executed via the BashTool.
|
|
13
|
+
*
|
|
14
|
+
* CRITICAL: Do not add arbitrary commands without security review.
|
|
15
|
+
*/
|
|
16
|
+
export declare const SAFE_COMMANDS: readonly ["ls", "grep", "find", "cat", "head", "tail", "wc", "sort", "uniq", "cut", "awk", "sed", "pwd", "echo", "date", "whoami", "hostname", "git", "rg", "fd", "rm", "mkdir", "touch", "cp", "mv"];
|
|
17
|
+
export type SafeCommand = typeof SAFE_COMMANDS[number];
|
|
18
|
+
/**
|
|
19
|
+
* Parsed command structure
|
|
20
|
+
*/
|
|
21
|
+
export interface ParsedCommand {
|
|
22
|
+
command: SafeCommand;
|
|
23
|
+
args: string[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Command execution options
|
|
27
|
+
*/
|
|
28
|
+
export interface CommandExecutionOptions {
|
|
29
|
+
cwd?: string;
|
|
30
|
+
timeout?: number;
|
|
31
|
+
maxBuffer?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Sanitize environment variables for child process.
|
|
35
|
+
* Only includes safe environment variables to prevent injection.
|
|
36
|
+
*
|
|
37
|
+
* @param env - Original process environment
|
|
38
|
+
* @returns Sanitized environment object
|
|
39
|
+
*/
|
|
40
|
+
export declare function sanitizeEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
41
|
+
/**
|
|
42
|
+
* Parse a command string into command and arguments.
|
|
43
|
+
* Validates that the command is in the whitelist.
|
|
44
|
+
*
|
|
45
|
+
* @param commandString - Full command string (e.g., "ls -la /tmp")
|
|
46
|
+
* @returns Parsed command structure
|
|
47
|
+
* @throws Error if command is not whitelisted
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseCommand(commandString: string): ParsedCommand;
|
|
50
|
+
/**
|
|
51
|
+
* Validate command arguments for shell metacharacters.
|
|
52
|
+
* Prevents command injection via argument injection.
|
|
53
|
+
*
|
|
54
|
+
* @param args - Command arguments to validate
|
|
55
|
+
* @returns Validation result
|
|
56
|
+
*/
|
|
57
|
+
export declare function validateArguments(args: string[]): {
|
|
58
|
+
valid: boolean;
|
|
59
|
+
errors: string[];
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Execute a safe command with validation.
|
|
63
|
+
* Uses execFile to avoid shell invocation and command injection.
|
|
64
|
+
*
|
|
65
|
+
* SECURITY: This function uses execFile instead of spawn('bash', ['-c'])
|
|
66
|
+
* to prevent command injection attacks.
|
|
67
|
+
*
|
|
68
|
+
* @param commandString - Command to execute
|
|
69
|
+
* @param options - Execution options
|
|
70
|
+
* @returns Tool result with output or error
|
|
71
|
+
*/
|
|
72
|
+
export declare function executeSafeCommand(commandString: string, options?: CommandExecutionOptions): Promise<ToolResult>;
|
|
73
|
+
/**
|
|
74
|
+
* Check if a command is safe to execute.
|
|
75
|
+
*
|
|
76
|
+
* @param command - Command name to check
|
|
77
|
+
* @returns True if command is in whitelist
|
|
78
|
+
*/
|
|
79
|
+
export declare function isSafeCommand(command: string): command is SafeCommand;
|
|
80
|
+
/**
|
|
81
|
+
* Get list of safe commands (for documentation/help).
|
|
82
|
+
*
|
|
83
|
+
* @returns Array of safe command names
|
|
84
|
+
*/
|
|
85
|
+
export declare function getSafeCommands(): readonly string[];
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command Security Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides secure command execution with whitelisting and validation.
|
|
5
|
+
* Prevents command injection vulnerabilities (REQ-SEC-001).
|
|
6
|
+
*
|
|
7
|
+
* @module command-security
|
|
8
|
+
*/
|
|
9
|
+
import { execFile } from 'child_process';
|
|
10
|
+
import { promisify } from 'util';
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
12
|
+
/**
|
|
13
|
+
* Whitelist of safe commands allowed for execution.
|
|
14
|
+
* Only these commands can be executed via the BashTool.
|
|
15
|
+
*
|
|
16
|
+
* CRITICAL: Do not add arbitrary commands without security review.
|
|
17
|
+
*/
|
|
18
|
+
export const SAFE_COMMANDS = [
|
|
19
|
+
'ls',
|
|
20
|
+
'grep',
|
|
21
|
+
'find',
|
|
22
|
+
'cat',
|
|
23
|
+
'head',
|
|
24
|
+
'tail',
|
|
25
|
+
'wc',
|
|
26
|
+
'sort',
|
|
27
|
+
'uniq',
|
|
28
|
+
'cut',
|
|
29
|
+
'awk',
|
|
30
|
+
'sed',
|
|
31
|
+
'pwd',
|
|
32
|
+
'echo',
|
|
33
|
+
'date',
|
|
34
|
+
'whoami',
|
|
35
|
+
'hostname',
|
|
36
|
+
'git',
|
|
37
|
+
'rg',
|
|
38
|
+
'fd',
|
|
39
|
+
'rm', // File deletion (safe with validation)
|
|
40
|
+
'mkdir', // Directory creation
|
|
41
|
+
'touch', // File creation
|
|
42
|
+
'cp', // File copy
|
|
43
|
+
'mv', // File move/rename
|
|
44
|
+
];
|
|
45
|
+
/**
|
|
46
|
+
* Environment variables safe to pass to child processes.
|
|
47
|
+
* Only these will be included in the child process environment.
|
|
48
|
+
*/
|
|
49
|
+
const SAFE_ENV_VARS = [
|
|
50
|
+
'PATH',
|
|
51
|
+
'HOME',
|
|
52
|
+
'USER',
|
|
53
|
+
'LANG',
|
|
54
|
+
'LC_ALL',
|
|
55
|
+
'TERM',
|
|
56
|
+
'TMPDIR',
|
|
57
|
+
'PWD',
|
|
58
|
+
];
|
|
59
|
+
/**
|
|
60
|
+
* Shell metacharacters that are forbidden in command arguments.
|
|
61
|
+
* These could enable command injection if not properly validated.
|
|
62
|
+
*/
|
|
63
|
+
const SHELL_METACHARACTERS = /[;&|`$(){}[\]<>'"\\*?~!#]/;
|
|
64
|
+
/**
|
|
65
|
+
* Sanitize environment variables for child process.
|
|
66
|
+
* Only includes safe environment variables to prevent injection.
|
|
67
|
+
*
|
|
68
|
+
* @param env - Original process environment
|
|
69
|
+
* @returns Sanitized environment object
|
|
70
|
+
*/
|
|
71
|
+
export function sanitizeEnv(env) {
|
|
72
|
+
const sanitized = {};
|
|
73
|
+
for (const key of SAFE_ENV_VARS) {
|
|
74
|
+
if (env[key]) {
|
|
75
|
+
sanitized[key] = env[key];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return sanitized;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Parse a command string into command and arguments.
|
|
82
|
+
* Validates that the command is in the whitelist.
|
|
83
|
+
*
|
|
84
|
+
* @param commandString - Full command string (e.g., "ls -la /tmp")
|
|
85
|
+
* @returns Parsed command structure
|
|
86
|
+
* @throws Error if command is not whitelisted
|
|
87
|
+
*/
|
|
88
|
+
export function parseCommand(commandString) {
|
|
89
|
+
const trimmed = commandString.trim();
|
|
90
|
+
if (!trimmed) {
|
|
91
|
+
throw new Error('Empty command string');
|
|
92
|
+
}
|
|
93
|
+
// Simple split by whitespace
|
|
94
|
+
const parts = trimmed.split(/\s+/);
|
|
95
|
+
const command = parts[0];
|
|
96
|
+
const args = parts.slice(1);
|
|
97
|
+
// Validate command is in whitelist
|
|
98
|
+
if (!SAFE_COMMANDS.includes(command)) {
|
|
99
|
+
throw new Error(`Command '${command}' not in whitelist. Allowed commands: ${SAFE_COMMANDS.join(', ')}`);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
command: command,
|
|
103
|
+
args,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Validate command arguments for shell metacharacters.
|
|
108
|
+
* Prevents command injection via argument injection.
|
|
109
|
+
*
|
|
110
|
+
* @param args - Command arguments to validate
|
|
111
|
+
* @returns Validation result
|
|
112
|
+
*/
|
|
113
|
+
export function validateArguments(args) {
|
|
114
|
+
const errors = [];
|
|
115
|
+
for (let i = 0; i < args.length; i++) {
|
|
116
|
+
const arg = args[i];
|
|
117
|
+
// Check for shell metacharacters
|
|
118
|
+
if (SHELL_METACHARACTERS.test(arg)) {
|
|
119
|
+
errors.push(`Argument ${i} contains forbidden shell metacharacters: "${arg}"`);
|
|
120
|
+
}
|
|
121
|
+
// Check for null bytes
|
|
122
|
+
if (arg.includes('\0')) {
|
|
123
|
+
errors.push(`Argument ${i} contains null byte`);
|
|
124
|
+
}
|
|
125
|
+
// Check length (prevent buffer overflow)
|
|
126
|
+
if (arg.length > 10000) {
|
|
127
|
+
errors.push(`Argument ${i} exceeds maximum length (10000 chars)`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
valid: errors.length === 0,
|
|
132
|
+
errors,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Execute a safe command with validation.
|
|
137
|
+
* Uses execFile to avoid shell invocation and command injection.
|
|
138
|
+
*
|
|
139
|
+
* SECURITY: This function uses execFile instead of spawn('bash', ['-c'])
|
|
140
|
+
* to prevent command injection attacks.
|
|
141
|
+
*
|
|
142
|
+
* @param commandString - Command to execute
|
|
143
|
+
* @param options - Execution options
|
|
144
|
+
* @returns Tool result with output or error
|
|
145
|
+
*/
|
|
146
|
+
export async function executeSafeCommand(commandString, options = {}) {
|
|
147
|
+
try {
|
|
148
|
+
// 1. Parse command into command + args
|
|
149
|
+
const parsed = parseCommand(commandString);
|
|
150
|
+
// 2. Validate arguments
|
|
151
|
+
const validation = validateArguments(parsed.args);
|
|
152
|
+
if (!validation.valid) {
|
|
153
|
+
return {
|
|
154
|
+
success: false,
|
|
155
|
+
error: `Command validation failed:\n${validation.errors.join('\n')}`,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
// 3. Prepare execution options
|
|
159
|
+
const execOptions = {
|
|
160
|
+
cwd: options.cwd || process.cwd(),
|
|
161
|
+
env: sanitizeEnv(process.env),
|
|
162
|
+
timeout: options.timeout || 30000, // 30 second default
|
|
163
|
+
maxBuffer: options.maxBuffer || 1024 * 1024, // 1MB default
|
|
164
|
+
};
|
|
165
|
+
// 4. Execute using execFile (no shell invocation)
|
|
166
|
+
const { stdout, stderr } = await execFileAsync(parsed.command, parsed.args, execOptions);
|
|
167
|
+
// 5. Return successful result
|
|
168
|
+
return {
|
|
169
|
+
success: true,
|
|
170
|
+
output: stdout || stderr || 'Command completed successfully',
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
// Handle execution errors
|
|
175
|
+
const errorMessage = error.message || String(error);
|
|
176
|
+
const exitCode = error.code || 'unknown';
|
|
177
|
+
return {
|
|
178
|
+
success: false,
|
|
179
|
+
error: `Command execution failed (exit code: ${exitCode}): ${errorMessage}`,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Check if a command is safe to execute.
|
|
185
|
+
*
|
|
186
|
+
* @param command - Command name to check
|
|
187
|
+
* @returns True if command is in whitelist
|
|
188
|
+
*/
|
|
189
|
+
export function isSafeCommand(command) {
|
|
190
|
+
return SAFE_COMMANDS.includes(command);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Get list of safe commands (for documentation/help).
|
|
194
|
+
*
|
|
195
|
+
* @returns Array of safe command names
|
|
196
|
+
*/
|
|
197
|
+
export function getSafeCommands() {
|
|
198
|
+
return SAFE_COMMANDS;
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=command-security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-security.js","sourceRoot":"","sources":["../../src/utils/command-security.ts"],"names":[],"mappings":"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"}
|