Skip to main content
Glama
UniversalSandbox.js35.4 kB
import { InputValidator } from './InputValidator.js'; import { JPLAssertions } from './JPLAssertions.js'; import { ErrorHandler } from './ErrorHandler.js'; import { RateLimiter } from './RateLimiter.js'; import { TrustedToolVerifier } from './TrustedToolVerifier.js'; import { createHash } from 'crypto'; // Constants for magic numbers const DEFAULT_MAX_REQUESTS = 50; const DEFAULT_WINDOW_MS = 60000; const DEFAULT_BURST_LIMIT = 5; const DEFAULT_MAX_COMMAND_LENGTH = 500; const DEFAULT_MAX_ARG_LENGTH = 300; const DEFAULT_MAX_TOTAL_ARGS = 20; const DEFAULT_MAX_FUNCTION_LINES = 60; const DEFAULT_MIN_ASSERTIONS_PER_FUNCTION = 2; const DEFAULT_MAX_RETRY_ATTEMPTS = 3; const MAX_AUDIT_LOG_SIZE = 10000; const CACHE_KEY_LENGTH = 16; const STACK_TRACE_LINES = 6; /** * Universal Sandbox - Security Policy Manager * Provides security policies and validation for command execution * Works with SecureCommandExecutor to enforce zero-trust architecture */ export class UniversalSandbox { constructor(options = {}) { // Initialize basic properties this.initializeBasicProperties(options); // Initialize security policies this.initializeSecurityPolicies(); // Initialize all security components this.initializeSecurityComponents(options); // Initialize if enabled if (this.options.enabled) { this.initialize(); } } /** * Initialize basic properties and data structures */ initializeBasicProperties(options) { this.options = { enabled: true, mode: 'block', // monitor | warn | block performanceOptimization: true, bypassTrustedTools: true, ...options, }; this.commandCache = new Map(); this.auditLog = []; this.securityMetrics = { totalCommands: 0, blockedCommands: 0, allowedCommands: 0, cachedCommands: 0, }; } /** * Initialize all security components */ initializeSecurityComponents(options) { const strictMode = this.options.mode === 'block'; // Initialize enhanced input validator this.inputValidator = new InputValidator({ strictMode: strictMode, maxCommandLength: DEFAULT_MAX_COMMAND_LENGTH, maxArgLength: DEFAULT_MAX_ARG_LENGTH, maxTotalArgs: DEFAULT_MAX_TOTAL_ARGS, }); // Initialize JPL runtime assertions for safety-critical compliance this.jplAssertions = new JPLAssertions({ enabled: true, strictMode: strictMode, throwOnFailure: strictMode, maxFunctionLines: DEFAULT_MAX_FUNCTION_LINES, minAssertionsPerFunction: DEFAULT_MIN_ASSERTIONS_PER_FUNCTION, }); // Initialize comprehensive error handling system this.errorHandler = new ErrorHandler({ enableRecovery: !strictMode, maxRetryAttempts: DEFAULT_MAX_RETRY_ATTEMPTS, enableCircuitBreaker: true, criticalErrorCallback: this.createCriticalErrorCallback(), sanitizeErrors: true, }); // Initialize rate limiter for DoS protection this.rateLimiter = new RateLimiter({ maxRequests: options.maxRequests || DEFAULT_MAX_REQUESTS, windowMs: options.windowMs || DEFAULT_WINDOW_MS, burstLimit: options.burstLimit || DEFAULT_BURST_LIMIT, enableDDoSProtection: options.enableDDoSProtection !== false, }); // Initialize trusted tool verifier with cryptographic verification this.trustedToolVerifier = new TrustedToolVerifier({ enableCryptographicVerification: options.enableCryptographicVerification !== false, enableChecksumVerification: options.enableChecksumVerification !== false, enableSignatureVerification: options.enableSignatureVerification || false, allowSelfSigned: options.allowSelfSigned !== false, }); } /** * Create critical error callback handler */ createCriticalErrorCallback() { return (errorInfo) => { // JPL Rule 5: Minimum 2 assertions per function if (!errorInfo) { throw new TypeError('Error info must be provided'); } if (typeof errorInfo !== 'object') { throw new TypeError('Error info must be an object'); } return this.handleCriticalSecurityError(errorInfo); }; } /** * Initialize security policies and command categorization */ initializeSecurityPolicies() { // Initialize command categories this.securityPolicies = this.initializeCommandCategories(); // Initialize dangerous patterns this.dangerousPatterns = this.initializeDangerousPatterns(); } /** * Initialize command security categories */ initializeCommandCategories() { return { // Commands that are always safe (no validation needed) ALWAYS_ALLOW: new Set(['echo', 'printf', 'true', 'false']), // Commands that need validation but are generally safe VALIDATE_REQUIRED: new Set([ 'ls', 'find', 'grep', 'cat', 'head', 'tail', 'wc', 'sort', 'uniq', 'cut', 'diff', ]), // Commands that require user consent REQUIRE_CONSENT: new Set([ 'npm', 'yarn', 'bun', 'git', 'docker', 'make', 'cmake', 'tsc', 'webpack', 'rm', // Moved from NEVER_ALLOW - destructive but may be needed 'curl', // Moved from NEVER_ALLOW - network access but may be needed 'wget', // Moved from NEVER_ALLOW - network access but may be needed ]), // Commands that are never allowed NEVER_ALLOW: new Set([ 'sudo', 'su', 'chmod', 'chown', 'ssh', 'scp', 'nc', 'telnet', 'dd', 'fdisk', 'mkfs', 'mount', 'umount', ]), // Trusted MCP tools that can bypass sandbox temporarily TRUSTED_TOOLS: new Set([ 'check_code_practices', 'format_code', 'check_safety_rules', 'scan_security_vulnerabilities', 'check_production_readiness', 'check_accessibility', 'check_graphql_schema', 'check_graphql_query', 'check_redux_patterns', ]), }; } /** * Initialize dangerous command patterns */ initializeDangerousPatterns() { return [ /rm\s+-rf\s*\//, // rm -rf / /sudo\s+/, // sudo usage /curl\s+.*\|\s*sh/, // curl | sh /wget\s+.*\|\s*sh/, // wget | sh /eval\s*\(/, // eval execution /exec\s*\(/, // exec execution /system\s*\(/, // system calls /`[^`]*`/, // backtick execution /\$\([^)]*\)/, // command substitution /;\s*(rm|del|format)/, // chained dangerous commands /&&\s*(rm|del|format)/, // conditional dangerous commands /\|\s*(rm|del|format)/, // piped dangerous commands ]; } /** * Initialize the universal sandbox security policy manager */ initialize() { this.logSecurityEvent('INIT', 'system', [], 'Universal Sandbox Policy Manager initialization started'); // Verify all security components are initialized this.verifySecurityComponents(); this.logSecurityEvent( 'INIT', 'system', [], `Universal Sandbox Policy Manager initialized in ${this.options.mode} mode`, ); } /** * Verify all security components are properly initialized */ verifySecurityComponents() { const components = [ { name: 'inputValidator', component: this.inputValidator }, { name: 'jplAssertions', component: this.jplAssertions }, { name: 'errorHandler', component: this.errorHandler }, { name: 'rateLimiter', component: this.rateLimiter }, { name: 'trustedToolVerifier', component: this.trustedToolVerifier }, ]; for (const { name, component } of components) { if (!component) { throw new Error(`Security component ${name} not initialized`); } } } /** * Check security policy for a command * @param {string} command - The command to check * @param {Array} args - Command arguments * @returns {Object} Policy decision with action and metadata */ checkCommandPolicy(command, args = []) { // Validate inputs if (!command || typeof command !== 'string') { return { action: 'BLOCK', reason: 'Invalid command', metadata: { command, args } }; } // Check for dangerous patterns first const fullCommand = `${command} ${args.join(' ')}`; for (const pattern of this.dangerousPatterns) { if (pattern.test(fullCommand)) { return { action: 'BLOCK', reason: 'Dangerous pattern detected', pattern: pattern.toString(), metadata: { command, args, fullCommand } }; } } // Get security action const action = this.determineSecurityAction(command, args); return { action, reason: this.getPolicyReason(action, command), metadata: { command, args, category: this.getCommandCategory(command) } }; } /** * Get the security policy for a specific command * @param {string} command - The command to get policy for * @returns {string} The policy category */ getPolicyForCommand(command) { if (this.securityPolicies.NEVER_ALLOW.has(command)) { return 'NEVER_ALLOW'; } if (this.securityPolicies.ALWAYS_ALLOW.has(command)) { return 'ALWAYS_ALLOW'; } if (this.securityPolicies.VALIDATE_REQUIRED.has(command)) { return 'VALIDATE_REQUIRED'; } if (this.securityPolicies.REQUIRE_CONSENT.has(command)) { return 'REQUIRE_CONSENT'; } if (this.securityPolicies.TRUSTED_TOOLS.has(command)) { return 'TRUSTED_TOOLS'; } return 'UNKNOWN'; } /** * Get command category for metadata */ getCommandCategory(command) { const category = this.getPolicyForCommand(command); return category !== 'UNKNOWN' ? category : 'UNCATEGORIZED'; } /** * Get human-readable reason for policy decision */ getPolicyReason(action, command) { const reasons = { 'ALLOW': `Command '${command}' is in the always-allow list`, 'VALIDATE': `Command '${command}' requires validation`, 'CONSENT': `Command '${command}' requires user consent`, 'BLOCK': `Command '${command}' is blocked by security policy` }; return reasons[action] || `Unknown security action for '${command}'`; } /** * Validate command execution request against security policies * Used by SecureCommandExecutor to enforce policies * @returns {Object} Validation result with success flag and metadata */ async validateCommandExecution(command, args = [], options = {}) { // JPL Rule 5: Function entry with assertions this.jplAssertions.enterFunction('validateCommandExecution'); // JPL Rule 5: Assertion 1 - Validate inputs this.jplAssertions.assert(typeof command === 'string', 'Command must be a string', { command, }); this.jplAssertions.assert(Array.isArray(args), 'Args must be an array', { args }); // Rate limiting check const clientId = options.clientId || 'default'; const rateLimitResult = await this.rateLimiter.isAllowed(clientId, command, { args, isError: false, }); if (!rateLimitResult.allowed) { this.securityMetrics.blockedCommands++; this.securityMetrics.rateLimitViolations = (this.securityMetrics.rateLimitViolations || 0) + 1; this.logSecurityEvent( 'RATE_LIMIT_EXCEEDED', command, args, `Rate limit exceeded: ${rateLimitResult.reason}` ); this.jplAssertions.exitFunction('validateCommandExecution'); return { success: false, action: 'BLOCK', reason: `Rate limit exceeded: ${rateLimitResult.reason}`, metadata: { command, args, clientId, rateLimitInfo: rateLimitResult, timestamp: new Date().toISOString() } }; } this.securityMetrics.totalCommands++; try { // Enhanced input validation const validationResult = this.inputValidator.validateCommandExecution( command, args, options, ); // JPL Rule 5: Assertion 2 - Validation must succeed for safety this.jplAssertions.assert(validationResult !== null, 'Validation result cannot be null'); if (!validationResult.isValid) { this.logSecurityEvent( 'VALIDATION_FAILED', command, args, `Input validation failed: ${validationResult.errors.join(', ')}`, ); // JPL Rule 5: Function exit with compliance check this.jplAssertions.exitFunction('validateCommandExecution'); return { success: false, action: 'BLOCK', reason: `Input validation failed: ${validationResult.errors.join(', ')}`, metadata: { command, args, validationErrors: validationResult.errors, timestamp: new Date().toISOString() } }; } // Log validation warnings if any if (validationResult.warnings.length > 0) { this.logSecurityEvent( 'VALIDATION_WARNING', command, args, `Validation warnings: ${validationResult.warnings.join(', ')}`, ); } // Use sanitized inputs for further processing const sanitizedCommand = validationResult.sanitized.command; const sanitizedArgs = validationResult.sanitized.args; const sanitizedOptions = validationResult.sanitized.options; // Get call source for audit trail const source = this.getCallSource(); // Check if this is from a trusted tool and bypass is enabled if (this.options.bypassTrustedTools && this.isTrustedToolCall(source)) { return this.executeTrustedCommand( method, sanitizedCommand, sanitizedArgs, sanitizedOptions, callback, ); } // Determine security action using sanitized inputs const securityAction = this.determineSecurityAction(sanitizedCommand, sanitizedArgs); // Log the attempt with validation metadata this.logCommandAttempt( method, sanitizedCommand, sanitizedArgs, source, securityAction, validationResult.metadata, ); // Handle based on security action let result; switch (securityAction) { case 'ALLOW': result = await this.validateAllowedCommand( sanitizedCommand, sanitizedArgs, sanitizedOptions, ); break; case 'VALIDATE': result = await this.validateCommandWithChecks( sanitizedCommand, sanitizedArgs, sanitizedOptions, ); break; case 'CONSENT': result = this.validateConsentCommand( sanitizedCommand, sanitizedArgs, sanitizedOptions, ); break; case 'BLOCK': result = this.validateBlockedCommand( sanitizedCommand, sanitizedArgs, 'Command blocked by security policy', ); break; default: result = this.validateBlockedCommand( sanitizedCommand, sanitizedArgs, 'Unknown security action', ); } // JPL Rule 7: Check return value this.jplAssertions.checkReturnValue(result, 'object', 'validation result'); this.jplAssertions.exitFunction('validateCommandExecution'); return result; } catch (error) { this.logSecurityEvent('ERROR', command, args, error.message); try { // Use comprehensive error handling const errorResult = await this.errorHandler.handleError(error, { operation: 'validateCommandExecution', command: command, args: args, function: 'validateCommandExecution', }); // If error handler returns retry signal if (errorResult && errorResult.shouldRetry) { this.logSecurityEvent( 'RETRY_ATTEMPT', command, args, `Retrying command validation: attempt ${errorResult.retryAttempt}`, ); // JPL Rule 5: Function exit before retry this.jplAssertions.exitFunction('validateCommandExecution'); // Recursive retry with same parameters return this.validateCommandExecution(command, args, options); } // If error handler returns structured error response if (errorResult && !errorResult.success) { this.jplAssertions.exitFunction('validateCommandExecution'); return { success: false, action: 'ERROR', reason: errorResult.error.message, error: errorResult.error, metadata: { command, args, timestamp: new Date().toISOString() } }; } } catch (handlerError) { // Error handler itself failed or threw this.logSecurityEvent('ERROR_HANDLER_FAILED', command, args, handlerError.message); } // JPL Rule 5: Function exit on error path this.jplAssertions.exitFunction('validateCommandExecution'); return { success: false, action: 'ERROR', reason: `Validation error: ${error.message}`, error: error, metadata: { command, args, timestamp: new Date().toISOString() } }; } } /** * Synchronous command validation */ validateCommandSync(command, args = [], options = {}) { this.securityMetrics.totalCommands++; const source = this.getCallSource(); const securityAction = this.determineSecurityAction(command, args); this.logCommandAttempt('sync', command, args, source, securityAction); switch (securityAction) { case 'ALLOW': return this.validateAllowedCommand(command, args, options); case 'VALIDATE': // For sync operations, we'll use a simplified validation if (this.isCommandSafe(command, args)) { this.securityMetrics.allowedCommands++; return { success: true, action: 'VALIDATE', metadata: { command, args, sync: true, timestamp: new Date().toISOString() } }; } else { this.securityMetrics.blockedCommands++; return { success: false, action: 'BLOCK', reason: `Command failed safety checks: ${command}`, metadata: { command, args, sync: true, timestamp: new Date().toISOString() } }; } case 'CONSENT': return this.validateConsentCommand(command, args, options); case 'BLOCK': default: return this.validateBlockedCommand(command, args, 'Command blocked by security policy'); } } /** * Determine what security action to take for a command */ determineSecurityAction(command, args = []) { // JPL Rule 5: Function entry with assertions this.jplAssertions.enterFunction('determineSecurityAction'); this.jplAssertions.assert(typeof command === 'string', 'Command must be a string', { command, }); this.jplAssertions.assert(Array.isArray(args), 'Args must be an array', { args }); // Check for dangerous patterns first const fullCommand = `${command} ${args.join(' ')}`; // JPL Rule 2: Loop with fixed bounds const loopId = 'dangerousPatternCheck'; this.jplAssertions.enterLoop(loopId, this.dangerousPatterns.length); for (const pattern of this.dangerousPatterns) { this.jplAssertions.checkLoopBound(loopId); if (pattern.test(fullCommand)) { this.jplAssertions.exitLoop(loopId); this.jplAssertions.exitFunction('determineSecurityAction'); return 'BLOCK'; } } this.jplAssertions.exitLoop(loopId); // Check against policy categories if (this.securityPolicies.NEVER_ALLOW.has(command)) { this.jplAssertions.exitFunction('determineSecurityAction'); return 'BLOCK'; } if (this.securityPolicies.ALWAYS_ALLOW.has(command)) { this.jplAssertions.exitFunction('determineSecurityAction'); return 'ALLOW'; } if (this.securityPolicies.VALIDATE_REQUIRED.has(command)) { this.jplAssertions.exitFunction('determineSecurityAction'); return 'VALIDATE'; } if (this.securityPolicies.REQUIRE_CONSENT.has(command)) { this.jplAssertions.exitFunction('determineSecurityAction'); return 'CONSENT'; } // Default to validation for unknown commands // JPL Rule 7: Check return value is valid const result = 'VALIDATE'; this.jplAssertions.assert(typeof result === 'string', 'Security action must be string', { result, }); this.jplAssertions.exitFunction('determineSecurityAction'); return result; } /** * Validate allowed command */ validateAllowedCommand(command, args, options) { this.securityMetrics.allowedCommands++; // Check cache for performance const cacheKey = this.getCacheKey(command, args); if (this.commandCache.has(cacheKey) && this.options.performanceOptimization) { this.securityMetrics.cachedCommands++; return this.commandCache.get(cacheKey); } const result = { success: true, action: 'ALLOW', metadata: { command, args, cached: false, timestamp: new Date().toISOString() } }; // Cache the result if (this.options.performanceOptimization) { this.commandCache.set(cacheKey, result); } return result; } /** * Validate command that requires additional checks */ async validateCommandWithChecks(command, args, options) { try { // Verify tool trust with cryptographic verification const verificationResult = await this.trustedToolVerifier.verifyTool(command, { args, checkPermissions: true, }); if (!verificationResult.trusted) { this.securityMetrics.blockedCommands++; this.securityMetrics.untrustedToolBlocks = (this.securityMetrics.untrustedToolBlocks || 0) + 1; this.logSecurityEvent( 'UNTRUSTED_TOOL', command, args, `Tool verification failed: ${verificationResult.message}`, ); return { success: false, action: 'BLOCK', reason: `Untrusted tool blocked: ${verificationResult.message}`, metadata: { command, args, verificationResult, timestamp: new Date().toISOString() } }; } // Log successful verification this.logSecurityEvent( 'TOOL_VERIFIED', command, args, `Tool verified: ${verificationResult.message}`, ); // Additional validation checks if (!this.isCommandSafe(command, args)) { this.securityMetrics.blockedCommands++; return { success: false, action: 'BLOCK', reason: 'Command failed safety checks', metadata: { command, args, timestamp: new Date().toISOString() } }; } this.securityMetrics.allowedCommands++; this.securityMetrics.verifiedToolExecutions = (this.securityMetrics.verifiedToolExecutions || 0) + 1; return { success: true, action: 'VALIDATE', metadata: { command, args, verified: true, verificationResult, timestamp: new Date().toISOString() } }; } catch (error) { this.securityMetrics.blockedCommands++; // Use error handler for validation failures try { const errorResult = await this.errorHandler.handleError(error, { operation: 'validateCommandWithChecks', command: command, args: args, }); // If it's a recoverable error and we get retry signal if (errorResult && errorResult.shouldRetry) { this.logSecurityEvent( 'VALIDATION_RETRY', command, args, `Retrying validation: attempt ${errorResult.retryAttempt}`, ); return this.validateCommandWithChecks(command, args, options); } } catch (handlerError) { // Error handler threw, continue with original error this.logSecurityEvent('ERROR_HANDLER_VALIDATION_FAILED', command, args, handlerError.message); } return { success: false, action: 'BLOCK', reason: `Validation failed: ${error.message}`, error: error, metadata: { command, args, timestamp: new Date().toISOString() } }; } } /** * Validate command requiring user consent */ validateConsentCommand(command, args, options) { if (this.options.mode === 'monitor') { // In monitor mode, log but allow this.logSecurityEvent('CONSENT_BYPASSED', command, args, 'Monitor mode - consent bypassed'); return { success: true, action: 'CONSENT', metadata: { command, args, consentBypassed: true, mode: 'monitor', timestamp: new Date().toISOString() } }; } // In block/warn mode, require actual consent return { success: false, action: 'CONSENT', reason: `Command requires user consent: ${command} ${args.join(' ')}`, metadata: { command, args, requiresConsent: true, timestamp: new Date().toISOString() } }; } /** * Validate blocked command */ validateBlockedCommand(command, args, reason) { this.securityMetrics.blockedCommands++; this.logSecurityEvent('BLOCKED', command, args, reason); if (this.options.mode === 'monitor') { this.logSecurityEvent('MONITOR_BLOCK', command, args, 'Would have blocked in enforce mode'); // In monitor mode, log but return success with warning return { success: true, action: 'BLOCK', warning: 'Would have been blocked in enforce mode', metadata: { command, args, reason, mode: 'monitor', timestamp: new Date().toISOString() } }; } return { success: false, action: 'BLOCK', reason: `${reason} - Command: ${command} ${args.join(' ')}`, metadata: { command, args, timestamp: new Date().toISOString() } }; } /** * Validate trusted tool command */ validateTrustedCommand(command, args, options) { this.logSecurityEvent('TRUSTED_BYPASS', command, args, 'Trusted tool bypass'); return { success: true, action: 'ALLOW', trusted: true, metadata: { command, args, trustedTool: true, timestamp: new Date().toISOString() } }; } /** * Check if command is from a trusted tool */ isTrustedToolCall(source) { // Check if the call stack includes trusted tools return ( this.securityPolicies.TRUSTED_TOOLS.has(source.tool) || source.stack.some((frame) => { // JPL Rule 5: Minimum 2 assertions per function if (!frame) { throw new TypeError('Frame must be provided'); } if (typeof frame !== 'object') { throw new TypeError('Frame must be an object'); } return this.securityPolicies.TRUSTED_TOOLS.has(frame.function); }) ); } /** * Get call source information for audit trail */ getCallSource() { const stack = new Error().stack.split('\n'); return { timestamp: new Date().toISOString(), tool: this.extractToolName(stack), stack: stack.slice(1, STACK_TRACE_LINES).map((line) => { // JPL Rule 5: Minimum 2 assertions per function if (!line) { throw new TypeError('Stack line must be provided'); } if (typeof line !== 'string') { throw new TypeError('Stack line must be a string'); } return { function: line.match(/at\s+([^\s]+)/)?.[1] || 'unknown', location: line.match(/\(([^)]+)\)/)?.[1] || 'unknown', }; }), }; } /** * Extract tool name from call stack */ extractToolName(stack) { for (const line of stack) { // Look for MCP tool patterns const toolMatch = line.match(/at\s+handle([A-Z][a-zA-Z]+)/); if (toolMatch) { return toolMatch[1].toLowerCase(); } // Look for file-based tool patterns const fileMatch = line.match(/([a-z-]+)\.js/); if (fileMatch && this.securityPolicies.TRUSTED_TOOLS.has(fileMatch[1])) { return fileMatch[1]; } } return 'unknown'; } /** * Simple safety check for commands */ isCommandSafe(command, args) { // Basic safety checks const fullCommand = `${command} ${args.join(' ')}`; // Check for dangerous patterns for (const pattern of this.dangerousPatterns) { if (pattern.test(fullCommand)) { return false; } } // Check for path traversal attempts if (args.some((arg) => arg.includes('../') || arg.includes('/etc') || arg.includes('/root'))) { return false; } return true; } /** * Create cache key for command */ getCacheKey(command, args) { return createHash('sha256') .update(`${command}:${args.join(':')}`) .digest('hex') .slice(0, CACHE_KEY_LENGTH); } /** * Create mock process object for SecureCommandExecutor results */ createMockProcess(result) { const mockProcess = { stdout: { on: (event, callback) => { // JPL Rule 5: Minimum 2 assertions per function if (!event) { throw new TypeError('Event must be provided'); } if (typeof callback !== 'function') { throw new TypeError('Callback must be a function'); } if (event === 'data') { setTimeout(() => callback(result.output), 0); } }, }, stderr: { on: (event, callback) => { // JPL Rule 5: Minimum 2 assertions per function if (!event) { throw new TypeError('Event must be provided'); } if (typeof callback !== 'function') { throw new TypeError('Callback must be a function'); } // No stderr for successful results }, }, on: (event, callback) => { // JPL Rule 5: Minimum 2 assertions per function if (!event) { throw new TypeError('Event must be provided'); } if (typeof callback !== 'function') { throw new TypeError('Callback must be a function'); } if (event === 'close') { setTimeout(() => callback(0), 0); } }, }; return mockProcess; } /** * Log command attempt for audit trail */ logCommandAttempt(method, command, args, source, action, validationMetadata = null) { const logEntry = { timestamp: source.timestamp, method, command, args: args.slice(), // Copy array source, action, mode: this.options.mode, validation: validationMetadata || { validated: false }, }; this.auditLog.push(logEntry); // Keep audit log size manageable if (this.auditLog.length > MAX_AUDIT_LOG_SIZE) { this.auditLog.shift(); } } /** * Log security events */ logSecurityEvent(eventType, command, args, details) { const event = { timestamp: new Date().toISOString(), type: eventType, command, args: args.slice(), details, mode: this.options.mode, }; // Add to audit log for later retrieval this.auditLog.push({ ...event, severity: this.getEventSeverity(eventType), }); // In production, this would integrate with external security monitoring // For development, events can be retrieved via getAuditLog() } /** * Get event severity level for security events */ getEventSeverity(eventType) { const severityMap = { BLOCKED: 'high', ERROR: 'high', VALIDATION_FAILED: 'high', VALIDATION_WARNING: 'low', CONSENT_BYPASSED: 'medium', TRUSTED_BYPASS: 'low', MONITOR_BLOCK: 'medium', INIT: 'info', SHUTDOWN: 'info', INTERCEPT: 'info', METRICS: 'info', EXPORT: 'info', EMERGENCY_DISABLE: 'critical', TOOL_START: 'info', TOOL_SUCCESS: 'info', TOOL_ERROR: 'medium', UNCAUGHT_EXCEPTION: 'critical', UNHANDLED_REJECTION: 'high', }; return severityMap[eventType] || 'medium'; } /** * Get security metrics */ getSecurityMetrics() { return { ...this.securityMetrics, auditLogSize: this.auditLog.length, cacheSize: this.commandCache.size, mode: this.options.mode, validation: this.inputValidator.getValidationMetrics(), jplCompliance: this.jplAssertions.getComplianceMetrics(), errorHandling: this.errorHandler.getErrorMetrics(), rateLimiting: this.rateLimiter.getStats(), toolVerification: this.trustedToolVerifier.getVerificationMetrics(), }; } /** * Get audit log */ getAuditLog(limit = 100) { return this.auditLog.slice(-limit); } /** * Reset audit log */ clearAuditLog() { this.auditLog = []; } /** * Disable universal sandbox policy manager */ disable() { if (!this.options.enabled) { return; } this.options.enabled = false; this.logSecurityEvent('SHUTDOWN', 'system', [], 'Universal Sandbox Policy Manager disabled'); } /** * Enable universal sandbox policy manager */ enable() { if (this.options.enabled) { return; } this.options.enabled = true; this.initialize(); } /** * Get JPL compliance report */ getJPLComplianceReport() { return this.jplAssertions.generateComplianceReport(); } /** * Handle critical security errors */ handleCriticalSecurityError(errorInfo) { // Log critical security event this.logSecurityEvent( 'CRITICAL_SECURITY_ERROR', errorInfo.context?.command || 'unknown', errorInfo.context?.args || [], `Critical security error: ${errorInfo.message}`, ); // In a production system, this could: // - Trigger incident response // - Send alerts to security team // - Initiate safe shutdown procedures // - Lock down the system // For now, we ensure it's logged and tracked this.securityMetrics.criticalErrors = (this.securityMetrics.criticalErrors || 0) + 1; return { handled: true, timestamp: new Date().toISOString(), action: 'logged_and_tracked', }; } } export default UniversalSandbox;

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/moikas-code/moidvk'

If you have feedback or need assistance with the MCP directory API, please join our Discord server