UNPKG

codecrucible-synth

Version:

Production-Ready AI Development Platform with Multi-Voice Synthesis, Smithery MCP Integration, Enterprise Security, and Zero-Timeout Reliability

530 lines 19.3 kB
/** * Claude Code-inspired Security System * Based on research of Claude Code security patterns (2024-2025) * * Key principles from Claude Code: * 1. User consent for potentially dangerous operations * 2. Path validation and CWD restrictions * 3. Command whitelisting with user approval for outliers * 4. Context-aware security rather than blanket blocking * 5. Audit logging for security monitoring */ import { EventEmitter } from 'events'; import * as path from 'path'; /** * Claude Code-inspired security system that prioritizes user consent over blanket blocking */ export class ClaudeCodeSecurity extends EventEmitter { workingDirectory; policy; auditLog = []; pendingConsentRequests = new Map(); constructor(workingDirectory = process.cwd()) { super(); this.workingDirectory = path.resolve(workingDirectory); this.policy = this.createDefaultPolicy(); } createDefaultPolicy() { return { // Path-based security (Claude Code CWD pattern) allowedPaths: [ this.workingDirectory, path.join(this.workingDirectory, '**'), '~/', // User home directory './src/**', './dist/**', './docs/**', './config/**', './tests/**', ], blockedPaths: ['/etc/**', '/sys/**', '/proc/**', 'C:\\Windows\\System32\\**', '/usr/bin/**'], requireConsentForPaths: [ '../**', // Outside project directory '/tmp/**', 'C:\\Users\\**\\AppData\\**', ], // Command security (Claude Code pattern) whitelistedCommands: [ 'ls', 'dir', 'cat', 'type', 'echo', 'pwd', 'cd', 'git', 'npm', 'node', 'python', 'pip', 'grep', 'find', 'sort', 'head', 'tail', 'mkdir', 'touch', ], dangerousCommands: [ 'rm -rf', 'del /s', 'format', 'shutdown', 'reboot', 'halt', 'dd', 'fdisk', 'mkfs', 'chmod 777', ], requireConsentForCommands: [ 'rm', 'del', 'move', 'mv', 'cp', 'copy', 'chmod', 'chown', 'sudo', 'su', 'curl', 'wget', 'ssh', 'scp', ], // File extension security allowedFileExtensions: [ '.js', '.ts', '.json', '.md', '.txt', '.yaml', '.yml', '.css', '.html', '.jsx', '.tsx', '.py', '.java', '.c', '.cpp', '.h', '.go', '.rs', '.php', ], dangerousFileExtensions: ['.exe', '.bat', '.cmd', '.ps1', '.sh', '.com', '.scr'], requireConsentForExtensions: ['.env', '.key', '.pem', '.p12', '.jks'], // Developer-friendly keywords (allowed but logged) developmentKeywords: [ 'refactor', 'update', 'modify', 'change', 'fix', 'implement', 'create', 'generate', 'build', 'compile', 'test', 'debug', ], sqlKeywords: [ 'select', 'insert', 'update', 'delete', 'create', 'drop', 'alter', 'union', 'join', 'where', 'group', 'order', ], systemCommands: ['process', 'system', 'exec', 'spawn', 'fork', 'kill'], }; } /** * Main security evaluation method - Claude Code inspired */ async evaluateSecurity(context) { // Log the operation for audit this.auditLog.push(context); this.emit('securityEvaluation', context); // Path-based security check (Claude Code CWD pattern) if (context.filePath) { const pathDecision = this.evaluatePathSecurity(context.filePath); if (pathDecision.action === 'block') { return pathDecision; } if (pathDecision.action === 'askUser') { return pathDecision; } } // Command-based security check (Claude Code whitelist pattern) if (context.command) { const commandDecision = this.evaluateCommandSecurity(context.command); if (commandDecision.action === 'block') { return commandDecision; } if (commandDecision.action === 'askUser') { return commandDecision; } } // Content analysis (contextual, not keyword blocking) const contentDecision = this.evaluateContentSecurity(context.userInput); if (contentDecision.action !== 'allow') { return contentDecision; } // Default: allow with logging return { action: 'allow', reason: 'Operation within security policy', riskLevel: 'low', }; } evaluatePathSecurity(filePath) { const resolvedPath = path.resolve(filePath); // Check blocked paths first for (const blockedPattern of this.policy.blockedPaths) { if (this.matchesPattern(resolvedPath, blockedPattern)) { return { action: 'block', reason: `Access to blocked path: ${blockedPattern}`, riskLevel: 'high', }; } } // Check if requires consent for (const consentPattern of this.policy.requireConsentForPaths) { if (this.matchesPattern(resolvedPath, consentPattern)) { return { action: 'askUser', reason: `Access outside working directory requires consent`, riskLevel: 'medium', requiresConsent: true, suggestedActions: ['Allow once', 'Allow for this session', 'Deny'], }; } } // Check if within allowed paths for (const allowedPattern of this.policy.allowedPaths) { if (this.matchesPattern(resolvedPath, allowedPattern)) { return { action: 'allow', reason: 'Path within allowed directory', riskLevel: 'low', }; } } // Default: ask for consent for unknown paths return { action: 'askUser', reason: 'Path not in predefined allowed list', riskLevel: 'medium', requiresConsent: true, }; } evaluateCommandSecurity(command) { const cmdLower = command.toLowerCase().trim(); // Check dangerous commands for (const dangerous of this.policy.dangerousCommands) { if (cmdLower.includes(dangerous.toLowerCase())) { return { action: 'block', reason: `Dangerous command detected: ${dangerous}`, riskLevel: 'critical', }; } } // Check commands requiring consent for (const consentCmd of this.policy.requireConsentForCommands) { if (cmdLower.startsWith(consentCmd.toLowerCase())) { return { action: 'askUser', reason: `Command '${consentCmd}' requires user consent`, riskLevel: 'medium', requiresConsent: true, suggestedActions: ['Allow this command', 'Modify command', 'Deny'], }; } } // Check whitelisted commands for (const allowed of this.policy.whitelistedCommands) { if (cmdLower.startsWith(allowed.toLowerCase())) { return { action: 'allow', reason: `Whitelisted command: ${allowed}`, riskLevel: 'low', }; } } // Default: ask for consent for unknown commands return { action: 'askUser', reason: 'Command not in whitelist', riskLevel: 'medium', requiresConsent: true, }; } evaluateContentSecurity(content) { const contentLower = content.toLowerCase(); // Check for development keywords (allow but note) const foundKeywords = { sql: this.policy.sqlKeywords.filter(kw => contentLower.includes(kw.toLowerCase())), development: this.policy.developmentKeywords.filter(kw => contentLower.includes(kw.toLowerCase())), system: this.policy.systemCommands.filter(kw => contentLower.includes(kw.toLowerCase())), }; // Contextual analysis - this is the key improvement over keyword blocking const isDevelopmentContext = this.isDevelopmentContext(content, foundKeywords); const isSQLContext = this.isSQLContext(content, foundKeywords); const isSystemContext = this.isSystemContext(content, foundKeywords); // Log for audit but don't block development work if (foundKeywords.sql.length > 0 || foundKeywords.system.length > 0 || foundKeywords.development.length > 0) { this.emit('keywordDetected', { sql: foundKeywords.sql, development: foundKeywords.development, system: foundKeywords.system, context: { isDevelopmentContext, isSQLContext, isSystemContext }, content: content.substring(0, 200) + '...', }); } // Only block or request consent for truly dangerous patterns const dangerousPatterns = [ // System commands { pattern: /rm\s+-rf\s+[\/\\]\*?/, reason: 'Recursive file deletion detected', risk: 'critical', }, { pattern: /format\s+[cC]:/i, reason: 'Disk format command detected', risk: 'critical' }, { pattern: /shutdown\s+-[rf]/i, reason: 'System shutdown command detected', risk: 'high' }, { pattern: /del\s+[\/\\].*\*.*[\/\\]s/i, reason: 'Recursive delete command detected', risk: 'critical', }, { pattern: /echo\s+.*>\s*\/dev\/(null|zero|random)/i, reason: 'System device manipulation detected', risk: 'high', }, // AI-specific prompt injection patterns (2024 threats) { pattern: /ignore\s+(?:all\s+)?(?:previous|above)\s+instructions?/i, reason: 'Prompt injection attempt detected', risk: 'high', }, { pattern: /forget\s+(everything|all|previous)(?:\s+(?:and\s+)?(?:start\s+over|instructions?|prompts?))?/i, reason: 'Memory manipulation attempt detected', risk: 'high', }, { pattern: /new\s+(instructions?|system\s+prompt|role):\s*/i, reason: 'Role hijacking attempt detected', risk: 'high', }, { pattern: /system\s*:\s*you\s+(are\s+now|must\s+now)/i, reason: 'System override attempt detected', risk: 'high', }, { pattern: /override\s+security/i, reason: 'Security bypass attempt detected', risk: 'critical', }, ]; for (const { pattern, reason, risk } of dangerousPatterns) { if (pattern.test(content)) { return { action: 'askUser', reason, riskLevel: risk, requiresConsent: true, suggestedActions: ['Review and confirm', 'Modify operation', 'Cancel'], }; } } // Handle file modification requests FIRST (higher priority than development context) if (this.isFileModificationRequest(content)) { return { action: 'askUser', reason: 'File modification requested - requires confirmation', riskLevel: 'medium', requiresConsent: true, suggestedActions: ['Allow file changes', 'Review changes first', 'Deny'], }; } // Handle development contexts appropriately (only for non-file operations) if (isDevelopmentContext && (foundKeywords.sql.length > 0 || foundKeywords.development.length > 0)) { // This is legitimate development work - allow with low risk return { action: 'allow', reason: 'Development operation in appropriate context', riskLevel: 'low', }; } return { action: 'allow', reason: 'Content passed contextual security analysis', riskLevel: 'low', }; } /** * Determine if content is in a development context */ isDevelopmentContext(content, keywords) { const contentLower = content.toLowerCase(); // Look for development indicators const devIndicators = [ 'refactor', 'implement', 'create function', 'modify code', 'update code', 'code review', 'test', 'debug', 'file.js', 'file.ts', 'component', 'database', 'query', 'migration', 'schema', 'table', ]; return (devIndicators.some(indicator => contentLower.includes(indicator)) || keywords.development.length > 0); } /** * Determine if content is in a SQL/database context */ isSQLContext(content, keywords) { const contentLower = content.toLowerCase(); // Look for SQL/database context indicators const sqlIndicators = ['database', 'table', 'query', 'migration', 'schema', 'sql']; return (sqlIndicators.some(indicator => contentLower.includes(indicator)) || keywords.sql.length >= 2); // Multiple SQL keywords suggest SQL context } /** * Determine if content is in a system administration context */ isSystemContext(content, keywords) { const contentLower = content.toLowerCase(); // Look for system administration context const sysIndicators = ['server', 'deploy', 'production', 'system', 'process']; return (sysIndicators.some(indicator => contentLower.includes(indicator)) || keywords.system.length > 0); } /** * Check if content is requesting file modifications */ isFileModificationRequest(content) { const contentLower = content.toLowerCase(); const fileModificationPatterns = [ /write\s+to\s+file/, /modify\s+.*\.js/, /change\s+.*\.ts/, /update\s+.*file/, /edit\s+.*\.json/, /create\s+.*\.js/, ]; return (fileModificationPatterns.some(pattern => pattern.test(contentLower)) || (contentLower.includes('file') && (contentLower.includes('write') || contentLower.includes('modify') || contentLower.includes('change')))); } matchesPattern(path, pattern) { // Simple glob-like pattern matching const regex = new RegExp(pattern.replace(/\*\*/g, '.*').replace(/\*/g, '[^/\\\\]*').replace(/\?/g, '.')); return regex.test(path); } /** * Create user consent request (Claude Code pattern) */ async requestUserConsent(context, decision) { const requestId = `consent_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const request = { id: requestId, context, decision, message: this.formatConsentMessage(context, decision), options: this.createConsentOptions(decision), }; this.pendingConsentRequests.set(requestId, request); this.emit('consentRequired', request); return request; } formatConsentMessage(context, decision) { let message = `Security Review Required\n\n`; message += `Operation: ${context.operation}\n`; message += `Risk Level: ${decision.riskLevel.toUpperCase()}\n`; message += `Reason: ${decision.reason}\n\n`; if (context.filePath) { message += `File: ${context.filePath}\n`; } if (context.command) { message += `Command: ${context.command}\n`; } message += `\nInput: ${context.userInput.substring(0, 200)}${context.userInput.length > 200 ? '...' : ''}\n\n`; message += `Would you like to proceed?`; return message; } createConsentOptions(decision) { const baseOptions = [ { id: 'allow', label: 'Allow', description: 'Proceed with this operation', action: 'allow', }, { id: 'deny', label: 'Deny', description: 'Block this operation', action: 'deny', }, ]; if (decision.suggestedActions?.includes('Modify')) { baseOptions.splice(1, 0, { id: 'modify', label: 'Modify', description: 'Suggest a safer alternative', action: 'modify', }); } return baseOptions; } /** * Get security audit log */ getAuditLog(limit) { return limit ? this.auditLog.slice(-limit) : [...this.auditLog]; } /** * Clear audit log (for privacy) */ clearAuditLog() { this.auditLog = []; this.emit('auditLogCleared'); } /** * Update security policy */ updatePolicy(updates) { this.policy = { ...this.policy, ...updates }; this.emit('policyUpdated', this.policy); } } //# sourceMappingURL=claude-code-security.js.map