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
JavaScript
"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