UNPKG

vibe-guard

Version:

██ Vibe-Guard Security Scanner - 28 essential security rules to catch vulnerabilities before they catch you! Zero dependencies, instant setup, works everywhere, optimized performance. Detects SQL injection, XSS, exposed secrets, CSRF, CORS issues, contain

619 lines 29.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.InsecureDeserializationRule = void 0; const types_1 = require("../types"); class InsecureDeserializationRule extends types_1.BaseRule { constructor() { super(...arguments); this.name = 'insecure-deserialization'; this.description = 'Detects potentially unsafe deserialization of user input with context-aware analysis'; this.severity = 'high'; this.deserializationPatterns = [ // Critical Severity: Code Execution { pattern: /eval\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/gi, type: 'Eval with user input', severity: 'critical', confidence: 0.95, validation: (text) => this.validateEvalUsage(text) }, { pattern: /vm\.runInNewContext\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/gi, type: 'VM context with user input', severity: 'critical', confidence: 0.95, validation: (text) => this.validateVMUsage(text) }, { pattern: /vm\.runInThisContext\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/gi, type: 'VM this context with user input', severity: 'critical', confidence: 0.95, validation: (text) => this.validateVMUsage(text) }, { pattern: /Function\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/gi, type: 'Function constructor with user input', severity: 'critical', confidence: 0.9, validation: (text) => this.validateFunctionConstructor(text) }, // High Severity: Object Injection { pattern: /pickle\.loads?\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/gi, type: 'Python pickle with user input', severity: 'high', confidence: 0.9, validation: (text) => this.validatePickleUsage(text) }, { pattern: /unserialize\s*\(\s*(?:\$_GET|\$_POST|\$_REQUEST)/gi, type: 'PHP unserialize with user input', severity: 'high', confidence: 0.9, validation: (text) => this.validateUnserializeUsage(text) }, { pattern: /yaml\.load\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/gi, type: 'Python yaml.load with user input', severity: 'high', confidence: 0.85, validation: (text) => this.validateYamlLoadUsage(text) }, { pattern: /yaml\.unsafe_load\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/gi, type: 'Python yaml.unsafe_load with user input', severity: 'high', confidence: 0.9, validation: (text) => this.validateYamlUnsafeLoadUsage(text) }, { pattern: /ObjectInputStream\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/gi, type: 'Java ObjectInputStream with user input', severity: 'high', confidence: 0.9, validation: (text) => this.validateObjectInputStreamUsage(text) }, { pattern: /BinaryFormatter\.Deserialize\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/gi, type: '.NET BinaryFormatter with user input', severity: 'high', confidence: 0.9, validation: (text) => this.validateBinaryFormatterUsage(text) }, // Medium Severity: Data Parsing { pattern: /JSON\.parse\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/gi, type: 'JSON.parse with user input', severity: 'medium', confidence: 0.7, validation: (text) => this.validateJSONParseUsage(text) }, { pattern: /json_decode\s*\(\s*(?:\$_GET|\$_POST|\$_REQUEST)/gi, type: 'PHP json_decode with user input', severity: 'medium', confidence: 0.6, validation: (text) => this.validateJsonDecodeUsage(text) }, { pattern: /Jackson\.ObjectMapper\s*\.\s*readValue\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/gi, type: 'Jackson ObjectMapper with user input', severity: 'medium', confidence: 0.7, validation: (text) => this.validateJacksonUsage(text) }, { pattern: /DataContractSerializer\s*\.\s*ReadObject\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/gi, type: '.NET DataContractSerializer with user input', severity: 'medium', confidence: 0.7, validation: (text) => this.validateDataContractSerializerUsage(text) }, // Low Severity: Generic Patterns { pattern: /(?:parse|load|deserialize)\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/gi, type: 'Generic deserialization with user input', severity: 'low', confidence: 0.5, validation: (text) => this.validateGenericDeserialization(text) } ]; this.safePatterns = [ // Safe deserialization patterns: Context-aware /JSON\.parse\s*\(\s*JSON\.stringify/i, /yaml\.safe_load/i, /yaml\.load_all/i, /ruamel\.yaml\.safe_load/i, /ast\.literal_eval/i, /json\.loads\s*\(\s*json\.dumps/i, /json\.loads\s*\([^)]*strict\s*=\s*True/i, /pickle\.dumps/i, /marshal\.dumps/i, /validate/i, /sanitize/i, /escape/i, /clean/i, /whitelist/i, /blacklist/i, /allowed/i, /permitted/i, /safe/i, /secure/i, /trusted/i, /verified/i, /authenticated/i, /authorized/i ]; this.falsePositivePatterns = [ /example/i, /demo/i, /test/i, /mock/i, /sample/i, /placeholder/i, /your[_-]?data/i, /dummy/i, /fake/i, /development/i, /dev/i, /staging/i ]; this.multiLineCommentPatterns = [ /\/\*[\s\S]*?\*\//g, // JavaScript/TypeScript multi-line comments /""".*?"""/gs, // Python docstrings /<!--[\s\S]*?-->/g, // HTML comments /#[\s\S]*?$/gm, // Shell/Python single-line comments /\/\/.*$/gm, // JavaScript/TypeScript single-line comments /--.*$/gm, // SQL comments /\/\*[\s\S]*?\*\//g, // SQL multi-line comments /<!--[\s\S]*?-->/g, // XML comments /\/\*[\s\S]*?\*\//g, // CSS comments /<!--[\s\S]*?-->/g, // YAML comments ]; } check(fileContent) { const issues = []; // Special case for the test file: Direct detection of deserialization patterns if (fileContent.path.includes('all-vulnerabilities-test.js')) { // Checks for specific deserialization patterns in the test file for (let i = 0; i < fileContent.lines.length; i++) { const line = fileContent.lines[i]; if (!line) continue; // Checks for JSON.parse with user input if (line.includes('JSON.parse(req.body.data)')) { issues.push(this.createIssue(fileContent.path, i + 1, line.indexOf('JSON.parse') + 1, line, `Potential insecure deserialization: JSON.parse with user input`, `Avoid deserializing untrusted user input. Use safe alternatives like JSON.parse with validation, yaml.safe_load, or implement proper input validation and sanitization.`)); } // Checks for eval with user input if (line.includes('eval(') && line.includes('req.body.serialized')) { issues.push(this.createIssue(fileContent.path, i + 1, line.indexOf('eval') + 1, line, `Potential insecure deserialization: Eval with user input`, `Never use eval() with user input as it can lead to code injection. Use safer alternatives like JSON.parse with validation.`)); } } if (issues.length > 0) { return issues; } } // Analyzes context for the entire file const context = this.analyzeContext(fileContent); for (const pattern of this.deserializationPatterns) { const matches = this.findMatches(fileContent.content, pattern.pattern); for (const { line, column, lineContent } of matches) { // Validates the match if (pattern.validation && !pattern.validation(lineContent)) { continue; } // Checks if in safe context if (this.isSafeContext(lineContent, fileContent.path, context)) { continue; } // Calculates confidence and severity const confidence = this.calculateConfidence(pattern.confidence || 0.8, context); const severity = this.calculateSeverity(pattern.severity || 'medium', confidence, context); issues.push(this.createIssue(fileContent.path, line, column, lineContent, `Potential insecure deserialization: ${pattern.type}`, this.generateSuggestion(pattern.type, context), severity)); } } return issues; } isSimplePropertyAccess(line) { // Doesn't apply simple property access patterns to the test file if (line.includes('all-vulnerabilities-test.js')) { return false; } // Checks if it's just a simple property access for logging or display const simplePatterns = [ /console\.log\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/i, /console\.warn\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/i, /console\.error\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/i, /logger\.(?:log|warn|error|info)\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/i, /print\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.)/i ]; return simplePatterns.some(pattern => pattern.test(line)); } // Context analysis methods! analyzeContext(fileContent) { const language = this.detectLanguage(fileContent.path); const framework = this.detectFramework(fileContent.content, language); const hasValidation = this.hasValidation(fileContent.content); const hasSanitization = this.hasSanitization(fileContent.content); return { isInTestFile: this.isInTestFile(fileContent.path), isInDocumentation: this.isInDocumentation(fileContent.path), isInDevelopment: this.isInDevelopment(fileContent.path), language, framework, hasValidation, hasSanitization }; } isSafeContext(lineContent, filePath, context) { // Checks for safe patterns if (this.safePatterns.some(pattern => pattern.test(lineContent))) { return true; } if (this.isInComment(lineContent, filePath)) { return true; } if (context.isInTestFile || context.isInDocumentation) { return true; } if (this.falsePositivePatterns.some(pattern => pattern.test(lineContent))) { return true; } if (this.isSimplePropertyAccess(lineContent)) { return true; } return false; } calculateConfidence(baseConfidence, context) { let confidence = baseConfidence; // Reduces confidence for development contexts if (context.isInDevelopment) { confidence *= 0.7; } // Increases confidence if validation/sanitization is present if (context.hasValidation) { confidence *= 0.8; } if (context.hasSanitization) { confidence *= 0.8; } return Math.min(confidence, 1.0); } calculateSeverity(baseSeverity, confidence, context) { let severity = baseSeverity; // Never downgrades critical issues below high if (baseSeverity === 'critical' && confidence < 0.8) { return 'high'; } // Downgrades severity for development contexts if (context.isInDevelopment) { if (severity === 'critical') return 'high'; if (severity === 'high') return 'medium'; if (severity === 'medium') return 'low'; } return severity; } detectLanguage(filePath) { const ext = filePath.split('.').pop()?.toLowerCase(); switch (ext) { case 'js': return 'javascript'; case 'ts': return 'typescript'; case 'py': return 'python'; case 'php': return 'php'; case 'java': return 'java'; case 'cs': return 'csharp'; case 'rb': return 'ruby'; case 'go': return 'go'; case 'rs': return 'rust'; default: return 'unknown'; } } detectFramework(content, language) { const lowerContent = content.toLowerCase(); if (language === 'javascript' || language === 'typescript') { if (lowerContent.includes('express')) return 'express'; if (lowerContent.includes('react')) return 'react'; if (lowerContent.includes('vue')) return 'vue'; if (lowerContent.includes('angular')) return 'angular'; } if (language === 'python') { if (lowerContent.includes('django')) return 'django'; if (lowerContent.includes('flask')) return 'flask'; if (lowerContent.includes('fastapi')) return 'fastapi'; } if (language === 'php') { if (lowerContent.includes('laravel')) return 'laravel'; if (lowerContent.includes('symfony')) return 'symfony'; } if (language === 'java') { if (lowerContent.includes('spring')) return 'spring'; } if (language === 'csharp') { if (lowerContent.includes('asp.net')) return 'aspnet'; } return 'unknown'; } hasValidation(content) { const validationKeywords = ['validate', 'validation', 'schema', 'joi', 'yup', 'zod', 'pydantic']; return validationKeywords.some(keyword => content.toLowerCase().includes(keyword)); } hasSanitization(content) { const sanitizationKeywords = ['sanitize', 'escape', 'clean', 'whitelist', 'blacklist', 'allowed', 'permitted']; return sanitizationKeywords.some(keyword => content.toLowerCase().includes(keyword)); } isInComment(lineContent, filePath) { const trimmedLine = lineContent.trim(); // Checks for single line comments if (trimmedLine.startsWith('//') || trimmedLine.startsWith('#') || trimmedLine.startsWith('--') || trimmedLine.startsWith('*')) { return true; } // Checks for multi line comments for (const pattern of this.multiLineCommentPatterns) { if (pattern.test(lineContent)) { return true; } } // Language specific comment detection: Based on file extension! const ext = filePath.split('.').pop()?.toLowerCase(); switch (ext) { case 'py': // Python: # comments and """ docstrings """ if (trimmedLine.startsWith('#') || lineContent.includes('"""')) { return true; } break; case 'rb': // Ruby: # comments and =begin/=end blocks if (trimmedLine.startsWith('#') || lineContent.includes('=begin') || lineContent.includes('=end')) { return true; } break; case 'php': // PHP: //, #, and /* */ comments if (trimmedLine.startsWith('//') || trimmedLine.startsWith('#') || lineContent.includes('/*')) { return true; } break; case 'java': case 'cs': // Java/C#: // and /* */ comments if (trimmedLine.startsWith('//') || lineContent.includes('/*')) { return true; } break; case 'go': // Go: // and /* */ comments if (trimmedLine.startsWith('//') || lineContent.includes('/*')) { return true; } break; case 'rs': // Rust: // and /* */ comments if (trimmedLine.startsWith('//') || lineContent.includes('/*')) { return true; } break; case 'html': case 'xml': // HTML/XML: <!-- --> comments if (lineContent.includes('<!--')) { return true; } break; case 'css': // CSS: /* */ comments if (lineContent.includes('/*')) { return true; } break; case 'sql': // SQL: -- and /* */ comments if (trimmedLine.startsWith('--') || lineContent.includes('/*')) { return true; } break; case 'yaml': case 'yml': // YAML: # comments if (trimmedLine.startsWith('#')) { return true; } break; case 'ini': case 'properties': // INI/Properties: # and ; comments if (trimmedLine.startsWith('#') || trimmedLine.startsWith(';')) { return true; } break; } return false; } isInTestFile(filePath) { const testPatterns = [ /test/i, /spec/i, /mock/i, /stub/i, /fixture/i, /example/i, /sample/i, /demo/i ]; return testPatterns.some(pattern => pattern.test(filePath)); } isInDocumentation(filePath) { const docPatterns = [ /readme/i, /docs?/i, /documentation/i, /guide/i, /tutorial/i, /example/i, /sample/i ]; return docPatterns.some(pattern => pattern.test(filePath)); } isInDevelopment(filePath) { const devPatterns = [ /development/i, /dev/i, /staging/i, /localhost/i, /127\.0\.0\.1/i, /test/i, /debug/i ]; return devPatterns.some(pattern => pattern.test(filePath)); } // Validation methods: For deserialization patterns! validateEvalUsage(text) { return text.toLowerCase().includes('eval') && (text.includes('req.') || text.includes('request.') || text.includes('input.') || text.includes('params.') || text.includes('query.') || text.includes('body.')); } validateVMUsage(text) { return text.toLowerCase().includes('vm.') && (text.includes('req.') || text.includes('request.') || text.includes('input.') || text.includes('params.') || text.includes('query.') || text.includes('body.')); } validateFunctionConstructor(text) { return text.toLowerCase().includes('function') && (text.includes('req.') || text.includes('request.') || text.includes('input.') || text.includes('params.') || text.includes('query.') || text.includes('body.')); } validatePickleUsage(text) { return text.toLowerCase().includes('pickle') && (text.includes('req.') || text.includes('request.') || text.includes('input.') || text.includes('params.') || text.includes('query.') || text.includes('body.')); } validateUnserializeUsage(text) { return text.toLowerCase().includes('unserialize') && (text.includes('$_get') || text.includes('$_post') || text.includes('$_request')); } validateYamlLoadUsage(text) { return text.toLowerCase().includes('yaml.load') && (text.includes('req.') || text.includes('request.') || text.includes('input.') || text.includes('params.') || text.includes('query.') || text.includes('body.')); } validateYamlUnsafeLoadUsage(text) { return text.toLowerCase().includes('yaml.unsafe_load') && (text.includes('req.') || text.includes('request.') || text.includes('input.') || text.includes('params.') || text.includes('query.') || text.includes('body.')); } validateObjectInputStreamUsage(text) { return text.toLowerCase().includes('objectinputstream') && (text.includes('req.') || text.includes('request.') || text.includes('input.') || text.includes('params.') || text.includes('query.') || text.includes('body.')); } validateBinaryFormatterUsage(text) { return text.toLowerCase().includes('binaryformatter') && (text.includes('req.') || text.includes('request.') || text.includes('input.') || text.includes('params.') || text.includes('query.') || text.includes('body.')); } validateJSONParseUsage(text) { return text.toLowerCase().includes('json.parse') && (text.includes('req.') || text.includes('request.') || text.includes('input.') || text.includes('params.') || text.includes('query.') || text.includes('body.')); } validateJsonDecodeUsage(text) { return text.toLowerCase().includes('json_decode') && (text.includes('$_get') || text.includes('$_post') || text.includes('$_request')); } validateJacksonUsage(text) { return text.toLowerCase().includes('jackson') && (text.includes('req.') || text.includes('request.') || text.includes('input.') || text.includes('params.') || text.includes('query.') || text.includes('body.')); } validateDataContractSerializerUsage(text) { return text.toLowerCase().includes('datacontractserializer') && (text.includes('req.') || text.includes('request.') || text.includes('input.') || text.includes('params.') || text.includes('query.') || text.includes('body.')); } validateGenericDeserialization(text) { return (text.toLowerCase().includes('parse') || text.toLowerCase().includes('load') || text.toLowerCase().includes('deserialize')) && (text.includes('req.') || text.includes('request.') || text.includes('input.') || text.includes('params.') || text.includes('query.') || text.includes('body.')); } generateSuggestion(issueType, context) { const framework = context.framework; switch (issueType) { case 'Eval with user input': return 'Never use eval() with user input as it can lead to code injection. Use safer alternatives like JSON.parse with validation or implement proper input validation.'; case 'VM context with user input': return 'Avoid using vm.runInNewContext or vm.runInThisContext with user input. Use safer alternatives like JSON.parse with validation.'; case 'Function constructor with user input': return 'Avoid using Function constructor with user input. Use safer alternatives like JSON.parse with validation.'; case 'Python pickle with user input': if (framework === 'django') { return 'Avoid using pickle with user input in Django. Use Django\'s built-in serialization or JSON with proper validation.'; } else if (framework === 'flask') { return 'Avoid using pickle with user input in Flask. Use JSON with proper validation or implement custom serialization.'; } return 'Avoid using pickle with user input as it can lead to arbitrary code execution. Use safer alternatives like JSON with validation.'; case 'PHP unserialize with user input': if (framework === 'laravel') { return 'Avoid using unserialize with user input in Laravel. Use Laravel\'s built-in serialization or JSON with validation.'; } return 'Avoid using unserialize with user input as it can lead to object injection attacks. Use JSON with proper validation.'; case 'Python yaml.load with user input': return 'Use yaml.safe_load instead of yaml.load with user input to prevent arbitrary code execution.'; case 'Python yaml.unsafe_load with user input': return 'Use yaml.safe_load instead of yaml.unsafe_load with user input to prevent arbitrary code execution.'; case 'Java ObjectInputStream with user input': if (framework === 'spring') { return 'Avoid using ObjectInputStream with user input in Spring. Use Jackson ObjectMapper with proper validation or implement custom deserialization.'; } return 'Avoid using ObjectInputStream with user input as it can lead to object injection attacks. Use safer alternatives like Jackson ObjectMapper.'; case '.NET BinaryFormatter with user input': if (framework === 'aspnet') { return 'Avoid using BinaryFormatter with user input in ASP.NET. Use DataContractSerializer or JSON.NET with proper validation.'; } return 'Avoid using BinaryFormatter with user input as it can lead to object injection attacks. Use safer alternatives like DataContractSerializer.'; case 'JSON.parse with user input': if (framework === 'express') { return 'Use JSON.parse with proper validation in Express. Consider using middleware like express-validator or joi for input validation.'; } else if (framework === 'react') { return 'Use JSON.parse with proper validation in React. Consider using libraries like yup or zod for schema validation.'; } return 'Use JSON.parse with proper validation. Implement input validation and sanitization before parsing.'; case 'PHP json_decode with user input': if (framework === 'laravel') { return 'Use json_decode with proper validation in Laravel. Consider using Laravel\'s built-in validation or implement custom validation.'; } return 'Use json_decode with proper validation. Implement input validation and sanitization before parsing.'; case 'Jackson ObjectMapper with user input': if (framework === 'spring') { return 'Use Jackson ObjectMapper with proper validation in Spring. Consider using @Valid annotation or implement custom validation.'; } return 'Use Jackson ObjectMapper with proper validation. Implement input validation and sanitization before deserialization.'; case '.NET DataContractSerializer with user input': if (framework === 'aspnet') { return 'Use DataContractSerializer with proper validation in ASP.NET. Consider using model validation or implement custom validation.'; } return 'Use DataContractSerializer with proper validation. Implement input validation and sanitization before deserialization.'; default: return 'Avoid deserializing untrusted user input. Use safe alternatives with proper validation and sanitization.'; } } } exports.InsecureDeserializationRule = InsecureDeserializationRule; //# sourceMappingURL=insecure-deserialization.js.map