UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

234 lines (233 loc) 8.46 kB
"use strict"; /** * Abstract base class for security vulnerability detectors */ Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseDetector = void 0; const PatternMatcher_1 = require("../utils/PatternMatcher"); const SecurityContext_1 = require("../utils/SecurityContext"); const pathUtils_1 = require("../../../utils/common/pathUtils"); const general_constants_1 = require("../constants/general.constants"); class BaseDetector { constructor(detectorName, vulnerabilityType, defaultSeverity, patterns = []) { this.detectorName = detectorName; this.vulnerabilityType = vulnerabilityType; this.defaultSeverity = defaultSeverity; this.patterns = patterns; } /** * Apply text-based pattern matching to file content */ applyPatternMatching(content, filePath, patterns = this.patterns) { const results = []; for (const pattern of patterns) { if (this.shouldApplyPattern(pattern, filePath)) { const result = PatternMatcher_1.PatternMatcher.applyPattern(pattern, content, filePath); if (result.matches.length > 0) { results.push(result); } } } return results; } /** * Apply AST-based analysis to TypeScript source files */ applyASTAnalysis(sourceFile, filePath, customAnalyzer) { if (customAnalyzer) { return customAnalyzer(sourceFile, filePath); } return []; } /** * Create a vulnerability instance with proper metadata */ createVulnerability(filePath, location, context, description, severity = this.defaultSeverity, confidence = "high", metadata) { return { id: this.generateVulnerabilityId(filePath, location, this.vulnerabilityType), type: this.vulnerabilityType, severity, confidence, description, filePath, location, context, metadata, detectedAt: Date.now(), }; } /** * Convert pattern match results to vulnerabilities */ convertPatternMatchesToVulnerabilities(patternResults, additionalValidator) { const vulnerabilities = []; for (const result of patternResults) { // Apply additional validation if provided if (additionalValidator && !additionalValidator(result)) { continue; } for (const match of result.matches) { const location = { line: match.line, column: match.column, endLine: match.line, endColumn: match.column + match.match.length, }; const context = { code: match.match, surroundingContext: match.context, functionName: this.extractFunctionName(match.context || ""), componentName: this.extractComponentName(result.filePath), }; const vulnerability = this.createVulnerability(result.filePath, location, context, result.pattern.description, result.pattern.severity, result.pattern.confidence, { patternId: result.pattern.id, matchedGroups: match.groups, }); vulnerabilities.push(vulnerability); } } return vulnerabilities; } /** * Filter files that should be analyzed by this detector */ filterRelevantFiles(scanResult, fileExtensions, excludePatterns) { let relevantFiles = scanResult.filePaths; // Filter by file extensions if (fileExtensions && fileExtensions.length > 0) { relevantFiles = relevantFiles.filter((filePath) => fileExtensions.some((ext) => filePath.endsWith(ext))); } // Exclude files matching patterns if (excludePatterns && excludePatterns.length > 0) { relevantFiles = relevantFiles.filter((filePath) => !excludePatterns.some((pattern) => { const regex = new RegExp(pattern); return regex.test(filePath); })); } return relevantFiles; } /** * Get file context information for security analysis */ getFileContext(filePath, content) { return SecurityContext_1.SecurityContext.analyzeFileContext(filePath, content); } /** * Check if a pattern should be applied to a specific file */ shouldApplyPattern(pattern, filePath) { if (pattern.fileTypes && pattern.fileTypes.length > 0) { const fileExtension = filePath.split(".").pop(); return pattern.fileTypes.includes(`.${fileExtension}`); } return pattern.enabled; } /** * Generate a unique ID for a vulnerability */ generateVulnerabilityId(filePath, location, type) { const input = `${filePath}:${location.line}:${location.column}:${type}`; return pathUtils_1.PathUtils.generateHash(input).substring(0, 16); } /** * Extract function name from context */ extractFunctionName(context) { // Simple heuristic to find function names in context const functionPatterns = [ /function\s+(\w+)/, /const\s+(\w+)\s*=/, /(\w+)\s*:\s*\(/, /(\w+)\s*\(/, ]; for (const pattern of functionPatterns) { const match = context.match(pattern); if (match && match[1]) { return match[1]; } } return undefined; } /** * Extract component name from file path */ extractComponentName(filePath) { const fileName = filePath.split("/").pop(); if (!fileName) return undefined; const nameWithoutExtension = fileName.replace(/\.(tsx?|jsx?)$/, ""); // Return component name if it starts with uppercase (React convention) if (/^[A-Z]/.test(nameWithoutExtension)) { return nameWithoutExtension; } return undefined; } /** * Validate vulnerability before adding to results */ validateVulnerability(vulnerability) { // Basic validation checks if (!vulnerability.filePath || !vulnerability.description) { return false; } if (vulnerability.location.line < 1 || vulnerability.location.column < 1) { return false; } if (!vulnerability.context.code.trim()) { return false; } return true; } /** * Apply confidence adjustments based on context */ adjustConfidenceBasedOnContext(vulnerability, fileContext) { let confidence = vulnerability.confidence; // Lower confidence for test files if (fileContext.fileType === "test") { confidence = this.lowerConfidence(confidence); } // Higher confidence for security-sensitive contexts if (fileContext.riskContexts.includes("authentication") || fileContext.riskContexts.includes("authorization")) { confidence = this.raiseConfidence(confidence); } return confidence; } /** * Helper to lower confidence level */ lowerConfidence(confidence) { const levels = ["low", "medium", "high"]; const currentIndex = levels.indexOf(confidence); return levels[Math.max(0, currentIndex - 1)]; } /** * Helper to raise confidence level */ raiseConfidence(confidence) { const levels = ["low", "medium", "high"]; const currentIndex = levels.indexOf(confidence); return levels[Math.min(levels.length - 1, currentIndex + 1)]; } /** * Check if context suggests this is test code */ isInTestContext(context) { return general_constants_1.TEST_INDICATORS.some((indicator) => context.includes(indicator)); } /** * Check if text is in a comment */ isInComment(context, text) { const lines = context.split("\n"); for (const line of lines) { if (line.includes(text) && (line.trim().startsWith("//") || line.trim().startsWith("*"))) { return true; } } return false; } } exports.BaseDetector = BaseDetector;