UNPKG

github-mcp-auto-git

Version:

GitHub MCP Auto Git v3.0 - メモリ効率化・統合MCP・モジュール化完了の完全自動Git操作システム

559 lines 23.6 kB
/** * Security Manager - セキュリティ強化システム * 入力検証、トークン管理、セキュリティ監視の包括的実装 */ import crypto from 'crypto'; // セキュリティレベル定義 export var SecurityLevel; (function (SecurityLevel) { SecurityLevel["PUBLIC"] = "public"; SecurityLevel["INTERNAL"] = "internal"; SecurityLevel["CONFIDENTIAL"] = "confidential"; SecurityLevel["RESTRICTED"] = "restricted"; })(SecurityLevel || (SecurityLevel = {})); // セキュリティ脅威タイプ export var ThreatType; (function (ThreatType) { ThreatType["INJECTION"] = "injection"; ThreatType["CREDENTIAL_LEAK"] = "credential_leak"; ThreatType["PATH_TRAVERSAL"] = "path_traversal"; ThreatType["COMMAND_INJECTION"] = "command_injection"; ThreatType["UNAUTHORIZED_ACCESS"] = "unauthorized_access"; ThreatType["DATA_EXFILTRATION"] = "data_exfiltration"; ThreatType["MALICIOUS_PAYLOAD"] = "malicious_payload"; })(ThreatType || (ThreatType = {})); export class SecurityManager { constructor() { this.securityLog = []; // 危険なパターン定義 this.DANGEROUS_PATTERNS = { // コマンドインジェクション commandInjection: [ /[;&|`$(){}[\]]/g, /\b(rm|del|format|fdisk|kill|shutdown|reboot)\b/gi, /\|\s*(curl|wget|nc|netcat)\b/gi, />\s*\/dev\//gi ], // パストラバーサル pathTraversal: [ /\.\.[\/\\]/g, /\%2e\%2e[\/\\]/gi, /\%252e\%252e[\/\\]/gi, /\.\.%2f/gi, /\.\.%5c/gi ], // 機密情報パターン credentials: [ // GitHub tokens /ghp_[a-zA-Z0-9]{36}/g, /gho_[a-zA-Z0-9]{36}/g, /ghu_[a-zA-Z0-9]{36}/g, /ghs_[a-zA-Z0-9]{36}/g, /ghr_[a-zA-Z0-9]{36}/g, // API keys /sk-[a-zA-Z0-9]{48,}/g, // OpenAI /AIza[0-9A-Za-z\\-_]{35}/g, // Google /AKIA[0-9A-Z]{16}/g, // AWS // Generic patterns /(?:password|pwd|secret|key|token|auth)\s*[:=]\s*['"][^'"]{8,}['"]/gi, /(?:api[_-]?key|access[_-]?token|secret[_-]?key)\s*[:=]\s*['"][^'"]{16,}['"]/gi ], // SQLインジェクション sqlInjection: [ /\b(union|select|insert|update|delete|drop|create|alter|exec|execute)\s+/gi, /\b(or|and)\s+\d+\s*=\s*\d+/gi, /['"]\s*(or|and)\s+['"]/gi, /;\s*(drop|delete|truncate)\s+/gi ], // スクリプトインジェクション scriptInjection: [ /<script[^>]*>.*?<\/script>/gis, /javascript:/gi, /on\w+\s*=\s*['"][^'"]*['"]/gi, /eval\s*\(/gi, /setTimeout\s*\(/gi, /setInterval\s*\(/gi ] }; // ホワイトリスト許可パターン this.ALLOWED_PATTERNS = { filePath: /^[a-zA-Z0-9\/\-_\.]+$/, gitBranch: /^[a-zA-Z0-9\/\-_\.]+$/, gitCommitMessage: /^[\w\s\-\.\(\)\[\],:;!?'"]+$/, url: /^https?:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}(\/.*)?$/, email: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ }; this.encryptionKey = this.generateEncryptionKey(); } /** * 包括的入力検証 */ validateInput(input, expectedType, securityLevel = SecurityLevel.INTERNAL) { const threats = []; let sanitizedInput = input; console.log(`🔒 セキュリティ検証開始: ${expectedType} (レベル: ${securityLevel})`); // 基本型チェック const typeValidation = this.validateType(input, expectedType); if (!typeValidation.isValid) { threats.push({ type: ThreatType.MALICIOUS_PAYLOAD, severity: 'medium', description: `期待される型 ${expectedType} と異なります`, source: 'type_validation', recommendation: '正しい型のデータを提供してください' }); } // 文字列の場合の詳細検証 if (typeof input === 'string') { const stringThreats = this.validateString(input, expectedType); threats.push(...stringThreats); sanitizedInput = this.sanitizeString(input); } // オブジェクトの場合の再帰的検証 if (typeof input === 'object' && input !== null) { const objectThreats = this.validateObject(input); threats.push(...objectThreats); sanitizedInput = this.sanitizeObject(input); } // セキュリティレベル別の追加チェック if (securityLevel === SecurityLevel.RESTRICTED || securityLevel === SecurityLevel.CONFIDENTIAL) { const advancedThreats = this.performAdvancedValidation(input); threats.push(...advancedThreats); } const isValid = threats.filter(t => t.severity === 'critical' || t.severity === 'high').length === 0; // セキュリティログ記録 this.logSecurityEvent('input_validation', isValid ? 'info' : 'warning', { type: expectedType, securityLevel, threatsFound: threats.length, isValid }); console.log(`${isValid ? '✅' : '⚠️'} セキュリティ検証完了: ${threats.length}個の脅威検出`); return { isValid, threats, sanitizedInput, securityLevel }; } /** * 文字列の脅威検出 */ validateString(input, expectedType) { const threats = []; // コマンドインジェクション検出 for (const pattern of this.DANGEROUS_PATTERNS.commandInjection) { const matches = input.match(pattern); if (matches) { threats.push({ type: ThreatType.COMMAND_INJECTION, severity: 'critical', description: 'コマンドインジェクションの可能性があります', source: 'command_injection_check', payload: matches.join(', '), recommendation: '特殊文字を削除するか、エスケープしてください' }); } } // パストラバーサル検出 for (const pattern of this.DANGEROUS_PATTERNS.pathTraversal) { const matches = input.match(pattern); if (matches) { threats.push({ type: ThreatType.PATH_TRAVERSAL, severity: 'high', description: 'パストラバーサル攻撃の可能性があります', source: 'path_traversal_check', payload: matches.join(', '), recommendation: '相対パスの使用を避け、絶対パスを使用してください' }); } } // 機密情報検出 for (const pattern of this.DANGEROUS_PATTERNS.credentials) { const matches = input.match(pattern); if (matches) { threats.push({ type: ThreatType.CREDENTIAL_LEAK, severity: 'critical', description: '機密情報(トークン・パスワード)が検出されました', source: 'credential_leak_check', payload: this.maskSensitiveData(matches.join(', ')), recommendation: '機密情報を削除し、環境変数を使用してください' }); } } // SQLインジェクション検出 for (const pattern of this.DANGEROUS_PATTERNS.sqlInjection) { const matches = input.match(pattern); if (matches) { threats.push({ type: ThreatType.INJECTION, severity: 'high', description: 'SQLインジェクションの可能性があります', source: 'sql_injection_check', payload: matches.join(', '), recommendation: 'SQLクエリの使用を避け、パラメータ化クエリを使用してください' }); } } // スクリプトインジェクション検出 for (const pattern of this.DANGEROUS_PATTERNS.scriptInjection) { const matches = input.match(pattern); if (matches) { threats.push({ type: ThreatType.INJECTION, severity: 'high', description: 'スクリプトインジェクションの可能性があります', source: 'script_injection_check', payload: matches.join(', '), recommendation: 'HTMLエスケープを行い、実行可能コードを削除してください' }); } } return threats; } /** * オブジェクトの検証 */ validateObject(obj) { const threats = []; try { // Git設定オブジェクトのホワイトリスト(緩和されたチェック) const gitConfigKeys = ['autoCommit', 'autoPush', 'createPR', 'branchName', 'targetBranch', 'enabled', 'triggers', 'paths', 'subAgents', 'notifications', 'github']; const isGitConfig = Object.keys(obj).some(key => gitConfigKeys.includes(key)) || Object.keys(obj).every(key => gitConfigKeys.includes(key) || typeof obj[key] === 'boolean' || typeof obj[key] === 'string' || typeof obj[key] === 'number'); // Git設定オブジェクトではない場合のみ完全検証 if (!isGitConfig) { const jsonString = JSON.stringify(obj); const stringThreats = this.validateString(jsonString, 'object'); threats.push(...stringThreats); } // プロトタイプ汚染チェック if (obj.hasOwnProperty('__proto__') || obj.hasOwnProperty('constructor') || obj.hasOwnProperty('prototype')) { threats.push({ type: ThreatType.MALICIOUS_PAYLOAD, severity: 'critical', description: 'プロトタイプ汚染攻撃の可能性があります', source: 'prototype_pollution_check', recommendation: '__proto__, constructor, prototypeプロパティを削除してください' }); } // 再帰的な検証(Git設定の場合は機密情報チェックのみ) for (const [key, value] of Object.entries(obj)) { if (typeof value === 'string') { if (isGitConfig) { // Git設定の場合は機密情報チェックのみ for (const pattern of this.DANGEROUS_PATTERNS.credentials) { const matches = value.match(pattern); if (matches) { threats.push({ type: ThreatType.CREDENTIAL_LEAK, severity: 'critical', description: '機密情報(トークン・パスワード)が検出されました', source: 'credential_leak_check', payload: this.maskSensitiveData(matches.join(', ')), recommendation: '機密情報を削除し、環境変数を使用してください' }); } } } else { // 通常のオブジェクトは完全検証 const valueThreats = this.validateString(value, 'string'); threats.push(...valueThreats); } } } } catch (error) { threats.push({ type: ThreatType.MALICIOUS_PAYLOAD, severity: 'medium', description: 'オブジェクトの解析中にエラーが発生しました', source: 'object_parsing_error', recommendation: '有効なオブジェクト形式を使用してください' }); } return threats; } /** * 高度な検証(機械学習ベースの異常検知風) */ performAdvancedValidation(input) { const threats = []; const inputString = typeof input === 'string' ? input : JSON.stringify(input); // エントロピー分析(ランダムな文字列検出) const entropy = this.calculateEntropy(inputString); if (entropy > 4.5) { // 高エントロピー = 潜在的に悪意のあるペイロード threats.push({ type: ThreatType.MALICIOUS_PAYLOAD, severity: 'medium', description: '高エントロピー文字列が検出されました(暗号化されたペイロードの可能性)', source: 'entropy_analysis', recommendation: '通常のテキスト形式での入力を推奨します' }); } // 長さ分析 if (inputString.length > 10000) { threats.push({ type: ThreatType.DATA_EXFILTRATION, severity: 'medium', description: '異常に長い入力が検出されました', source: 'length_analysis', recommendation: '入力サイズを制限してください' }); } // 異常文字検出 const suspiciousChars = inputString.match(/[^\x20-\x7E\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FAF]/g); if (suspiciousChars && suspiciousChars.length > 5) { threats.push({ type: ThreatType.MALICIOUS_PAYLOAD, severity: 'low', description: '通常でない文字が多数検出されました', source: 'character_analysis', recommendation: '標準的な文字セットの使用を推奨します' }); } return threats; } /** * 文字列サニタイゼーション */ sanitizeString(input) { let sanitized = input; // 基本的な特殊文字のエスケープ sanitized = sanitized .replace(/[<>]/g, '') // HTML tags .replace(/[;&|`]/g, '') // Command separators .replace(/\$\(/g, '') // Command substitution .replace(/\.\.\//g, '') // Path traversal .replace(/eval\(/gi, '') // JavaScript eval .trim(); return sanitized; } /** * オブジェクトサニタイゼーション */ sanitizeObject(obj) { if (typeof obj !== 'object' || obj === null) { return obj; } const sanitized = {}; for (const [key, value] of Object.entries(obj)) { // 危険なキーをスキップ if (['__proto__', 'constructor', 'prototype'].includes(key)) { continue; } if (typeof value === 'string') { sanitized[key] = this.sanitizeString(value); } else if (typeof value === 'object') { sanitized[key] = this.sanitizeObject(value); } else { sanitized[key] = value; } } return sanitized; } /** * トークン管理 */ async validateToken(token, type) { console.log(`🔑 トークン検証開始: ${type}`); const tokenInfo = { value: token, type, permissions: [], isValid: false }; // トークン形式検証 const formatValidation = this.validateTokenFormat(token, type); if (!formatValidation) { this.logSecurityEvent('token_validation', 'error', { type, error: 'Invalid token format' }); return tokenInfo; } // GitHub トークンの特別な検証 if (type === 'github') { try { const githubValidation = await this.validateGitHubToken(token); tokenInfo.permissions = githubValidation.permissions; tokenInfo.isValid = githubValidation.isValid; tokenInfo.expiresAt = githubValidation.expiresAt; } catch (error) { console.warn(`⚠️ GitHub トークン検証失敗: ${error}`); tokenInfo.isValid = false; } } else { // 基本的な検証 tokenInfo.isValid = token.length >= 16 && !this.isTokenCompromised(token); } // 使用記録更新 tokenInfo.lastUsed = new Date(); this.logSecurityEvent('token_validation', tokenInfo.isValid ? 'info' : 'warning', { type, isValid: tokenInfo.isValid, permissions: tokenInfo.permissions }); console.log(`${tokenInfo.isValid ? '✅' : '❌'} トークン検証完了: ${type}`); return tokenInfo; } /** * トークン形式検証 */ validateTokenFormat(token, type) { const patterns = { github: /^gh[pus]_[a-zA-Z0-9]{36}$/, api_key: /^[a-zA-Z0-9\-_]{16,}$/, secret: /^[a-zA-Z0-9\-_]{32,}$/, other: /^[a-zA-Z0-9\-_]{8,}$/ }; const pattern = patterns[type]; return pattern ? pattern.test(token) : false; } /** * GitHub トークン検証(実際のAPI呼び出し) */ async validateGitHubToken(token) { // 実際の実装では GitHub API を呼び出し // ここではモック実装 if (token.startsWith('ghp_')) { return { isValid: true, permissions: ['repo', 'user'], expiresAt: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000) // 1年後 }; } return { isValid: false, permissions: [] }; } /** * トークン漏洩チェック */ isTokenCompromised(token) { // 実際の実装では known compromised tokens database をチェック const compromisedPatterns = [ 'test', 'demo', 'example', 'sample', '123456', 'password', 'secret' ]; return compromisedPatterns.some(pattern => token.toLowerCase().includes(pattern)); } /** * セキュアなトークン暗号化 */ encryptToken(token) { const iv = crypto.randomBytes(16); const cipher = crypto.createCipher('aes-256-cbc', this.encryptionKey); let encrypted = cipher.update(token, 'utf8', 'hex'); encrypted += cipher.final('hex'); return iv.toString('hex') + ':' + encrypted; } /** * トークン復号化 */ decryptToken(encryptedToken) { const [ivHex, encrypted] = encryptedToken.split(':'); if (!ivHex || !encrypted) { throw new Error('Invalid encrypted token format'); } const iv = Buffer.from(ivHex, 'hex'); const decipher = crypto.createDecipher('aes-256-cbc', this.encryptionKey); let decrypted = decipher.update(encrypted, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } /** * ユーティリティ関数 */ generateEncryptionKey() { return crypto.randomBytes(32); } validateType(input, expectedType) { const actualType = typeof input; const isValid = actualType === expectedType || (expectedType === 'array' && Array.isArray(input)) || (expectedType === 'object' && actualType === 'object' && !Array.isArray(input)); return { isValid }; } calculateEntropy(str) { const freq = {}; for (const char of str) { freq[char] = (freq[char] || 0) + 1; } let entropy = 0; const len = str.length; for (const count of Object.values(freq)) { const p = count / len; entropy -= p * Math.log2(p); } return entropy; } maskSensitiveData(data) { return data.replace(/(.{4})(.*)(.{4})/g, '$1***MASKED***$3'); } logSecurityEvent(event, severity, details) { const logEntry = { timestamp: new Date(), event, severity, details }; this.securityLog.push(logEntry); // ログファイルへの出力(本番環境用) if (severity === 'error' || severity === 'warning') { console.warn(`🔒 [${severity.toUpperCase()}] ${event}:`, details); } } /** * セキュリティレポート生成 */ generateSecurityReport() { const recentEvents = this.securityLog .filter(log => Date.now() - log.timestamp.getTime() < 24 * 60 * 60 * 1000) // 24時間以内 .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()) .slice(0, 50); const criticalCount = recentEvents.filter(e => e.severity === 'critical').length; const highCount = recentEvents.filter(e => e.severity === 'high').length; const mediumCount = recentEvents.filter(e => e.severity === 'medium').length; const lowCount = recentEvents.filter(e => e.severity === 'low').length; const recommendations = []; if (criticalCount > 0) { recommendations.push('クリティカルな脅威が検出されています。即座に対応してください。'); } if (highCount > 5) { recommendations.push('高レベルの脅威が多発しています。入力検証を強化してください。'); } if (recentEvents.length > 100) { recommendations.push('セキュリティイベントが多発しています。システムの監視を強化してください。'); } return { summary: { totalEvents: recentEvents.length, criticalThreats: criticalCount, highThreats: highCount, mediumThreats: mediumCount, lowThreats: lowCount }, recentEvents, recommendations }; } /** * セキュリティログのクリーンアップ */ cleanupSecurityLogs(olderThanDays = 30) { const cutoff = Date.now() - (olderThanDays * 24 * 60 * 60 * 1000); const initialCount = this.securityLog.length; this.securityLog = this.securityLog.filter(log => log.timestamp.getTime() > cutoff); const removed = initialCount - this.securityLog.length; console.log(`🧹 ${removed}個の古いセキュリティログをクリーンアップしました`); return removed; } } //# sourceMappingURL=security-manager.js.map