UNPKG

@bhupesh123/security

Version:

Security vulnerability analysis microservice for GitHub repositories

350 lines 13.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SecurityAnalyzer = void 0; const rest_1 = require("@octokit/rest"); class SecurityAnalyzer { constructor(githubToken) { this.octokit = new rest_1.Octokit({ auth: githubToken }); } /** * Analyze security vulnerabilities in a GitHub repository */ async analyzeRepository(owner, repo, branch = 'main') { const issues = []; const dependencies = []; const bySeverity = { critical: 0, high: 0, medium: 0, low: 0, info: 0 }; const byCategory = {}; const byFile = {}; try { // Check for security advisories const advisories = await this.getSecurityAdvisories(owner, repo); dependencies.push(...advisories); // Check for vulnerable dependencies const depVulns = await this.checkDependencies(owner, repo, branch); dependencies.push(...depVulns); // Analyze source code for security issues const sourceFiles = await this.getSourceFiles(owner, repo, branch); for (const file of sourceFiles) { const fileIssues = await this.analyzeFile(owner, repo, file, branch); issues.push(...fileIssues); // Update statistics fileIssues.forEach(issue => { bySeverity[issue.severity]++; byCategory[issue.category] = (byCategory[issue.category] || 0) + 1; byFile[issue.file] = (byFile[issue.file] || 0) + 1; }); } // Check for secrets const secretIssues = await this.scanForSecrets(owner, repo, branch); issues.push(...secretIssues); secretIssues.forEach(issue => { bySeverity[issue.severity]++; byCategory[issue.category] = (byCategory[issue.category] || 0) + 1; byFile[issue.file] = (byFile[issue.file] || 0) + 1; }); // Calculate security score (0-100) const securityScore = this.calculateSecurityScore(bySeverity, dependencies.length); return { totalIssues: issues.length, criticalIssues: bySeverity.critical, highIssues: bySeverity.high, mediumIssues: bySeverity.medium, lowIssues: bySeverity.low, issues, dependencies, summary: { bySeverity, byCategory, byFile, }, securityScore, timestamp: new Date().toISOString(), }; } catch (error) { throw new Error(`Failed to analyze repository: ${error}`); } } /** * Get security advisories from GitHub */ async getSecurityAdvisories(owner, repo) { const vulnerabilities = []; try { // Use GitHub's vulnerability alerts API const { data: alerts } = await this.octokit.request('GET /repos/{owner}/{repo}/vulnerability-alerts', { owner, repo }); // Note: This requires special permissions } catch (error) { // Vulnerability alerts may not be accessible console.log('Unable to fetch vulnerability alerts'); } return vulnerabilities; } /** * Check dependencies for vulnerabilities */ async checkDependencies(owner, repo, branch) { const vulnerabilities = []; try { // Check package.json const packageJson = await this.getFile(owner, repo, 'package.json', branch); if (packageJson) { const deps = JSON.parse(packageJson); const allDeps = { ...deps.dependencies, ...deps.devDependencies }; for (const [pkg, version] of Object.entries(allDeps)) { const vulns = await this.checkPackageVulnerability(pkg, version); vulnerabilities.push(...vulns); } } } catch { } try { // Check requirements.txt const requirements = await this.getFile(owner, repo, 'requirements.txt', branch); if (requirements) { const lines = requirements.split('\n'); for (const line of lines) { const match = line.match(/^([a-zA-Z0-9-_]+)==(.+)$/); if (match) { const [, pkg, version] = match; const vulns = await this.checkPackageVulnerability(pkg, version); vulnerabilities.push(...vulns); } } } } catch { } return vulnerabilities; } /** * Check if a package has known vulnerabilities */ async checkPackageVulnerability(packageName, version) { // This would integrate with vulnerability databases // For now, return empty array return []; } /** * Analyze a single file for security issues */ async analyzeFile(owner, repo, filePath, branch) { try { const content = await this.getFile(owner, repo, filePath, branch); if (!content) return []; return this.scanContent(content, filePath); } catch (error) { console.error(`Error analyzing file ${filePath}:`, error); return []; } } /** * Scan content for security issues */ scanContent(content, filePath) { const issues = []; const lines = content.split('\n'); lines.forEach((line, index) => { // SQL Injection if (/execute\s*\(\s*["'`].*\$\{.*\}.*["'`]/.test(line) || /query\s*\(\s*["'`].*\+.*["'`]/.test(line)) { issues.push({ file: filePath, line: index + 1, severity: 'high', category: 'sql-injection', title: 'Potential SQL Injection', description: 'SQL query constructed using string concatenation or template literals', cwe: 'CWE-89', recommendation: 'Use parameterized queries or prepared statements', source: line.trim(), }); } // XSS if (/innerHTML\s*=/.test(line) || /dangerouslySetInnerHTML/.test(line)) { issues.push({ file: filePath, line: index + 1, severity: 'medium', category: 'xss', title: 'Potential XSS Vulnerability', description: 'Direct HTML injection detected', cwe: 'CWE-79', recommendation: 'Sanitize user input before rendering', source: line.trim(), }); } // Command Injection if (/exec\s*\(/.test(line) || /spawn\s*\(/.test(line) || /system\s*\(/.test(line)) { issues.push({ file: filePath, line: index + 1, severity: 'critical', category: 'command-injection', title: 'Potential Command Injection', description: 'Execution of system commands detected', cwe: 'CWE-78', recommendation: 'Avoid executing system commands with user input', source: line.trim(), }); } // Hardcoded credentials if (/password\s*=\s*["'][^"']+["']/.test(line) || /api[_-]?key\s*=\s*["'][^"']+["']/.test(line)) { issues.push({ file: filePath, line: index + 1, severity: 'critical', category: 'hardcoded-credentials', title: 'Hardcoded Credentials', description: 'Credentials found in source code', cwe: 'CWE-798', recommendation: 'Use environment variables or secure vaults', source: '[REDACTED]', }); } // Insecure random if (/Math\.random\(\)/.test(line)) { issues.push({ file: filePath, line: index + 1, severity: 'low', category: 'weak-random', title: 'Weak Random Number Generator', description: 'Math.random() is not cryptographically secure', cwe: 'CWE-338', recommendation: 'Use crypto.randomBytes() for security-sensitive operations', source: line.trim(), }); } // eval usage if (/\beval\s*\(/.test(line)) { issues.push({ file: filePath, line: index + 1, severity: 'high', category: 'code-injection', title: 'Use of eval()', description: 'eval() can execute arbitrary code', cwe: 'CWE-95', recommendation: 'Avoid using eval(), use safer alternatives', source: line.trim(), }); } // Insecure HTTP if (/http:\/\//.test(line) && !line.includes('localhost')) { issues.push({ file: filePath, line: index + 1, severity: 'medium', category: 'insecure-transport', title: 'Insecure HTTP Connection', description: 'HTTP connection detected instead of HTTPS', cwe: 'CWE-319', recommendation: 'Use HTTPS for all external connections', source: line.trim(), }); } }); return issues; } /** * Scan for exposed secrets */ async scanForSecrets(owner, repo, branch) { const issues = []; try { // Check common secret files const secretFiles = ['.env', '.env.local', 'config.json', 'secrets.json']; for (const file of secretFiles) { try { const content = await this.getFile(owner, repo, file, branch); if (content) { issues.push({ file, line: 0, severity: 'critical', category: 'exposed-secrets', title: 'Exposed Secret File', description: `Secret file ${file} is committed to repository`, cwe: 'CWE-540', recommendation: 'Remove secret files from repository and add to .gitignore', }); } } catch { } } } catch (error) { console.error('Error scanning for secrets:', error); } return issues; } /** * Calculate security score (0-100) */ calculateSecurityScore(bySeverity, dependencyVulns) { let score = 100; score -= bySeverity.critical * 20; score -= bySeverity.high * 10; score -= bySeverity.medium * 5; score -= bySeverity.low * 2; score -= dependencyVulns * 3; return Math.max(0, Math.min(100, score)); } /** * Get file content */ async getFile(owner, repo, path, branch) { try { const { data } = await this.octokit.repos.getContent({ owner, repo, path, ref: branch, }); if ('content' in data) { return Buffer.from(data.content, 'base64').toString('utf-8'); } return null; } catch { return null; } } /** * Get all source files */ async getSourceFiles(owner, repo, branch, path = '') { const files = []; const extensions = ['.js', '.jsx', '.ts', '.tsx', '.py', '.java', '.go', '.php', '.rb']; try { const { data } = await this.octokit.repos.getContent({ owner, repo, path, ref: branch, }); if (Array.isArray(data)) { for (const item of data) { if (item.type === 'file' && extensions.some(ext => item.name.endsWith(ext))) { files.push(item.path); } else if (item.type === 'dir' && !item.name.startsWith('.') && item.name !== 'node_modules') { const subFiles = await this.getSourceFiles(owner, repo, branch, item.path); files.push(...subFiles); } } } } catch (error) { console.error(`Error getting source files from ${path}:`, error); } return files; } } exports.SecurityAnalyzer = SecurityAnalyzer; exports.default = SecurityAnalyzer; //# sourceMappingURL=index.js.map