UNPKG

vibe-guard

Version:

🛡️ Vibe-Guard Security Scanner - Catch security issues before they catch you!

98 lines 4.45 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MissingAuthenticationRule = void 0; const types_1 = require("../types"); class MissingAuthenticationRule extends types_1.BaseRule { constructor() { super(...arguments); this.name = 'missing-authentication'; this.description = 'Detects potentially unprotected routes and endpoints'; this.severity = 'high'; this.routePatterns = [ // Express.js { pattern: /app\.(get|post|put|delete|patch)\s*\(\s*['"`]([^'"`]+)['"`]\s*,\s*(?!.*auth|.*login|.*verify|.*middleware)/gi, framework: 'Express' }, { pattern: /router\.(get|post|put|delete|patch)\s*\(\s*['"`]([^'"`]+)['"`]\s*,\s*(?!.*auth|.*login|.*verify|.*middleware)/gi, framework: 'Express' }, // Next.js API routes { pattern: /export\s+(?:default\s+)?(?:async\s+)?function\s+handler\s*\([^)]*\)\s*\{(?![\s\S]*auth|[\s\S]*login|[\s\S]*verify)/gi, framework: 'Next.js' }, // Flask { pattern: /@app\.route\s*\(\s*['"`]([^'"`]+)['"`](?:[^)]*)\)\s*\n\s*def\s+\w+\s*\([^)]*\)\s*:(?![\s\S]*auth|[\s\S]*login|[\s\S]*verify)/gi, framework: 'Flask' }, // FastAPI { pattern: /@app\.(get|post|put|delete|patch)\s*\(\s*['"`]([^'"`]+)['"`]\s*\)\s*\n\s*(?:async\s+)?def\s+\w+\s*\([^)]*\)\s*:(?![\s\S]*auth|[\s\S]*login|[\s\S]*verify)/gi, framework: 'FastAPI' }, // Laravel { pattern: /Route::(get|post|put|delete|patch)\s*\(\s*['"`]([^'"`]+)['"`]\s*,\s*(?!.*auth|.*login|.*verify|.*middleware)/gi, framework: 'Laravel' } ]; this.protectedPatterns = [ /auth/i, /login/i, /verify/i, /middleware/i, /guard/i, /protect/i, /secure/i, /jwt/i, /token/i, /session/i, /permission/i, /role/i ]; this.publicEndpoints = [ /\/public/i, /\/health/i, /\/ping/i, /\/status/i, /\/docs/i, /\/swagger/i, /\/api-docs/i, /\/favicon/i, /\/robots\.txt/i, /\/sitemap/i, /\/login/i, /\/register/i, /\/signup/i, /\/forgot-password/i, /\/reset-password/i ]; } check(fileContent) { const issues = []; for (const { pattern, framework } of this.routePatterns) { const matches = this.findMatches(fileContent.content, pattern); for (const { match, line, column, lineContent } of matches) { const route = this.extractRoute(match); // Skip if it's a known public endpoint if (this.isPublicEndpoint(route)) { continue; } // Skip if the surrounding code suggests authentication if (this.hasAuthenticationContext(fileContent.content, line)) { continue; } issues.push(this.createIssue(fileContent.path, line, column, lineContent, `Potentially unprotected ${framework} route: ${route}`, `Add authentication middleware or verify that this endpoint should be publicly accessible. Consider using authentication guards, middleware, or decorators.`)); } } return issues; } extractRoute(match) { // Try to find the route path in the match for (let i = 1; i < match.length; i++) { const matchGroup = match[i]; if (matchGroup && matchGroup.startsWith('/')) { return matchGroup; } } return match[0] || ''; } isPublicEndpoint(route) { return this.publicEndpoints.some(pattern => pattern.test(route)); } hasAuthenticationContext(content, lineNumber) { const lines = content.split('\n'); const contextRange = 10; // Check 10 lines before and after const startLine = Math.max(0, lineNumber - contextRange - 1); const endLine = Math.min(lines.length, lineNumber + contextRange); const contextLines = lines.slice(startLine, endLine).join('\n'); return this.protectedPatterns.some(pattern => pattern.test(contextLines)); } } exports.MissingAuthenticationRule = MissingAuthenticationRule; //# sourceMappingURL=missing-authentication.js.map