UNPKG

anon-identity

Version:

Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure

501 lines 18.7 kB
"use strict"; /** * Function Executor for MCP * * Secure function execution framework with validation, sandboxing, and monitoring */ Object.defineProperty(exports, "__esModule", { value: true }); exports.FunctionExecutor = void 0; const events_1 = require("events"); const types_1 = require("../types"); /** * Function Executor */ class FunctionExecutor extends events_1.EventEmitter { constructor(functionRegistry, authManager, auditLogger, defaultEnvironment = { sandboxed: true, timeoutMs: 30000, memoryLimitMB: 100, networkAccess: false, fileSystemAccess: false, allowedModules: ['crypto', 'util', 'querystring'], environmentVariables: {} }) { super(); this.functionRegistry = functionRegistry; this.authManager = authManager; this.auditLogger = auditLogger; this.defaultEnvironment = defaultEnvironment; this.executionQueue = []; this.activeExecutions = new Map(); this.resultValidators = new Map(); this.securityPolicies = new Map(); this.isProcessing = false; this.startQueueProcessor(); } /** * Execute function with full security and validation */ async executeFunction(functionCall, context, options = { priority: 'normal', retryOnFailure: true, maxRetries: 3, validateResult: true, auditExecution: true }) { // Pre-execution security checks await this.performSecurityChecks(functionCall, context); // For critical priority, execute immediately if (options.priority === 'critical') { return this.executeImmediate(functionCall, context, options); } // Otherwise, queue for execution return this.queueExecution(functionCall, context, options); } /** * Register result validator for function */ registerResultValidator(functionName, schema) { this.resultValidators.set(functionName, schema); } /** * Set security policy for function */ setSecurityPolicy(functionName, policy) { this.securityPolicies.set(functionName, policy); } /** * Get execution statistics */ getExecutionStatistics() { // This would be tracked in a real implementation return { queueSize: this.executionQueue.length, activeExecutions: this.activeExecutions.size, totalExecuted: 0, averageExecutionTime: 0, errorRate: 0, securityViolations: 0 }; } /** * Perform security checks before execution */ async performSecurityChecks(functionCall, context) { // Check if function exists and is enabled const definition = this.functionRegistry.getFunctionDefinition(functionCall.name); if (!definition) { throw new types_1.MCPError({ code: types_1.MCPErrorCode.FUNCTION_NOT_FOUND, message: `Function ${functionCall.name} not found`, timestamp: new Date(), retryable: false }); } // Get security policy const security = this.securityPolicies.get(functionCall.name) || { requiredScopes: [], riskLevel: types_1.FunctionRiskLevel.LOW, auditRequired: true, approvalRequired: false }; // Check required scopes if (security.requiredScopes.length > 0) { const authResult = await this.authManager.authorize(context.agentDID, `function:${functionCall.name}`, 'execute'); if (!authResult.authorized) { throw new types_1.MCPError({ code: types_1.MCPErrorCode.FORBIDDEN, message: `Insufficient permissions for function ${functionCall.name}`, timestamp: new Date(), retryable: false }); } } // Check approval requirement for high-risk functions if (security.approvalRequired && security.riskLevel === types_1.FunctionRiskLevel.HIGH) { // In a real implementation, this would check for approval this.emit('approval_required', { functionCall, context, security }); } // Audit function call if required if (security.auditRequired) { await this.auditLogger.logRequest({ id: context.requestId, type: 'function_call', prompt: `Function call: ${functionCall.name}`, agentDID: context.agentDID, sessionId: context.sessionId, metadata: { agentDID: context.agentDID, sessionId: context.sessionId, requestId: context.requestId, timestamp: new Date(), source: 'function-executor', priority: 'medium', functionName: functionCall.name } }, context.agentDID, context.sessionId); } } /** * Execute function immediately */ async executeImmediate(functionCall, context, options) { const executionId = `exec-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; try { // Create execution environment const environment = this.createExecutionEnvironment(functionCall.name, options.environment); // Start execution with monitoring const result = await this.executeWithMonitoring(executionId, functionCall, context, environment, options); // Validate result if required if (options.validateResult) { await this.validateExecutionResult(functionCall.name, result); } return result; } catch (error) { // Handle retry logic if (options.retryOnFailure && options.maxRetries > 0) { const retryOptions = { ...options, maxRetries: options.maxRetries - 1 }; return this.executeImmediate(functionCall, context, retryOptions); } throw error; } } /** * Queue execution for later processing */ async queueExecution(functionCall, context, options) { return new Promise((resolve, reject) => { const queueItem = { id: `queue-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, functionCall, context, options, resolve, reject, queuedAt: new Date(), priority: this.getPriorityValue(options.priority) }; // Insert in priority order const insertIndex = this.executionQueue.findIndex(item => item.priority < queueItem.priority); if (insertIndex === -1) { this.executionQueue.push(queueItem); } else { this.executionQueue.splice(insertIndex, 0, queueItem); } this.emit('function_queued', { functionName: functionCall.name, queuePosition: insertIndex === -1 ? this.executionQueue.length : insertIndex + 1, queueSize: this.executionQueue.length }); }); } /** * Execute function with comprehensive monitoring */ async executeWithMonitoring(executionId, functionCall, context, environment, options) { const startTime = Date.now(); const metrics = { functionName: functionCall.name, executionTime: 0, memoryUsed: 0, cpuTime: 0, networkCalls: 0, errors: [], warnings: [], securityViolations: [] }; try { // Set up timeout const timeout = setTimeout(() => { this.activeExecutions.delete(executionId); throw new types_1.MCPError({ code: types_1.MCPErrorCode.FUNCTION_TIMEOUT, message: `Function ${functionCall.name} timed out after ${environment.timeoutMs}ms`, timestamp: new Date(), retryable: true }); }, environment.timeoutMs); this.activeExecutions.set(executionId, timeout); // Create sandboxed execution context if required let executionResult; if (environment.sandboxed) { executionResult = await this.executeSandboxed(functionCall, context, environment, metrics); } else { executionResult = await this.functionRegistry.executeFunction(functionCall, context); } // Clear timeout clearTimeout(timeout); this.activeExecutions.delete(executionId); // Update metrics metrics.executionTime = Date.now() - startTime; // Emit monitoring events this.emit('function_executed', { executionId, functionCall, result: executionResult, metrics }); return executionResult; } catch (error) { // Clear timeout const timeout = this.activeExecutions.get(executionId); if (timeout) { clearTimeout(timeout); this.activeExecutions.delete(executionId); } metrics.executionTime = Date.now() - startTime; metrics.errors.push(error.message); this.emit('function_error', { executionId, functionCall, error, metrics }); throw error; } } /** * Execute function in sandbox */ async executeSandboxed(functionCall, context, environment, metrics) { // In a real implementation, this would use actual sandboxing // For now, we'll simulate sandbox constraints and monitoring const violations = []; // Monitor memory usage (simulated) const initialMemory = process.memoryUsage(); try { // Execute with constraints const result = await this.functionRegistry.executeFunction(functionCall, context); // Check memory usage const finalMemory = process.memoryUsage(); const memoryUsed = (finalMemory.heapUsed - initialMemory.heapUsed) / 1024 / 1024; // MB if (memoryUsed > environment.memoryLimitMB) { violations.push({ type: 'memory', message: `Function exceeded memory limit: ${memoryUsed}MB > ${environment.memoryLimitMB}MB`, timestamp: new Date(), severity: 'high' }); } metrics.memoryUsed = memoryUsed; // Report violations if (violations.length > 0) { violations.forEach(violation => { metrics.securityViolations.push(violation.message); }); this.emit('sandbox_violation', { functionName: functionCall.name, violations }); } return result; } catch (error) { // Handle sandbox-specific errors if (error.message.includes('MODULE_NOT_FOUND')) { violations.push({ type: 'module', message: 'Attempted to access unauthorized module', timestamp: new Date(), severity: 'critical' }); } throw error; } } /** * Validate execution result */ async validateExecutionResult(functionName, result) { const schema = this.resultValidators.get(functionName); if (!schema) { return; // No validation schema defined } const validation = this.validateResultAgainstSchema(result.result, schema); if (!validation.valid) { throw new types_1.MCPError({ code: types_1.MCPErrorCode.FUNCTION_ERROR, message: `Function result validation failed: ${validation.errors.join(', ')}`, timestamp: new Date(), retryable: false }); } // Add validation warnings to result metadata if (validation.warnings.length > 0) { result.metadata.warnings = (result.metadata.warnings || []).concat(validation.warnings); } } /** * Validate result against schema */ validateResultAgainstSchema(result, schema) { const errors = []; const warnings = []; // Type validation if (schema.type && typeof result !== schema.type) { if (schema.type === 'array' && !Array.isArray(result)) { errors.push(`Expected array, got ${typeof result}`); } else if (schema.type === 'object' && (typeof result !== 'object' || result === null || Array.isArray(result))) { errors.push(`Expected object, got ${typeof result}`); } } // String validation if (schema.type === 'string' && typeof result === 'string') { if (schema.maxLength && result.length > schema.maxLength) { errors.push(`String length ${result.length} exceeds maximum ${schema.maxLength}`); } if (schema.pattern && !new RegExp(schema.pattern).test(result)) { errors.push(`String does not match pattern ${schema.pattern}`); } } // Array validation if (schema.type === 'array' && Array.isArray(result)) { if (schema.maxItems && result.length > schema.maxItems) { errors.push(`Array length ${result.length} exceeds maximum ${schema.maxItems}`); } } // Object validation if (schema.type === 'object' && typeof result === 'object' && result !== null) { if (schema.required) { for (const requiredProp of schema.required) { if (!(requiredProp in result)) { errors.push(`Missing required property: ${requiredProp}`); } } } if (schema.properties) { for (const [prop, value] of Object.entries(result)) { if (!schema.properties[prop]) { warnings.push(`Unexpected property: ${prop}`); } } } } return { valid: errors.length === 0, errors, warnings }; } /** * Create execution environment */ createExecutionEnvironment(functionName, overrides) { // Get function-specific security settings const security = this.securityPolicies.get(functionName); let environment = { ...this.defaultEnvironment }; // Apply security-based environment settings if (security) { switch (security.riskLevel) { case types_1.FunctionRiskLevel.HIGH: case types_1.FunctionRiskLevel.CRITICAL: environment.networkAccess = false; environment.fileSystemAccess = false; environment.timeoutMs = Math.min(environment.timeoutMs, 10000); // 10 seconds max environment.memoryLimitMB = Math.min(environment.memoryLimitMB, 50); // 50MB max break; case types_1.FunctionRiskLevel.MEDIUM: environment.networkAccess = false; environment.timeoutMs = Math.min(environment.timeoutMs, 20000); // 20 seconds max break; case types_1.FunctionRiskLevel.LOW: // Use defaults break; } } // Apply overrides if (overrides) { environment = { ...environment, ...overrides }; } return environment; } /** * Get priority value for sorting */ getPriorityValue(priority) { switch (priority) { case 'critical': return 4; case 'high': return 3; case 'normal': return 2; case 'low': return 1; default: return 2; } } /** * Start queue processor */ startQueueProcessor() { this.processInterval = setInterval(() => { this.processQueue(); }, 100); // Process every 100ms } /** * Process execution queue */ async processQueue() { if (this.isProcessing || this.executionQueue.length === 0) { return; } // Limit concurrent executions const maxConcurrent = 10; if (this.activeExecutions.size >= maxConcurrent) { return; } this.isProcessing = true; try { const item = this.executionQueue.shift(); if (item) { // Execute the queued function this.executeImmediate(item.functionCall, item.context, item.options) .then(result => item.resolve(result)) .catch(error => item.reject(error)); } } finally { this.isProcessing = false; } } /** * Clear execution queue */ clearQueue() { for (const item of this.executionQueue) { item.reject(new types_1.MCPError({ code: types_1.MCPErrorCode.FUNCTION_ERROR, message: 'Execution queue cleared', timestamp: new Date(), retryable: false })); } this.executionQueue = []; } /** * Shutdown executor */ shutdown() { // Clear timers if (this.processInterval) { clearInterval(this.processInterval); } // Cancel active executions for (const [id, timeout] of this.activeExecutions) { clearTimeout(timeout); } this.activeExecutions.clear(); // Clear queue this.clearQueue(); this.removeAllListeners(); } } exports.FunctionExecutor = FunctionExecutor; exports.default = FunctionExecutor; //# sourceMappingURL=function-executor.js.map