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

534 lines 23.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HardcodedSensitiveDataRule = void 0; const types_1 = require("../types"); class HardcodedSensitiveDataRule extends types_1.BaseRule { constructor() { super(...arguments); this.name = 'hardcoded-sensitive-data'; this.description = 'Detects hardcoded sensitive information in configuration files with context-aware analysis'; this.severity = 'critical'; this.sensitivePatterns = [ // Database connections: Expanded coverage { pattern: /(?:database_url|db_url|connection_string)\s*[:=]\s*['"`]([^'"`\s]+)['"`]/gi, type: 'Database Connection', confidence: 0.9, severity: 'critical', validation: (text) => this.validateDatabaseConnection(text) }, { pattern: /(?:mongodb|mysql|postgres|redis):\/\/[^'"`\s]+/gi, type: 'Database URL', confidence: 0.95, severity: 'critical', validation: (text) => this.validateDatabaseURL(text) }, // JDBC, MSSQL, Oracle patterns: Expanded coverage { pattern: /jdbc:(?:mysql|postgresql|oracle|sqlserver|mssql):\/\/[^'"`\s]+/gi, type: 'JDBC Connection String', confidence: 0.95, severity: 'critical', validation: (text) => this.validateJDBCConnection(text) }, { pattern: /(?:oracle|mssql|sqlserver)_(?:host|server|port|database|db)\s*[:=]\s*['"`][^'"`\s]+['"`]/gi, type: 'Database Configuration', confidence: 0.85, severity: 'critical', validation: (text) => this.validateDatabaseConfig(text) }, // Encryption keys: Expanded coverage { pattern: /(?:encryption_key|secret_key|private_key)\s*[:=]\s*['"`]([a-zA-Z0-9+/=]{20,})['"`]/gi, type: 'Encryption Key', confidence: 0.9, severity: 'critical', validation: (text) => this.validateEncryptionKey(text) }, { pattern: /-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----/gi, type: 'Private Key', confidence: 0.95, severity: 'critical', validation: (text) => this.validatePrivateKey(text) }, // Generic high entropy keys: Expanded coverage { pattern: /(?:key|secret|token)\s*[:=]\s*['"`]([a-zA-Z0-9+/=_-]{32,})['"`]/gi, type: 'High-Entropy Secret', confidence: 0.8, severity: 'critical', validation: (text) => this.validateHighEntropySecret(text) }, // Configuration secrets: Expanded coverage { pattern: /(?:app_secret|session_secret|jwt_secret)\s*[:=]\s*['"`]([^'"`\s]{16,})['"`]/gi, type: 'Application Secret', confidence: 0.9, severity: 'critical', validation: (text) => this.validateApplicationSecret(text) }, { pattern: /(?:salt|hash_salt)\s*[:=]\s*['"`]([^'"`\s]{8,})['"`]/gi, type: 'Cryptographic Salt', confidence: 0.7, severity: 'high', validation: (text) => this.validateCryptographicSalt(text) }, // Third-party service keys: Expanded coverage { pattern: /(?:stripe_secret|stripe_key)\s*[:=]\s*['"`](sk_[a-zA-Z0-9_]+)['"`]/gi, type: 'Stripe Secret Key', confidence: 0.95, severity: 'critical', validation: (text) => this.validateStripeKey(text) }, { pattern: /(?:sendgrid_api_key)\s*[:=]\s*['"`](SG\.[a-zA-Z0-9_\-\.]+)['"`]/gi, type: 'SendGrid API Key', confidence: 0.95, severity: 'critical', validation: (text) => this.validateSendGridKey(text) }, { pattern: /(?:twilio_auth_token)\s*[:=]\s*['"`]([a-zA-Z0-9]{32})['"`]/gi, type: 'Twilio Auth Token', confidence: 0.95, severity: 'critical', validation: (text) => this.validateTwilioToken(text) }, // AWS, Azure, GCP patterns: Expanded coverage { pattern: /(?:aws_access_key|aws_secret_key|azure_key|gcp_key)\s*[:=]\s*['"`]([a-zA-Z0-9+/=]{20,})['"`]/gi, type: 'Cloud Provider Key', confidence: 0.95, severity: 'critical', validation: (text) => this.validateCloudProviderKey(text) }, // Generic API keys: Expanded coverage { pattern: /(?:api_key|apikey)\s*[:=]\s*['"`](sk_[a-zA-Z0-9_]+)['"`]/gi, type: 'API Key', confidence: 0.9, severity: 'critical', validation: (text) => this.validateAPIKey(text) }, // Generic sensitive patterns: Expanded coverage { pattern: /(?:admin_password|root_password|db_password)\s*[:=]\s*['"`]([^'"`\s]{6,})['"`]/gi, type: 'Admin Password', confidence: 0.8, severity: 'high', validation: (text) => this.validateAdminPassword(text) }, { pattern: /(?:webhook_secret|signing_secret)\s*[:=]\s*['"`]([^'"`\s]{16,})['"`]/gi, type: 'Webhook Secret', confidence: 0.9, severity: 'critical', validation: (text) => this.validateWebhookSecret(text) }, // Configuration file patterns: Expanded coverage { pattern: /password\s*[:=]\s*['"`](?!.*(?:password|secret|key|token))[^'"`\s]{8,}['"`]/gi, type: 'Configuration Password', confidence: 0.7, severity: 'high', validation: (text) => this.validateConfigurationPassword(text) } ]; // Multi line comment patterns: Expanded coverage this.multiLineCommentPatterns = [ /\/\*[\s\S]*?\*\//g, // JavaScript/TypeScript multi-line comments /""".*?"""/gs, // Python docstrings /<!--.*?-->/gs, // HTML comments /#\[\[.*?\]\]/gs, // Lua multi-line comments /\/\*[\s\S]*?\*\//g, // C/C++ multi-line comments /\/\*[\s\S]*?\*\//g // Java multi-line comments ]; this.falsePositivePatterns = [ // Common false positive patterns: Expanded coverage /example/i, /sample/i, /demo/i, /placeholder/i, /your[_-]?(?:key|secret|password)/i, /\$\{.*\}/, // Environment variables /%.*%/, // Windows environment variables /\{\{.*\}\}/, // Template variables /^[x]+$/i, // Only x's /^[*]+$/, // Only asterisks /^[0]+$/, // Only zeros /^[1]+$/, // Only ones /test/i, /mock/i, /fake/i, /dummy/i, /development/i, /dev/i, /staging/i, /localhost/i, /127\.0\.0\.1/i, /console\.log/i, /console\.warn/i, /console\.error/i, /logger\.(?:log|warn|error|info)/i, /print/i, /echo/i, /printf/i, /System\.out\.println/i, /puts/i, /Console\.WriteLine/i, /comment/i, /note/i, /todo/i, /fixme/i, /\/\/.*(?:password|secret|key)/i, /#.*(?:password|secret|key)/i, /\/\*.*(?:password|secret|key).*\*\//i, /<!--.*(?:password|secret|key).*-->/i, /password.*=.*['"`]password['"`]/i, /secret.*=.*['"`]secret['"`]/i, /key.*=.*['"`]key['"`]/i ]; } check(fileContent) { const issues = []; const language = this.detectLanguage(fileContent.path); const framework = this.detectFramework(fileContent.content, language); const hasEnvironmentVariables = this.hasEnvironmentVariables(fileContent.content); // Focuses on configuration files and certain code files if (!this.isSensitiveFile(fileContent.path)) { return issues; } for (const { pattern, type, confidence, severity, validation } of this.sensitivePatterns) { const matches = this.findMatches(fileContent.content, pattern); for (const { match, line, column, lineContent } of matches) { const matchedText = match[0]; const context = this.analyzeContext(fileContent, line, column, language, framework, hasEnvironmentVariables, type); // Skips if in safe context if (this.isSafeContext(context)) { continue; } // Validates the sensitive data issue if (!validation(matchedText)) { continue; } // Calculates final confidence and severity based on context const finalConfidence = this.calculateConfidence(confidence, context); const finalSeverity = this.calculateSeverity(severity, context); if (finalConfidence >= 0.5) { issues.push(this.createIssue(fileContent.path, line, column, lineContent, `${finalSeverity.toUpperCase()}: Hardcoded ${type} found (confidence: ${Math.round(finalConfidence * 100)}%): ${this.maskSensitiveData(matchedText)}`, this.generateSuggestion(type, context), finalSeverity)); } } } return issues; } // Context analysis methods: Expanded coverage detectLanguage(filePath) { const ext = filePath.split('.').pop()?.toLowerCase(); const languageMap = { 'js': 'javascript', 'jsx': 'javascript', 'ts': 'typescript', 'tsx': 'typescript', 'py': 'python', 'php': 'php', 'rb': 'ruby', 'go': 'go', 'java': 'java', 'cs': 'csharp', 'env': 'environment', 'yaml': 'yaml', 'yml': 'yaml', 'json': 'json', 'toml': 'toml', 'ini': 'ini', 'properties': 'properties' }; return languageMap[ext || ''] || 'unknown'; } detectFramework(content, language) { if (language === 'javascript' || language === 'typescript') { if (content.includes('express') || content.includes('app.get') || content.includes('app.post')) return 'express'; if (content.includes('next') || content.includes('Next.js')) return 'nextjs'; if (content.includes('react') || content.includes('jsx') || content.includes('tsx')) return 'react'; } if (language === 'python') { if (content.includes('django') || content.includes('Django')) return 'django'; if (content.includes('flask') || content.includes('Flask')) return 'flask'; } if (language === 'php') { if (content.includes('laravel') || content.includes('Laravel')) return 'laravel'; } return undefined; } hasEnvironmentVariables(content) { const envPatterns = [ /process\.env\./i, /os\.environ\./i, /\$_ENV/i, /getenv\(/i, /System\.getenv\(/i ]; return envPatterns.some(pattern => pattern.test(content)); } analyzeContext(fileContent, line, column, language, framework, hasEnvironmentVariables, issueType) { const lines = fileContent.lines; const currentLine = lines[line - 1] || ''; const surroundingLines = lines.slice(Math.max(0, line - 3), line + 2); return { isInComment: this.isInComment(currentLine, language, fileContent.content, line), isInString: this.isInString(currentLine, column), isInTestFile: this.isInTestFile(fileContent.path), isInDocumentation: this.isInDocumentation(fileContent.path), isInDevelopment: this.isInDevelopment(surroundingLines), surroundingCode: surroundingLines.join('\n'), language, framework, hasEnvironmentVariables: hasEnvironmentVariables || false, issueType }; } isSafeContext(context) { if (context.isInComment) return true; if (context.isInTestFile) return true; if (context.isInDocumentation) return true; if (context.isInDevelopment) return true; if (this.falsePositivePatterns.some(pattern => pattern.test(context.surroundingCode))) { return true; } return false; } isInComment(line, language, fullContent, lineNumber) { const trimmed = line.trim(); // Checks for single line comments if (language === 'javascript' || language === 'typescript') { if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) return true; } if (language === 'python') { if (trimmed.startsWith('#')) return true; } if (language === 'php') { if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('#')) return true; } if (language === 'yaml') { if (trimmed.startsWith('#')) return true; } // Checks for multi line comments const beforeContent = fullContent.split('\n').slice(0, lineNumber).join('\n'); for (const pattern of this.multiLineCommentPatterns) { const matches = beforeContent.match(pattern); if (matches && matches.length > 0) { // Checks if the current line is within a multi line comment const lastMatch = matches[matches.length - 1]; if (lastMatch) { const lastMatchIndex = beforeContent.lastIndexOf(lastMatch); const commentEndIndex = lastMatchIndex + lastMatch.length; // If we're still within the comment, returns true if (commentEndIndex >= beforeContent.length) { return true; } } } } return false; } isInString(line, column) { const before = line.substring(0, column); const quotes = (before.match(/['"`]/g) || []).length; return quotes % 2 === 1; } isInTestFile(filePath) { return filePath.includes('test') || filePath.includes('spec') || filePath.includes('__tests__') || filePath.includes('mock') || filePath.match(/\.(test|spec)\./i) !== null; } isInDocumentation(filePath) { const docPatterns = [ /docs?\//i, /documentation/i, /examples?/i, /samples?/i, /tutorials?/i, /guides?/i, /readme/i, /\.md$/i, /\.rst$/i, /\.txt$/i ]; return docPatterns.some(pattern => pattern.test(filePath)); } isInDevelopment(lines) { return lines.some(line => line.includes('development') || line.includes('dev') || line.includes('staging') || line.includes('localhost') || line.includes('127.0.0.1') || line.includes('NODE_ENV') || line.includes('DEBUG')); } calculateConfidence(baseConfidence, context) { let confidence = baseConfidence; // Adjusts confidence based on context if (context.hasEnvironmentVariables) confidence *= 0.8; // Reduces if env vars present if (context.framework) confidence *= 1.1; // Increases for known frameworks return Math.min(confidence, 1.0); } calculateSeverity(baseSeverity, context) { let severity = baseSeverity; // Adjusts severity based on context if (context.hasEnvironmentVariables) { if (severity === 'critical') severity = 'high'; } return severity; } generateSuggestion(type, context) { const suggestions = { 'Database Connection': 'Move database connection strings to environment variables. Use connection pooling and secure credential management.', 'Database URL': 'Use environment variables for database URLs. Implement proper connection string management.', 'JDBC Connection String': 'Store JDBC connection strings in environment variables or secure configuration files.', 'Database Configuration': 'Use environment variables for database configuration. Implement secure credential management.', 'Encryption Key': 'Store encryption keys in environment variables or use a key management service (KMS).', 'Private Key': 'Store private keys in secure key management systems. Never commit private keys to version control.', 'High-Entropy Secret': 'Move high-entropy secrets to environment variables or secure secret management systems.', 'Application Secret': 'Use environment variables for application secrets. Implement secure secret rotation.', 'Cryptographic Salt': 'Generate salts dynamically or store in secure configuration. Use cryptographically secure random generation.', 'Stripe Secret Key': 'Use Stripe\'s environment variables (STRIPE_SECRET_KEY). Never expose live keys in code.', 'SendGrid API Key': 'Store SendGrid API keys in environment variables. Use API key rotation.', 'Twilio Auth Token': 'Use Twilio\'s environment variables (TWILIO_AUTH_TOKEN). Implement secure token management.', 'Cloud Provider Key': 'Use cloud provider IAM roles and environment variables. Implement least privilege access.', 'API Key': 'Store API keys in environment variables. Use API key rotation and secure storage.', 'Admin Password': 'Use environment variables for admin passwords. Implement secure password management.', 'Webhook Secret': 'Store webhook secrets in environment variables. Use secure secret management.', 'Configuration Password': 'Move passwords to environment variables. Use secure configuration management.' }; let suggestion = suggestions[type] || 'Move sensitive data to environment variables or secure configuration management. Use proper secret management practices.'; if (context.framework) { suggestion += ` For ${context.framework}, consider using framework-specific configuration management.`; if (context.framework === 'express') { suggestion += ' Use dotenv for environment variables and express-session for secure session management.'; } else if (context.framework === 'django') { suggestion += ' Use Django\'s settings.py with environment variables and django-environ for configuration.'; } else if (context.framework === 'laravel') { suggestion += ' Use Laravel\'s .env files and config caching for secure configuration management.'; } } if (context.language === 'environment') { suggestion += ' Consider using a secrets management service like AWS Secrets Manager, Azure Key Vault, or HashiCorp Vault.'; } return suggestion; } isSensitiveFile(filePath) { const sensitiveFiles = [ /\.env/i, /\.config/i, /\.conf/i, /\.ini/i, /\.properties/i, /\.yaml/i, /\.yml/i, /\.json/i, /\.toml/i, /config\./i, /settings\./i, /constants\./i, /\.js$/i, /\.ts$/i, /\.py$/i, /\.php$/i, /\.rb$/i, /\.jsx$/i, /\.tsx$/i, /\.vue$/i, /\.svelte$/i ]; return sensitiveFiles.some(pattern => pattern.test(filePath)); } maskSensitiveData(data) { if (data.length <= 8) { return '*'.repeat(data.length); } const start = data.substring(0, 4); const end = data.substring(data.length - 4); const middle = '*'.repeat(Math.min(data.length - 8, 10)); return `${start}${middle}${end}`; } // Validation methods for different sensitive data patterns: Expanded coverage validateDatabaseConnection(text) { return /(?:database_url|db_url|connection_string)\s*[:=]/.test(text); } validateDatabaseURL(text) { return /(?:mongodb|mysql|postgres|redis):\/\//.test(text); } validateJDBCConnection(text) { return /jdbc:(?:mysql|postgresql|oracle|sqlserver|mssql):\/\//.test(text); } validateDatabaseConfig(text) { return /(?:oracle|mssql|sqlserver)_(?:host|server|port|database|db)\s*[:=]/.test(text); } validateEncryptionKey(text) { return /(?:encryption_key|secret_key|private_key)\s*[:=]/.test(text) && /[a-zA-Z0-9+/=]{20,}/.test(text); } validatePrivateKey(text) { return /-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----/.test(text); } validateHighEntropySecret(text) { return /(?:key|secret|token)\s*[:=]/.test(text) && /[a-zA-Z0-9+/=_-]{32,}/.test(text); } validateApplicationSecret(text) { return /(?:app_secret|session_secret|jwt_secret)\s*[:=]/.test(text); } validateCryptographicSalt(text) { return /(?:salt|hash_salt)\s*[:=]/.test(text); } validateStripeKey(text) { return /(?:stripe_secret|stripe_key)\s*[:=]/.test(text) && /sk_/.test(text); } validateSendGridKey(text) { return /(?:sendgrid_api_key)\s*[:=]/.test(text) && /SG\./.test(text); } validateTwilioToken(text) { return /(?:twilio_auth_token)\s*[:=]/.test(text); } validateCloudProviderKey(text) { return /(?:aws_access_key|aws_secret_key|azure_key|gcp_key)\s*[:=]/.test(text); } validateAPIKey(text) { return /(?:api_key|apikey)\s*[:=]/.test(text) && /sk_/.test(text); } validateAdminPassword(text) { return /(?:admin_password|root_password|db_password)\s*[:=]/.test(text); } validateWebhookSecret(text) { return /(?:webhook_secret|signing_secret)\s*[:=]/.test(text); } validateConfigurationPassword(text) { return /password\s*[:=]/.test(text) && !/(?:password|secret|key|token)/.test(text); } } exports.HardcodedSensitiveDataRule = HardcodedSensitiveDataRule; //# sourceMappingURL=hardcoded-sensitive-data.js.map