UNPKG

bugnitor-security-scanner

Version:

AI-Era Security Scanner: Intelligent automated security review agent specializing in AI-generated vulnerability patterns

505 lines 21.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.DependencyAnalyzer = void 0; const fs = __importStar(require("fs")); const path = __importStar(require("path")); // Known vulnerable packages (simplified - in production would use CVE database) const knownVulnerabilities = { 'lodash': [ { versionRange: '<4.17.12', vulnerability: { id: 'CVE-2019-10744', title: 'Prototype Pollution', description: 'Lodash versions prior to 4.17.12 are vulnerable to Prototype Pollution', severity: 'high', cwe: 'CWE-1321', cvss: 7.4, publishedDate: '2019-07-26' } } ], 'log4j-core': [ { versionRange: '>=2.0-beta9 <2.12.2', vulnerability: { id: 'CVE-2021-44228', title: 'Log4Shell Remote Code Execution', description: 'Apache Log4j2 JNDI features do not protect against attacker controlled LDAP', severity: 'critical', cwe: 'CWE-917', cvss: 10.0, publishedDate: '2021-12-09' } } ], 'serialize-javascript': [ { versionRange: '<3.1.0', vulnerability: { id: 'CVE-2020-7660', title: 'Cross-site Scripting (XSS)', description: 'serialize-javascript prior to 3.1.0 allows remote attackers to inject XSS', severity: 'medium', cwe: 'CWE-79', cvss: 5.4, publishedDate: '2020-06-01' } } ], 'axios': [ { versionRange: '>=0.8.1 <0.21.1', vulnerability: { id: 'CVE-2020-28168', title: 'Server-Side Request Forgery (SSRF)', description: 'Axios NPM package contains a Server-Side Request Forgery (SSRF) vulnerability', severity: 'medium', cwe: 'CWE-918', cvss: 5.9, publishedDate: '2020-11-06' } } ], 'express': [ { versionRange: '<4.17.1', vulnerability: { id: 'CVE-2019-5413', title: 'Open Redirect', description: 'Express.js redirect() method vulnerable to open redirect', severity: 'medium', cwe: 'CWE-601', cvss: 4.3, publishedDate: '2019-04-26' } } ] }; class DependencyAnalyzer { async analyzeDependencies(projectPath) { const findings = []; const vulnerableDependencies = []; const outdatedDependencies = []; // Analyze package.json (Node.js) const packageJsonPath = path.join(projectPath, 'package.json'); if (fs.existsSync(packageJsonPath)) { const result = await this.analyzePackageJson(packageJsonPath); findings.push(...result.findings); vulnerableDependencies.push(...result.vulnerableDependencies); outdatedDependencies.push(...result.outdatedDependencies); } // Analyze requirements.txt (Python) const requirementsPath = path.join(projectPath, 'requirements.txt'); if (fs.existsSync(requirementsPath)) { const result = await this.analyzeRequirementsTxt(requirementsPath); findings.push(...result.findings); } // Analyze pom.xml (Java Maven) const pomXmlPath = path.join(projectPath, 'pom.xml'); if (fs.existsSync(pomXmlPath)) { const result = await this.analyzePomXml(pomXmlPath); findings.push(...result.findings); } // Analyze Gemfile (Ruby) const gemfilePath = path.join(projectPath, 'Gemfile'); if (fs.existsSync(gemfilePath)) { const result = await this.analyzeGemfile(gemfilePath); findings.push(...result.findings); } // Analyze go.mod (Go) const goModPath = path.join(projectPath, 'go.mod'); if (fs.existsSync(goModPath)) { const result = await this.analyzeGoMod(goModPath); findings.push(...result.findings); } return { findings, vulnerableDependencies, outdatedDependencies }; } async analyzePackageJson(filePath) { const findings = []; const vulnerableDependencies = []; const outdatedDependencies = []; try { const content = fs.readFileSync(filePath, 'utf-8'); const packageJson = JSON.parse(content); // Check dependencies and devDependencies const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies }; for (const [name, version] of Object.entries(allDeps)) { const versionStr = version; // Check for vulnerable versions if (knownVulnerabilities[name]) { for (const vuln of knownVulnerabilities[name]) { if (this.isVersionVulnerable(versionStr, vuln.versionRange)) { vulnerableDependencies.push({ name, version: versionStr, vulnerability: vuln.vulnerability }); findings.push({ type: 'dependency', category: 'Vulnerable Dependency', severity: vuln.vulnerability.severity, title: `Vulnerable dependency: ${name}`, description: `${name}@${versionStr} has known vulnerability: ${vuln.vulnerability.title}`, file: path.basename(filePath), recommendation: `Update ${name} to a secure version to fix ${vuln.vulnerability.id}`, confidence: 0.95, cwe: vuln.vulnerability.cwe, impact: vuln.vulnerability.description, effort: 'low' }); } } } // Check for potentially unsafe package patterns if (this.isSuspiciousPackage(name)) { findings.push({ type: 'dependency', category: 'Suspicious Dependency', severity: 'medium', title: `Suspicious package name: ${name}`, description: `Package name "${name}" appears suspicious and may be a typosquatting attempt`, file: path.basename(filePath), recommendation: `Verify the package name and publisher. Consider using official packages.`, confidence: 0.6, impact: 'Potential malicious code execution', effort: 'low' }); } } // Check for security-related configurations if (packageJson.scripts) { for (const [scriptName, scriptCommand] of Object.entries(packageJson.scripts)) { const command = scriptCommand; if (this.hasInsecureScriptCommand(command)) { findings.push({ type: 'config', category: 'Insecure NPM Script', severity: 'medium', title: `Insecure script command: ${scriptName}`, description: `NPM script "${scriptName}" contains potentially insecure commands`, file: path.basename(filePath), code: `"${scriptName}": "${command}"`, recommendation: 'Review script commands for security implications. Avoid downloading and executing remote scripts.', confidence: 0.7, impact: 'Potential code execution during npm install', effort: 'low' }); } } } } catch (error) { findings.push({ type: 'config', category: 'Malformed Configuration', severity: 'low', title: 'Malformed package.json', description: `Cannot parse package.json: ${error}`, file: path.basename(filePath), recommendation: 'Fix JSON syntax errors in package.json', confidence: 0.9, impact: 'Build failures, dependency resolution issues', effort: 'low' }); } return { findings, vulnerableDependencies, outdatedDependencies }; } async analyzeRequirementsTxt(filePath) { const findings = []; const vulnerableDependencies = []; const outdatedDependencies = []; try { const content = fs.readFileSync(filePath, 'utf-8'); const lines = content.split('\n').filter(line => line.trim() && !line.startsWith('#')); for (const line of lines) { // Check for insecure package sources if (line.includes('http://') && !line.includes('https://')) { findings.push({ type: 'dependency', category: 'Insecure Package Source', severity: 'medium', title: 'HTTP package source', description: 'Package installed from insecure HTTP source', file: path.basename(filePath), code: line.trim(), recommendation: 'Use HTTPS sources for package installation', confidence: 0.8, impact: 'Man-in-the-middle attacks during package installation', effort: 'low' }); } // Check for unpinned versions if (line.includes('>=') || line.includes('>') || line.includes('*')) { findings.push({ type: 'dependency', category: 'Unpinned Dependency Version', severity: 'low', title: 'Unpinned dependency version', description: 'Dependency version not pinned, may lead to inconsistent builds', file: path.basename(filePath), code: line.trim(), recommendation: 'Pin dependency versions for reproducible builds', confidence: 0.6, impact: 'Build inconsistencies, potential breaking changes', effort: 'low' }); } } } catch (error) { findings.push({ type: 'config', category: 'File Access Error', severity: 'low', title: 'Cannot read requirements.txt', description: `Error reading requirements.txt: ${error}`, file: path.basename(filePath), recommendation: 'Ensure requirements.txt is accessible and properly formatted', confidence: 0.9, impact: 'Dependency analysis incomplete', effort: 'low' }); } return { findings, vulnerableDependencies, outdatedDependencies }; } async analyzePomXml(filePath) { const findings = []; const vulnerableDependencies = []; const outdatedDependencies = []; try { const content = fs.readFileSync(filePath, 'utf-8'); // Check for Log4j vulnerability if (content.includes('log4j-core') && !content.includes('2.17.0')) { findings.push({ type: 'dependency', category: 'Critical Vulnerability', severity: 'critical', title: 'Log4Shell vulnerability (CVE-2021-44228)', description: 'Project uses vulnerable version of Log4j', file: path.basename(filePath), recommendation: 'Update Log4j to version 2.17.0 or later immediately', confidence: 0.9, cwe: 'CWE-917', impact: 'Remote code execution via JNDI injection', effort: 'low' }); } // Check for insecure repositories if (content.includes('<repository>') && content.includes('http://')) { findings.push({ type: 'config', category: 'Insecure Repository', severity: 'medium', title: 'Insecure Maven repository', description: 'Maven repository configured with HTTP instead of HTTPS', file: path.basename(filePath), recommendation: 'Use HTTPS for all Maven repositories', confidence: 0.8, impact: 'Man-in-the-middle attacks during dependency download', effort: 'low' }); } } catch (error) { findings.push({ type: 'config', category: 'File Access Error', severity: 'low', title: 'Cannot read pom.xml', description: `Error reading pom.xml: ${error}`, file: path.basename(filePath), recommendation: 'Ensure pom.xml is accessible and properly formatted', confidence: 0.9, impact: 'Dependency analysis incomplete', effort: 'low' }); } return { findings, vulnerableDependencies, outdatedDependencies }; } async analyzeGemfile(filePath) { const findings = []; const vulnerableDependencies = []; const outdatedDependencies = []; try { const content = fs.readFileSync(filePath, 'utf-8'); // Check for insecure gem sources if (content.includes('source \'http://')) { findings.push({ type: 'dependency', category: 'Insecure Gem Source', severity: 'medium', title: 'Insecure gem source', description: 'Gem source configured with HTTP instead of HTTPS', file: path.basename(filePath), recommendation: 'Use HTTPS for gem sources', confidence: 0.8, impact: 'Man-in-the-middle attacks during gem installation', effort: 'low' }); } } catch (error) { findings.push({ type: 'config', category: 'File Access Error', severity: 'low', title: 'Cannot read Gemfile', description: `Error reading Gemfile: ${error}`, file: path.basename(filePath), recommendation: 'Ensure Gemfile is accessible and properly formatted', confidence: 0.9, impact: 'Dependency analysis incomplete', effort: 'low' }); } return { findings, vulnerableDependencies, outdatedDependencies }; } async analyzeGoMod(filePath) { const findings = []; const vulnerableDependencies = []; const outdatedDependencies = []; try { const content = fs.readFileSync(filePath, 'utf-8'); // Check for insecure module paths if (content.includes('replace ') && content.includes('=>') && content.includes('http://')) { findings.push({ type: 'dependency', category: 'Insecure Module Source', severity: 'medium', title: 'Insecure Go module source', description: 'Go module replacement uses HTTP instead of HTTPS', file: path.basename(filePath), recommendation: 'Use HTTPS for Go module sources', confidence: 0.8, impact: 'Man-in-the-middle attacks during module download', effort: 'low' }); } } catch (error) { findings.push({ type: 'config', category: 'File Access Error', severity: 'low', title: 'Cannot read go.mod', description: `Error reading go.mod: ${error}`, file: path.basename(filePath), recommendation: 'Ensure go.mod is accessible and properly formatted', confidence: 0.9, impact: 'Dependency analysis incomplete', effort: 'low' }); } return { findings, vulnerableDependencies, outdatedDependencies }; } isVersionVulnerable(version, versionRange) { // Simplified version comparison - in production would use semver library const cleanVersion = version.replace(/[\^~]/g, ''); if (versionRange.includes('<')) { const targetVersion = versionRange.replace('<', '').trim(); return this.compareVersions(cleanVersion, targetVersion) < 0; } if (versionRange.includes('>=') && versionRange.includes('<')) { const [minVersion, maxVersion] = versionRange.split('<'); const min = minVersion.replace('>=', '').trim(); const max = maxVersion.trim(); return this.compareVersions(cleanVersion, min) >= 0 && this.compareVersions(cleanVersion, max) < 0; } return false; } compareVersions(v1, v2) { const parts1 = v1.split('.').map(Number); const parts2 = v2.split('.').map(Number); for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) { const part1 = parts1[i] || 0; const part2 = parts2[i] || 0; if (part1 < part2) return -1; if (part1 > part2) return 1; } return 0; } isSuspiciousPackage(name) { const suspiciousPatterns = [ /^[a-z]{1,3}$/, // Very short names /\d{10,}/, // Long numeric sequences /^(test|temp|demo)-/, // Test packages /-[a-z]{1,2}$/, // Single letter suffixes ]; return suspiciousPatterns.some(pattern => pattern.test(name)); } hasInsecureScriptCommand(command) { const insecurePatterns = [ /curl.*\|.*sh/, // Pipe to shell /wget.*\|.*sh/, // Pipe to shell /http:\/\/[^\/]*\//, // HTTP downloads /sudo/, // Sudo commands /rm\s+-rf\s+\//, // Dangerous file operations ]; return insecurePatterns.some(pattern => pattern.test(command)); } } exports.DependencyAnalyzer = DependencyAnalyzer; //# sourceMappingURL=dependency-analyzer.js.map