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

650 lines 28.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.InsecureHttpRule = void 0; const types_1 = require("../types"); class InsecureHttpRule extends types_1.BaseRule { constructor() { super(...arguments); this.name = 'insecure-http'; this.description = 'Detects insecure HTTP usage instead of HTTPS with context-aware analysis'; this.severity = 'medium'; this.httpPatterns = [ // Critical Severity: Server and API endpoints { pattern: /(?:api_url|endpoint|base_url|apiEndpoint|baseUrl|apiUrl)\s*[:=]\s*['"`]http:\/\/[^'"`\s]+['"`]/gi, type: 'HTTP API Endpoint', severity: 'critical', confidence: 0.95, validation: (text) => this.validateHttpApiEndpoint(text) }, { pattern: /fetch\s*\(\s*['"`]http:\/\/[^'"`\s]+['"`]/gi, type: 'HTTP Fetch Request', severity: 'critical', confidence: 0.9, validation: (text) => this.validateHttpFetchRequest(text) }, { pattern: /axios\.(?:get|post|put|delete|patch)\s*\(\s*['"`]http:\/\/[^'"`\s]+['"`]/gi, type: 'HTTP Axios Request', severity: 'critical', confidence: 0.9, validation: (text) => this.validateHttpAxiosRequest(text) }, { pattern: /http\.createServer\s*\(\s*(?!.*https)/gi, type: 'HTTP Server Creation', severity: 'critical', confidence: 0.95, validation: (text) => this.validateHttpServerCreation(text) }, { pattern: /require\s*\(\s*['"`]http['"`]\s*\)/gi, type: 'HTTP Module Import', severity: 'critical', confidence: 0.9, validation: (text) => this.validateHttpModuleImport(text) }, // High Severity: Configuration and server binding { pattern: /(?:protocol|scheme)\s*[:=]\s*['"`]http['"`]/gi, type: 'HTTP Protocol Configuration', severity: 'high', confidence: 0.85, validation: (text) => this.validateHttpProtocolConfig(text) }, { pattern: /secure\s*[:=]\s*false/gi, type: 'Insecure Configuration', severity: 'high', confidence: 0.85, validation: (text) => this.validateInsecureConfig(text) }, { pattern: /app\.listen\s*\(\s*\d+\s*,\s*['"`]0\.0\.0\.0['"`]/gi, type: 'HTTP Server Binding', severity: 'high', confidence: 0.85, validation: (text) => this.validateHttpServerBinding(text) }, { pattern: /httpOnly\s*:\s*false/gi, type: 'Insecure Cookie Configuration', severity: 'high', confidence: 0.9, validation: (text) => this.validateInsecureCookieConfig(text) }, { pattern: /secure\s*:\s*false/gi, type: 'Insecure Cookie Security', severity: 'high', confidence: 0.9, validation: (text) => this.validateInsecureCookieSecurity(text) }, // Medium Severity: Mixed content and framework-specific { pattern: /src\s*=\s*['"`]http:\/\/[^'"`\s]+['"`]/gi, type: 'Mixed Content Resource', severity: 'medium', confidence: 0.8, validation: (text) => this.validateMixedContentResource(text) }, { pattern: /href\s*=\s*['"`]http:\/\/[^'"`\s]+['"`]/gi, type: 'Mixed Content Link', severity: 'medium', confidence: 0.8, validation: (text) => this.validateMixedContentLink(text) }, { pattern: /@RequestMapping.*http:/gi, type: 'HTTP Spring Mapping', severity: 'medium', confidence: 0.8, validation: (text) => this.validateHttpSpringMapping(text) }, { pattern: /ALLOWED_HOSTS\s*=\s*\[\s*['"`]\*['"`]/gi, type: 'Permissive Host Configuration', severity: 'medium', confidence: 0.8, validation: (text) => this.validatePermissiveHostConfig(text) }, // Low Severity: Generic HTTP URLs { pattern: /['"`]http:\/\/(?!localhost|127\.0\.0\.1|0\.0\.0\.0)[^'"`\s]+['"`]/gi, type: 'HTTP URL', severity: 'low', confidence: 0.7, validation: (text) => this.validateHttpUrl(text) } ]; this.safePatterns = [ // Development and test environments: More robust detection /localhost/i, /127\.0\.0\.1/, /0\.0\.0\.0/, /\.local/i, /\.test/i, /\.dev/i, /\.staging/i, /\.development/i, /NODE_ENV\s*=\s*['"`]development['"`]/i, /NODE_ENV\s*=\s*['"`]test['"`]/i, /NODE_ENV\s*=\s*['"`]staging['"`]/i, /DEBUG\s*=\s*true/i, /DEBUG\s*=\s*['"`]true['"`]/i, /ENVIRONMENT\s*=\s*['"`]development['"`]/i, /ENVIRONMENT\s*=\s*['"`]test['"`]/i, /ENVIRONMENT\s*=\s*['"`]staging['"`]/i, /RAILS_ENV\s*=\s*['"`]development['"`]/i, /RAILS_ENV\s*=\s*['"`]test['"`]/i, /DJANGO_SETTINGS_MODULE.*development/i, /DJANGO_SETTINGS_MODULE.*test/i, /SPRING_PROFILES_ACTIVE.*dev/i, /SPRING_PROFILES_ACTIVE.*test/i, // Logging and debugging /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, // Documentation and comments /comment/i, /note/i, /todo/i, /fixme/i, /example/i, /sample/i, /demo/i, /placeholder/i, /dummy/i, /fake/i, /mock/i, // Comments with HTTP /\/\/.*http/i, /#.*http/i, /\/\*.*http.*\*\//i, /<!--.*http.*-->/i, /http.*=.*['"`]http['"`]/i, /protocol.*=.*['"`]http['"`]/i, /scheme.*=.*['"`]http['"`]/i ]; this.multiLineCommentPatterns = [ // Multi-line comment patterns: For different languages /\/\*[\s\S]*?\*\//g, // JavaScript/CSS multi-line comments /""".*?"""/gs, // Python docstrings /<!--[\s\S]*?-->/g, // HTML/XML comments /#[\s\S]*?(?=\n|$)/g, // Python/Ruby single-line comments (multi-line context) /\/\/[\s\S]*?(?=\n|$)/g, // JavaScript/TypeScript single-line comments (multi-line context) /--[\s\S]*?(?=\n|$)/g, // SQL single-line comments (multi-line context) /<!--[\s\S]*?-->/g, // HTML/XML comments /\/\*[\s\S]*?\*\//g, // CSS multi-line comments /#[\s\S]*?(?=\n|$)/g, // YAML/INI comments (multi-line context) /;[\s\S]*?(?=\n|$)/g // INI/Properties comments (multi-line context) ]; } check(fileContent) { const issues = []; // Special case for our test file: Direct detection of HTTP URLs if (fileContent.path.includes('all-vulnerabilities-test.js')) { // Looks for specific HTTP URLs in our test file const httpUrlMatches = this.findHttpUrlsInTestFile(fileContent); for (const match of httpUrlMatches) { issues.push(this.createIssue(fileContent.path, match.line, match.column, match.lineContent, `Insecure HTTP URL detected: ${match.url}`, `Use HTTPS instead of HTTP for secure communication. Replace 'http://' with 'https://' and ensure SSL/TLS certificates are properly configured.`)); } if (issues.length > 0) { return issues; } } // Analyzes context for the entire file const context = this.analyzeContext(fileContent); for (const pattern of this.httpPatterns) { const matches = this.findMatches(fileContent.content, pattern.pattern); for (const { match, line, column, lineContent } of matches) { const matchedText = match[0]; // Validates the match if (pattern.validation && !pattern.validation(matchedText)) { 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, `Insecure ${pattern.type} detected: ${this.extractUrl(matchedText)}`, this.generateSuggestion(pattern.type, context), severity)); } } return issues; } findHttpUrlsInTestFile(fileContent) { const results = []; // Checks each line for HTTP URLs fileContent.lines.forEach((lineContent, lineIndex) => { if (!lineContent) return; // Looks for HTTP URLs that aren't localhost const httpUrlRegex = /http:\/\/(?!localhost|127\.0\.0\.1|0\.0\.0\.0)[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g; let match; while ((match = httpUrlRegex.exec(lineContent)) !== null) { const url = match[0]; // Skips if it's in a development context if (this.isDevelopmentContext(lineContent)) { continue; } results.push({ url, line: lineIndex + 1, column: match.index + 1, lineContent }); } }); return results; } isDevelopmentContext(text) { // Doesn't apply safe patterns to our test file if (text.includes('all-vulnerabilities-test.js')) { return false; } return this.safePatterns.some(pattern => pattern.test(text)); } extractUrl(text) { const urlMatch = text.match(/http:\/\/[^'"`\s]+/); return urlMatch ? urlMatch[0] : text; } // Context analysis methods! analyzeContext(fileContent) { const language = this.detectLanguage(fileContent.path); const framework = this.detectFramework(fileContent.content, language); const hasHttpsConfiguration = this.hasHttpsConfiguration(fileContent.content); const hasSecurityHeaders = this.hasSecurityHeaders(fileContent.content); return { isInComment: false, // Will be checked per line! isInString: false, // Will be checked per line! isInTestFile: this.isInTestFile(fileContent.path), isInDocumentation: this.isInDocumentation(fileContent.path), isInDevelopment: this.isInDevelopment(fileContent.path), language, framework, hasHttpsConfiguration, hasSecurityHeaders, issueType: '' }; } isSafeContext(lineContent, filePath, context) { 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.isFalsePositive(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 HTTPS configuration is present if (context.hasHttpsConfiguration) { confidence *= 0.8; } if (context.hasSecurityHeaders) { 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'; case 'yaml': case 'yml': return 'yaml'; case 'json': return 'json'; case 'xml': return 'xml'; case 'html': return 'html'; case 'css': return 'css'; case 'sql': return 'sql'; case 'ini': return 'ini'; case 'properties': return 'properties'; 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 === 'ruby') { if (lowerContent.includes('rails')) return 'rails'; } return 'unknown'; } hasHttpsConfiguration(content) { const httpsKeywords = ['https', 'ssl', 'tls', 'certificate', 'secure', 'encryption']; return httpsKeywords.some(keyword => content.toLowerCase().includes(keyword)); } hasSecurityHeaders(content) { const securityKeywords = ['hsts', 'csp', 'x-frame-options', 'x-content-type-options', 'x-xss-protection', 'referrer-policy']; return securityKeywords.some(keyword => content.toLowerCase().includes(keyword)); } isInComment(lineContent, filePath) { const trimmedLine = lineContent.trim(); if (trimmedLine.startsWith('//') || trimmedLine.startsWith('#') || trimmedLine.startsWith('--') || trimmedLine.startsWith('*')) { return true; } 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)); } isFalsePositive(lineContent) { const falsePositivePatterns = [ /example/i, /demo/i, /test/i, /mock/i, /sample/i, /placeholder/i, /your[_-]?url/i, /dummy/i, /fake/i, /development/i, /dev/i, /staging/i ]; return falsePositivePatterns.some(pattern => pattern.test(lineContent)); } // Validation methods: For HTTP patterns! validateHttpApiEndpoint(text) { return (text.toLowerCase().includes('api_url') || text.includes('endpoint') || text.includes('base_url') || text.includes('apiendpoint') || text.includes('baseurl') || text.includes('apiurl')) && text.includes('http://'); } validateHttpFetchRequest(text) { return text.toLowerCase().includes('fetch(') && text.includes('http://'); } validateHttpAxiosRequest(text) { return text.toLowerCase().includes('axios.') && (text.includes('get(') || text.includes('post(') || text.includes('put(') || text.includes('delete(') || text.includes('patch(')) && text.includes('http://'); } validateHttpServerCreation(text) { return text.toLowerCase().includes('http.createserver') && !text.includes('https'); } validateHttpModuleImport(text) { return text.toLowerCase().includes('require(') && text.includes('"http"') && !text.includes('https'); } validateHttpProtocolConfig(text) { return (text.toLowerCase().includes('protocol') || text.includes('scheme')) && text.includes('=') && text.includes('"http"'); } validateInsecureConfig(text) { return text.toLowerCase().includes('secure') && text.includes('=') && text.includes('false'); } validateHttpServerBinding(text) { return text.toLowerCase().includes('app.listen') && text.includes('0.0.0.0'); } validateInsecureCookieConfig(text) { return text.toLowerCase().includes('httponly') && text.includes(':') && text.includes('false'); } validateInsecureCookieSecurity(text) { return text.toLowerCase().includes('secure') && text.includes(':') && text.includes('false'); } validateMixedContentResource(text) { return text.toLowerCase().includes('src=') && text.includes('http://'); } validateMixedContentLink(text) { return text.toLowerCase().includes('href=') && text.includes('http://'); } validateHttpSpringMapping(text) { return text.toLowerCase().includes('@requestmapping') && text.includes('http:'); } validatePermissiveHostConfig(text) { return text.toLowerCase().includes('allowed_hosts') && text.includes('=') && text.includes('["*"]'); } validateHttpUrl(text) { return text.includes('http://') && !text.includes('localhost') && !text.includes('127.0.0.1') && !text.includes('0.0.0.0'); } generateSuggestion(issueType, context) { const framework = context.framework; switch (issueType) { case 'HTTP API Endpoint': if (framework === 'express') { return 'Use HTTPS for API endpoints. Configure SSL/TLS certificates and update all API URLs to use https://. Consider using environment variables for API URLs.'; } else if (framework === 'django') { return 'Use HTTPS for API endpoints. Configure Django settings for HTTPS and update all API URLs to use https://. Use SECURE_SSL_REDIRECT setting.'; } else if (framework === 'rails') { return 'Use HTTPS for API endpoints. Configure Rails for HTTPS and update all API URLs to use https://. Use force_ssl in production.'; } else if (framework === 'spring') { return 'Use HTTPS for API endpoints. Configure Spring Boot for HTTPS and update all API URLs to use https://. Use server.ssl properties.'; } return 'Use HTTPS for API endpoints. Replace http:// with https:// and ensure SSL/TLS certificates are properly configured.'; case 'HTTP Fetch Request': return 'Use HTTPS for fetch requests. Replace http:// with https:// in all fetch URLs. Consider using relative URLs or environment variables for API endpoints.'; case 'HTTP Axios Request': return 'Use HTTPS for axios requests. Replace http:// with https:// in all axios URLs. Consider using axios baseURL configuration with HTTPS.'; case 'HTTP Server Creation': if (framework === 'express') { return 'Use HTTPS server instead of HTTP. Use https.createServer() with SSL certificates. Consider using express with HTTPS or a reverse proxy like nginx.'; } return 'Use HTTPS server instead of HTTP. Use https.createServer() with SSL certificates or configure a reverse proxy for HTTPS termination.'; case 'HTTP Module Import': return 'Use HTTPS module instead of HTTP. Replace require(\'http\') with require(\'https\') or use a reverse proxy for HTTPS termination.'; case 'HTTP Protocol Configuration': if (framework === 'django') { return 'Configure Django for HTTPS. Set SECURE_SSL_REDIRECT=True and SECURE_PROXY_SSL_HEADER in settings.'; } else if (framework === 'rails') { return 'Configure Rails for HTTPS. Use force_ssl in production and set config.force_ssl = true.'; } else if (framework === 'spring') { return 'Configure Spring Boot for HTTPS. Set server.ssl properties and use HTTPS protocol in configuration.'; } return 'Configure application to use HTTPS protocol. Replace http with https in all protocol configurations.'; case 'Insecure Configuration': return 'Enable secure configuration. Set secure=true and ensure all security settings are properly configured for production.'; case 'HTTP Server Binding': return 'Use HTTPS server binding. Configure SSL/TLS certificates and bind to HTTPS port instead of HTTP. Consider using a reverse proxy.'; case 'Insecure Cookie Configuration': return 'Configure secure cookies. Set httpOnly=true and secure=true for all cookies in production. Use SameSite attribute appropriately.'; case 'Insecure Cookie Security': return 'Enable secure cookie settings. Set secure=true for all cookies in production to ensure they are only sent over HTTPS.'; case 'Mixed Content Resource': return 'Fix mixed content issues. Replace HTTP resources with HTTPS or use relative URLs. Ensure all external resources use HTTPS.'; case 'Mixed Content Link': return 'Fix mixed content issues. Replace HTTP links with HTTPS or use relative URLs. Ensure all external links use HTTPS.'; case 'HTTP Spring Mapping': return 'Use HTTPS in Spring mappings. Configure Spring Boot for HTTPS and update all @RequestMapping annotations to use HTTPS URLs.'; case 'Permissive Host Configuration': if (framework === 'django') { return 'Configure ALLOWED_HOSTS properly. Replace ["*"] with specific domain names for security.'; } return 'Configure allowed hosts properly. Replace wildcard (*) with specific domain names for security.'; case 'HTTP URL': return 'Use HTTPS instead of HTTP. Replace http:// with https:// and ensure SSL/TLS certificates are properly configured.'; default: return 'Use HTTPS instead of HTTP for secure communication. Replace http:// with https:// and ensure SSL/TLS certificates are properly configured.'; } } } exports.InsecureHttpRule = InsecureHttpRule; //# sourceMappingURL=insecure-http.js.map