mstro-app 0.3.1 → 0.3.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.
@@ -255,6 +255,28 @@ function readTextContent(fullPath, filePath, fileName, stats) {
255
255
  size: stats.size, modifiedAt: stats.mtime.toISOString()
256
256
  };
257
257
  }
258
+ function validateFileAccess(fullPath, filePath, fileName, workingDir) {
259
+ const normalizedPath = join(fullPath);
260
+ if (!normalizedPath.startsWith(join(workingDir)) && !isPathInSafeLocation(normalizedPath)) {
261
+ return { path: filePath, fileName, content: '', error: 'Access denied: path outside allowed locations' };
262
+ }
263
+ if (!existsSync(fullPath)) {
264
+ return { path: filePath, fileName, content: '', error: 'File not found' };
265
+ }
266
+ return null;
267
+ }
268
+ function readValidatedFile(fullPath, filePath, fileName, stats) {
269
+ if (stats.isDirectory())
270
+ return readDirectoryContent(fullPath, filePath, fileName);
271
+ const isBin = isBinaryFile(fullPath);
272
+ const MAX_FILE_SIZE = isBin ? 10 * 1024 * 1024 : 1024 * 1024;
273
+ if (stats.size > MAX_FILE_SIZE) {
274
+ return { path: filePath, fileName, content: '', size: stats.size, error: `File too large (${Math.round(stats.size / 1024)}KB). Maximum is ${isBin ? '10MB' : '1MB'}.` };
275
+ }
276
+ return isBin
277
+ ? readBinaryContent(fullPath, filePath, fileName, stats)
278
+ : readTextContent(fullPath, filePath, fileName, stats);
279
+ }
258
280
  /**
259
281
  * Read file content for context injection
260
282
  */
@@ -262,26 +284,10 @@ export function readFileContent(filePath, workingDir) {
262
284
  try {
263
285
  const fullPath = filePath.startsWith('/') ? filePath : join(workingDir, filePath);
264
286
  const fileName = fullPath.split(sep).pop() || filePath;
265
- const normalizedPath = join(fullPath);
266
- const isInWorkingDir = normalizedPath.startsWith(join(workingDir));
267
- if (!isInWorkingDir && !isPathInSafeLocation(normalizedPath)) {
268
- return { path: filePath, fileName, content: '', error: 'Access denied: path outside allowed locations' };
269
- }
270
- if (!existsSync(fullPath)) {
271
- return { path: filePath, fileName, content: '', error: 'File not found' };
272
- }
273
- const stats = statSync(fullPath);
274
- if (stats.isDirectory()) {
275
- return readDirectoryContent(fullPath, filePath, fileName);
276
- }
277
- const isBin = isBinaryFile(fullPath);
278
- const MAX_FILE_SIZE = isBin ? 10 * 1024 * 1024 : 1024 * 1024;
279
- if (stats.size > MAX_FILE_SIZE) {
280
- return { path: filePath, fileName, content: '', size: stats.size, error: `File too large (${Math.round(stats.size / 1024)}KB). Maximum is ${isBin ? '10MB' : '1MB'}.` };
281
- }
282
- return isBin
283
- ? readBinaryContent(fullPath, filePath, fileName, stats)
284
- : readTextContent(fullPath, filePath, fileName, stats);
287
+ const accessError = validateFileAccess(fullPath, filePath, fileName, workingDir);
288
+ if (accessError)
289
+ return accessError;
290
+ return readValidatedFile(fullPath, filePath, fileName, statSync(fullPath));
285
291
  }
286
292
  catch (error) {
287
293
  console.error('[FileUtils] Error reading file:', error);
@@ -1 +1 @@
1
- {"version":3,"file":"file-utils.js","sourceRoot":"","sources":["../../../../server/services/websocket/file-utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"file-utils.js","sourceRoot":"","sources":["../../../../server/services/websocket/file-utils.ts"],"names":[],"mappings":"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"}
@@ -10,16 +10,4 @@ export declare const MSTRO_ROOT: string;
10
10
  * Path to the MCP bouncer server script
11
11
  */
12
12
  export declare const MCP_SERVER_PATH: string;
13
- /**
14
- * Path to the MCP bouncer configuration template
15
- */
16
- export declare const MCP_CONFIG_TEMPLATE_PATH: string;
17
- /**
18
- * Path to the hooks directory
19
- */
20
- export declare const HOOKS_DIR: string;
21
- /**
22
- * Path to the bouncer hook script
23
- */
24
- export declare const BOUNCER_HOOK: string;
25
13
  //# sourceMappingURL=paths.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../server/utils/paths.ts"],"names":[],"mappings":"AAiBA;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,QAA8B,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,eAAe,QAA8C,CAAC;AAE3E;;GAEG;AACH,eAAO,MAAM,wBAAwB,QAAgD,CAAC;AAEtF;;GAEG;AACH,eAAO,MAAM,SAAS,QAA+B,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,YAAY,QAAmC,CAAC"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../server/utils/paths.ts"],"names":[],"mappings":"AAiBA;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,QAA8B,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,eAAe,QAA8C,CAAC"}
@@ -23,16 +23,4 @@ export const MSTRO_ROOT = resolve(__dirname, '../..');
23
23
  * Path to the MCP bouncer server script
24
24
  */
25
25
  export const MCP_SERVER_PATH = resolve(MSTRO_ROOT, 'server/mcp/server.ts');
26
- /**
27
- * Path to the MCP bouncer configuration template
28
- */
29
- export const MCP_CONFIG_TEMPLATE_PATH = resolve(MSTRO_ROOT, 'mstro-bouncer-mcp.json');
30
- /**
31
- * Path to the hooks directory
32
- */
33
- export const HOOKS_DIR = resolve(MSTRO_ROOT, 'hooks');
34
- /**
35
- * Path to the bouncer hook script
36
- */
37
- export const BOUNCER_HOOK = resolve(HOOKS_DIR, 'bouncer.sh');
38
26
  //# sourceMappingURL=paths.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../server/utils/paths.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,gEAAgE;AAEhE;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,kDAAkD;AAClD,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAEtD;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC;AAE3E;;GAEG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC,UAAU,EAAE,wBAAwB,CAAC,CAAC;AAEtF;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AAEtD;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC"}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../server/utils/paths.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,gEAAgE;AAEhE;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,kDAAkD;AAClD,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAEtD;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mstro-app",
3
- "version": "0.3.1",
3
+ "version": "0.3.4",
4
4
  "description": "Run Claude Code from any browser - streams live sessions from your machine to mstro.app",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -29,7 +29,6 @@
29
29
  "files": [
30
30
  "bin/",
31
31
  "server/",
32
- "hooks/",
33
32
  "dist/",
34
33
  "PRIVACY.md",
35
34
  "!bin/release.sh",
package/server/README.md CHANGED
@@ -93,7 +93,6 @@ server/
93
93
  │ ├── server.ts # MCP server entry
94
94
  │ ├── bouncer-integration.ts # 2-layer security (patterns + AI)
95
95
  │ ├── security-patterns.ts # Threat pattern matching
96
- │ ├── bouncer-cli.ts # CLI wrapper for hook integration
97
96
  │ └── security-audit.ts # Audit logging
98
97
  └── utils/
99
98
  ├── paths.ts # Path utilities
@@ -22,7 +22,6 @@ MCP (Model Context Protocol) server that provides tool approval decisions for Cl
22
22
  - **bouncer-integration.ts** — Core 2-layer security review logic. Orchestrates pattern check → AI analysis flow.
23
23
  - **security-patterns.ts** — Pattern definitions: CRITICAL_THREATS, SAFE_OPERATIONS, NEEDS_AI_REVIEW, SENSITIVE_PATHS.
24
24
  - **security-audit.ts** — Audit logging to `~/.mstro/logs/bouncer-audit.jsonl` (JSON Lines format).
25
- - **bouncer-cli.ts** — Shell-callable wrapper invoked by `~/.claude/hooks/bouncer.sh`. Reads JSON from stdin, outputs decision to stdout.
26
25
 
27
26
  ## Usage
28
27
 
@@ -49,10 +48,6 @@ claude --print \
49
48
 
50
49
  The MCP config is auto-generated by the headless runner at `~/.mstro/mcp-config.json`. It includes the bouncer server plus any user-configured MCP servers from `~/.claude.json`.
51
50
 
52
- ### Hook Integration
53
-
54
- When installed via `mstro configure-hooks`, a shell script at `~/.claude/hooks/bouncer.sh` calls `bouncer-cli.ts` as a PreToolUse hook. This provides security for both mstro sessions and standalone Claude Code usage.
55
-
56
51
  ## Environment Variables
57
52
 
58
53
  | Variable | Default | Description |
@@ -271,18 +271,50 @@ or
271
271
  });
272
272
  }
273
273
 
274
+ /**
275
+ * Finalize a bouncer decision: log, track analytics, cache, and return.
276
+ */
277
+ function finalizeDecision(
278
+ operation: string,
279
+ decision: BouncerDecision,
280
+ layer: string,
281
+ startTime: number,
282
+ context: BouncerReviewRequest['context'],
283
+ logFn: typeof import('./security-audit.js')['logBouncerDecision'],
284
+ opts?: { error?: string; skipCache?: boolean; skipAnalytics?: boolean },
285
+ ): BouncerDecision {
286
+ const latencyMs = Math.round(performance.now() - startTime);
287
+
288
+ logFn(operation, decision.decision, decision.confidence, decision.reasoning, {
289
+ context, threatLevel: decision.threatLevel, layer, latencyMs, ...(opts?.error && { error: opts.error }),
290
+ });
291
+
292
+ if (!opts?.skipAnalytics) {
293
+ const event = decision.decision === 'deny' ? AnalyticsEvents.BOUNCER_TOOL_DENIED : AnalyticsEvents.BOUNCER_TOOL_ALLOWED;
294
+ trackEvent(event, {
295
+ layer,
296
+ operation_length: operation.length,
297
+ threat_level: decision.threatLevel,
298
+ confidence: decision.confidence,
299
+ latency_ms: latencyMs,
300
+ });
301
+ }
302
+
303
+ if (!opts?.skipCache) cacheDecision(operation, decision);
304
+ return decision;
305
+ }
306
+
274
307
  /**
275
308
  * Main bouncer review function - 2-layer hybrid system
276
309
  */
277
310
  export async function reviewOperation(request: BouncerReviewRequest): Promise<BouncerDecision> {
278
- // Import audit logger
279
311
  const { logBouncerDecision } = await import('./security-audit.js');
280
-
281
312
  const startTime = performance.now();
282
-
283
313
  const { operation } = request;
314
+ const fin = (d: BouncerDecision, layer: string, opts?: Parameters<typeof finalizeDecision>[6]) =>
315
+ finalizeDecision(operation, d, layer, startTime, request.context, logBouncerDecision, opts);
284
316
 
285
- // Check cache first (pattern-layer decisions and prior Haiku results)
317
+ // Check cache first
286
318
  const cached = getCachedDecision(operation);
287
319
  if (cached) {
288
320
  console.error(`[Bouncer] ⚡ Cache hit: ${cached.decision} (${cached.confidence}%)`);
@@ -295,257 +327,70 @@ export async function reviewOperation(request: BouncerReviewRequest): Promise<Bo
295
327
  console.error(`[Bouncer] User request: ${request.context.userRequest}`);
296
328
  }
297
329
 
298
- // ========================================
299
- // PRE-CHECK: Malformed/empty tool calls
300
- // ========================================
301
- // Empty-param Edit/Write calls are no-ops that will fail validation anyway.
302
- // Allow immediately instead of wasting ~8s on Haiku analysis.
330
+ // PRE-CHECK: Empty-param Edit/Write calls are no-ops — allow immediately
303
331
  const toolInput = request.context?.toolInput;
304
332
  if (toolInput && typeof toolInput === 'object' && Object.keys(toolInput).length === 0) {
305
333
  console.error('[Bouncer] ⚡ Fast path: Empty tool parameters (no-op)');
306
- const latencyMs = Math.round(performance.now() - startTime);
307
-
308
- const decision: BouncerDecision = {
309
- decision: 'allow',
310
- confidence: 95,
311
- reasoning: 'Empty tool parameters - operation is a no-op with no side effects.',
312
- threatLevel: 'low'
313
- };
314
-
315
- logBouncerDecision(
316
- operation,
317
- decision.decision,
318
- decision.confidence,
319
- decision.reasoning,
320
- { context: request.context, threatLevel: decision.threatLevel, layer: 'pattern-noop', latencyMs }
321
- );
322
-
323
- cacheDecision(operation, decision);
324
- return decision;
334
+ return fin({ decision: 'allow', confidence: 95, reasoning: 'Empty tool parameters - operation is a no-op with no side effects.', threatLevel: 'low' }, 'pattern-noop', { skipAnalytics: true });
325
335
  }
326
336
 
327
- // ========================================
328
337
  // LAYER 1: Pattern-Based Fast Path (< 5ms)
329
- // ========================================
330
338
 
331
- // Check safe operations FIRST - allows trusted sources (e.g., brew, rustup)
339
+ // Check safe operations FIRST allows trusted sources (e.g., brew, rustup)
332
340
  // to pass before hitting critical threat patterns like curl|bash
333
341
  const safeOperation = matchesPattern(operation, SAFE_OPERATIONS);
334
342
  if (safeOperation) {
335
343
  console.error('[Bouncer] ⚡ Fast path: Safe operation approved');
336
- const latencyMs = Math.round(performance.now() - startTime);
337
-
338
- const decision: BouncerDecision = {
339
- decision: 'allow',
340
- confidence: 95,
341
- reasoning: 'Operation matches known-safe patterns. No security concerns detected.',
342
- threatLevel: 'low'
343
- };
344
-
345
- logBouncerDecision(
346
- operation,
347
- decision.decision,
348
- decision.confidence,
349
- decision.reasoning,
350
- { context: request.context, threatLevel: decision.threatLevel, layer: 'pattern-safe', latencyMs }
351
- );
352
- trackEvent(AnalyticsEvents.BOUNCER_TOOL_ALLOWED, {
353
- layer: 'pattern-safe',
354
- operation_length: operation.length,
355
- threat_level: 'low',
356
- confidence: 95,
357
- latency_ms: latencyMs,
358
- });
359
-
360
- cacheDecision(operation, decision);
361
- return decision;
344
+ return fin({ decision: 'allow', confidence: 95, reasoning: 'Operation matches known-safe patterns. No security concerns detected.', threatLevel: 'low' }, 'pattern-safe');
362
345
  }
363
346
 
364
- // Check critical threats (catastrophic operations like rm -rf /, fork bombs)
365
- // These are ALWAYS denied - no context can justify them
347
+ // Critical threats (rm -rf /, fork bombs) — ALWAYS denied
366
348
  const criticalThreat = matchesPattern(operation, CRITICAL_THREATS);
367
349
  if (criticalThreat) {
368
350
  console.error('[Bouncer] ⚡ Fast path: CRITICAL THREAT detected');
369
- const latencyMs = Math.round(performance.now() - startTime);
370
-
371
- const decision: BouncerDecision = {
372
- decision: 'deny',
373
- confidence: 99,
374
- reasoning: `🚨 CRITICAL THREAT: ${criticalThreat.reason}`,
375
- threatLevel: 'critical',
351
+ return fin({
352
+ decision: 'deny', confidence: 99, reasoning: `🚨 CRITICAL THREAT: ${criticalThreat.reason}`, threatLevel: 'critical',
376
353
  alternative: 'This operation should never be performed. If you need to accomplish a specific task, please describe your goal and I can suggest safe alternatives.',
377
- enforceable: true
378
- };
379
-
380
- logBouncerDecision(
381
- operation,
382
- decision.decision,
383
- decision.confidence,
384
- decision.reasoning,
385
- { context: request.context, threatLevel: decision.threatLevel, layer: 'pattern-critical', latencyMs }
386
- );
387
- trackEvent(AnalyticsEvents.BOUNCER_TOOL_DENIED, {
388
- layer: 'pattern-critical',
389
- operation_length: operation.length,
390
- threat_level: 'critical',
391
- confidence: 99,
392
- latency_ms: latencyMs,
393
- });
394
-
395
- cacheDecision(operation, decision);
396
- return decision;
354
+ enforceable: true,
355
+ }, 'pattern-critical');
397
356
  }
398
357
 
399
- // ========================================
400
358
  // LAYER 2: Haiku AI Analysis (~200-500ms)
401
- // ========================================
402
359
 
403
- // Only invoke AI for operations that truly need context
360
+ // Default allow for operations that don't need AI review
404
361
  if (!requiresAIReview(operation)) {
405
- // Default allow for operations that don't match any pattern
406
362
  console.error('[Bouncer] ⚡ Fast path: No concerning patterns, allowing');
407
- const latencyMs = Math.round(performance.now() - startTime);
408
-
409
- const decision: BouncerDecision = {
410
- decision: 'allow',
411
- confidence: 80,
412
- reasoning: 'Operation appears safe based on pattern analysis. No obvious threats detected.',
413
- threatLevel: 'low'
414
- };
415
-
416
- logBouncerDecision(
417
- operation,
418
- decision.decision,
419
- decision.confidence,
420
- decision.reasoning,
421
- { context: request.context, threatLevel: decision.threatLevel, layer: 'pattern-default', latencyMs }
422
- );
423
- trackEvent(AnalyticsEvents.BOUNCER_TOOL_ALLOWED, {
424
- layer: 'pattern-default',
425
- operation_length: operation.length,
426
- threat_level: 'low',
427
- confidence: 80,
428
- latency_ms: latencyMs,
429
- });
430
-
431
- cacheDecision(operation, decision);
432
- return decision;
363
+ return fin({ decision: 'allow', confidence: 80, reasoning: 'Operation appears safe based on pattern analysis. No obvious threats detected.', threatLevel: 'low' }, 'pattern-default');
433
364
  }
434
365
 
435
- // Check if AI analysis is enabled
436
- const useAI = process.env.BOUNCER_USE_AI !== 'false';
437
-
438
- if (!useAI) {
366
+ if (process.env.BOUNCER_USE_AI === 'false') {
439
367
  console.error('[Bouncer] AI analysis disabled (BOUNCER_USE_AI=false)');
440
- const latencyMs = Math.round(performance.now() - startTime);
441
-
442
- const decision: BouncerDecision = {
443
- decision: 'warn_allow',
444
- confidence: 60,
445
- reasoning: 'Operation requires review but AI analysis is disabled. Proceeding with caution.',
446
- threatLevel: 'medium'
447
- };
448
-
449
- logBouncerDecision(
450
- operation,
451
- decision.decision,
452
- decision.confidence,
453
- decision.reasoning,
454
- { context: request.context, threatLevel: decision.threatLevel, layer: 'ai-disabled', latencyMs }
455
- );
456
-
457
- return decision;
368
+ return fin({ decision: 'warn_allow', confidence: 60, reasoning: 'Operation requires review but AI analysis is disabled. Proceeding with caution.', threatLevel: 'medium' }, 'ai-disabled', { skipCache: true, skipAnalytics: true });
458
369
  }
459
370
 
460
371
  console.error('[Bouncer] 🤖 Invoking Haiku for AI analysis...');
461
- trackEvent(AnalyticsEvents.BOUNCER_HAIKU_REVIEW, {
462
- operation_length: operation.length,
463
- });
372
+ trackEvent(AnalyticsEvents.BOUNCER_HAIKU_REVIEW, { operation_length: operation.length });
464
373
 
465
- // Get Claude command and working directory from context or use defaults
466
374
  const claudeCommand = process.env.CLAUDE_COMMAND || 'claude';
467
375
  const workingDir = request.context?.workingDirectory || process.cwd();
468
376
 
469
377
  try {
470
378
  const decision = await analyzeWithHaiku(request, claudeCommand, workingDir);
471
- const latencyMs = Math.round(performance.now() - startTime);
472
- console.error(`[Bouncer] ✓ Haiku decision: ${decision.decision} (${decision.confidence}% confidence) [${latencyMs}ms]`);
379
+ console.error(`[Bouncer] Haiku decision: ${decision.decision} (${decision.confidence}% confidence) [${Math.round(performance.now() - startTime)}ms]`);
473
380
  console.error(`[Bouncer] Reasoning: ${decision.reasoning}`);
474
-
475
- logBouncerDecision(
476
- operation,
477
- decision.decision,
478
- decision.confidence,
479
- decision.reasoning,
480
- { context: request.context, threatLevel: decision.threatLevel, layer: 'haiku-ai', latencyMs }
481
- );
482
- trackEvent(decision.decision === 'deny' ? AnalyticsEvents.BOUNCER_TOOL_DENIED : AnalyticsEvents.BOUNCER_TOOL_ALLOWED, {
483
- layer: 'haiku-ai',
484
- operation_length: operation.length,
485
- threat_level: decision.threatLevel,
486
- confidence: decision.confidence,
487
- latency_ms: latencyMs,
488
- });
489
-
490
- cacheDecision(operation, decision);
491
- return decision;
492
-
381
+ return fin(decision, 'haiku-ai');
493
382
  } catch (error: unknown) {
494
- const latencyMs = Math.round(performance.now() - startTime);
495
383
  const errorMessage = error instanceof Error ? error.message : String(error);
496
- const isTimeout = errorMessage.includes('timed out');
497
384
 
498
- if (isTimeout) {
499
- // Timeout: default to ALLOW — prefer availability over security stall,
500
- // since the user drove the interaction
385
+ if (errorMessage.includes('timed out')) {
501
386
  console.error(`[Bouncer] ⚠️ Haiku analysis timed out after ${HAIKU_TIMEOUT_MS}ms — defaulting to ALLOW`);
502
387
  captureException(error, { context: 'bouncer.haiku_timeout', operation });
503
-
504
- const decision: BouncerDecision = {
505
- decision: 'allow',
506
- confidence: 50,
507
- reasoning: `Security analysis timed out after ${HAIKU_TIMEOUT_MS}ms. Defaulting to allow — user initiated the action.`,
508
- threatLevel: 'medium'
509
- };
510
-
511
- logBouncerDecision(
512
- operation,
513
- decision.decision,
514
- decision.confidence,
515
- decision.reasoning,
516
- { context: request.context, threatLevel: decision.threatLevel, layer: 'haiku-timeout', latencyMs, error: errorMessage }
517
- );
518
- trackEvent(AnalyticsEvents.BOUNCER_TOOL_ALLOWED, {
519
- layer: 'haiku-timeout',
520
- operation_length: operation.length,
521
- threat_level: 'medium',
522
- confidence: 50,
523
- latency_ms: latencyMs,
524
- });
525
-
526
- return decision;
388
+ return fin({ decision: 'allow', confidence: 50, reasoning: `Security analysis timed out after ${HAIKU_TIMEOUT_MS}ms. Defaulting to allow — user initiated the action.`, threatLevel: 'medium' }, 'haiku-timeout', { skipCache: true });
527
389
  }
528
390
 
529
391
  console.error(`[Bouncer] ⚠️ Haiku analysis failed: ${errorMessage}`);
530
392
  captureException(error, { context: 'bouncer.haiku_analysis', operation });
531
-
532
- // Fail-safe: deny on non-timeout AI failure
533
- const decision: BouncerDecision = {
534
- decision: 'deny',
535
- confidence: 0,
536
- reasoning: `Security analysis failed: ${errorMessage}. Denying for safety.`,
537
- threatLevel: 'critical'
538
- };
539
-
540
- logBouncerDecision(
541
- operation,
542
- decision.decision,
543
- decision.confidence,
544
- decision.reasoning,
545
- { context: request.context, threatLevel: decision.threatLevel, layer: 'ai-error', latencyMs, error: errorMessage }
546
- );
547
-
548
- return decision;
393
+ return fin({ decision: 'deny', confidence: 0, reasoning: `Security analysis failed: ${errorMessage}. Denying for safety.`, threatLevel: 'critical' }, 'ai-error', { skipCache: true, skipAnalytics: true, error: errorMessage });
549
394
  }
550
395
  }
551
396
 
@@ -7,7 +7,7 @@
7
7
  * File type detection, gitignore parsing, and directory scanning utilities.
8
8
  */
9
9
 
10
- import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
10
+ import { existsSync, readdirSync, readFileSync, type Stats, statSync } from 'node:fs';
11
11
  import { extname, join, relative, sep } from 'node:path';
12
12
  import type { CacheEntry, } from './types.js';
13
13
 
@@ -285,6 +285,31 @@ function readTextContent(fullPath: string, filePath: string, fileName: string, s
285
285
  };
286
286
  }
287
287
 
288
+ function validateFileAccess(fullPath: string, filePath: string, fileName: string, workingDir: string): FileContentResult | null {
289
+ const normalizedPath = join(fullPath);
290
+ if (!normalizedPath.startsWith(join(workingDir)) && !isPathInSafeLocation(normalizedPath)) {
291
+ return { path: filePath, fileName, content: '', error: 'Access denied: path outside allowed locations' };
292
+ }
293
+ if (!existsSync(fullPath)) {
294
+ return { path: filePath, fileName, content: '', error: 'File not found' };
295
+ }
296
+ return null;
297
+ }
298
+
299
+ function readValidatedFile(fullPath: string, filePath: string, fileName: string, stats: Stats): FileContentResult {
300
+ if (stats.isDirectory()) return readDirectoryContent(fullPath, filePath, fileName);
301
+
302
+ const isBin = isBinaryFile(fullPath);
303
+ const MAX_FILE_SIZE = isBin ? 10 * 1024 * 1024 : 1024 * 1024;
304
+ if (stats.size > MAX_FILE_SIZE) {
305
+ return { path: filePath, fileName, content: '', size: stats.size, error: `File too large (${Math.round(stats.size / 1024)}KB). Maximum is ${isBin ? '10MB' : '1MB'}.` };
306
+ }
307
+
308
+ return isBin
309
+ ? readBinaryContent(fullPath, filePath, fileName, stats)
310
+ : readTextContent(fullPath, filePath, fileName, stats);
311
+ }
312
+
288
313
  /**
289
314
  * Read file content for context injection
290
315
  */
@@ -293,30 +318,10 @@ export function readFileContent(filePath: string, workingDir: string): FileConte
293
318
  const fullPath = filePath.startsWith('/') ? filePath : join(workingDir, filePath);
294
319
  const fileName = fullPath.split(sep).pop() || filePath;
295
320
 
296
- const normalizedPath = join(fullPath);
297
- const isInWorkingDir = normalizedPath.startsWith(join(workingDir));
298
- if (!isInWorkingDir && !isPathInSafeLocation(normalizedPath)) {
299
- return { path: filePath, fileName, content: '', error: 'Access denied: path outside allowed locations' };
300
- }
301
-
302
- if (!existsSync(fullPath)) {
303
- return { path: filePath, fileName, content: '', error: 'File not found' };
304
- }
305
-
306
- const stats = statSync(fullPath);
307
- if (stats.isDirectory()) {
308
- return readDirectoryContent(fullPath, filePath, fileName);
309
- }
310
-
311
- const isBin = isBinaryFile(fullPath);
312
- const MAX_FILE_SIZE = isBin ? 10 * 1024 * 1024 : 1024 * 1024;
313
- if (stats.size > MAX_FILE_SIZE) {
314
- return { path: filePath, fileName, content: '', size: stats.size, error: `File too large (${Math.round(stats.size / 1024)}KB). Maximum is ${isBin ? '10MB' : '1MB'}.` };
315
- }
321
+ const accessError = validateFileAccess(fullPath, filePath, fileName, workingDir);
322
+ if (accessError) return accessError;
316
323
 
317
- return isBin
318
- ? readBinaryContent(fullPath, filePath, fileName, stats)
319
- : readTextContent(fullPath, filePath, fileName, stats);
324
+ return readValidatedFile(fullPath, filePath, fileName, statSync(fullPath));
320
325
  } catch (error: unknown) {
321
326
  console.error('[FileUtils] Error reading file:', error);
322
327
  return { path: filePath, fileName: filePath.split(sep).pop() || filePath, content: '', error: (error instanceof Error ? error.message : String(error)) || 'Failed to read file' };
@@ -29,17 +29,3 @@ export const MSTRO_ROOT = resolve(__dirname, '../..');
29
29
  */
30
30
  export const MCP_SERVER_PATH = resolve(MSTRO_ROOT, 'server/mcp/server.ts');
31
31
 
32
- /**
33
- * Path to the MCP bouncer configuration template
34
- */
35
- export const MCP_CONFIG_TEMPLATE_PATH = resolve(MSTRO_ROOT, 'mstro-bouncer-mcp.json');
36
-
37
- /**
38
- * Path to the hooks directory
39
- */
40
- export const HOOKS_DIR = resolve(MSTRO_ROOT, 'hooks');
41
-
42
- /**
43
- * Path to the bouncer hook script
44
- */
45
- export const BOUNCER_HOOK = resolve(HOOKS_DIR, 'bouncer.sh');