UNPKG

agentic-qe

Version:

Agentic Quality Engineering Fleet System - AI-driven quality management platform

700 lines 30.3 kB
"use strict"; /** * SecurityScannerAgent - Vulnerability detection and compliance validation * * Responsibilities: * - SAST scanning (static code analysis: SonarQube, Checkmarx, Semgrep) * - DAST scanning (dynamic application security: OWASP ZAP, Burp Suite) * - Dependency scanning (vulnerable packages: npm audit, Snyk, Dependabot) * - Container scanning (Docker image vulnerabilities: Trivy, Clair) * - Compliance checking (OWASP Top 10, CWE, GDPR, SOC2) * - Security gate enforcement (block deployments on critical vulnerabilities) * - CVE monitoring (track known vulnerabilities) */ Object.defineProperty(exports, "__esModule", { value: true }); exports.SecurityScannerAgent = void 0; const BaseAgent_1 = require("./BaseAgent"); const types_1 = require("../types"); const SecurityScanner_1 = require("../utils/SecurityScanner"); class SecurityScannerAgent extends BaseAgent_1.BaseAgent { constructor(config) { super({ id: config.id || `security-scanner-${Date.now()}`, type: types_1.QEAgentType.SECURITY_SCANNER, capabilities: [ { name: 'sast-scanning', version: '2.0.0', description: 'Static application security testing (SonarQube, Checkmarx, Semgrep)' }, { name: 'dast-scanning', version: '2.0.0', description: 'Dynamic application security testing (OWASP ZAP, Burp Suite)' }, { name: 'dependency-scanning', version: '2.0.0', description: 'Vulnerable package detection (npm audit, Snyk, Dependabot)' }, { name: 'container-scanning', version: '2.0.0', description: 'Docker image vulnerability scanning (Trivy, Clair)' }, { name: 'compliance-checking', version: '2.0.0', description: 'Regulatory compliance validation (OWASP, GDPR, SOC2)' }, { name: 'security-gate-enforcement', version: '2.0.0', description: 'Block deployments on critical vulnerabilities' }, { name: 'cve-monitoring', version: '2.0.0', description: 'Real-time CVE database monitoring' } ], context: config.context, memoryStore: config.memoryStore, eventBus: config.eventBus }); this.cveDatabase = new Map(); this.scanHistory = []; this.baselineFindings = new Map(); this.config = { tools: { sast: 'semgrep', dast: 'owasp-zap', dependencies: 'npm-audit', containers: 'trivy', ...config.tools }, thresholds: { maxCriticalVulnerabilities: 0, maxHighVulnerabilities: 5, maxMediumVulnerabilities: 20, minSecurityScore: 80, ...config.thresholds }, compliance: { standards: ['OWASP-Top-10', 'CWE-25'], enforceCompliance: true, ...config.compliance }, scanScope: { includeCode: true, includeDependencies: true, includeContainers: false, includeDynamic: false, ...config.scanScope }, ...config }; // Initialize real security scanner this.realScanner = new SecurityScanner_1.RealSecurityScanner(process.cwd()); } // ============================================================================ // BaseAgent Abstract Methods Implementation // ============================================================================ async initializeComponents() { console.log(`[SecurityScanner] Initializing security scanning tools`); // Register event handlers for security coordination this.registerEventHandler({ eventType: 'test.generated', handler: async (event) => { // Automatically scan newly generated tests for security issues await this.handleTestGenerated(event.data); } }); this.registerEventHandler({ eventType: 'deployment.requested', handler: async (event) => { // Enforce security gate before deployment await this.handleDeploymentRequest(event.data); } }); this.registerEventHandler({ eventType: 'cve.published', handler: async (event) => { // Monitor new CVE publications await this.handleNewCVE(event.data); } }); // Load CVE database await this.loadCVEDatabase(); // Initialize scanning tools (mock initialization for now) await this.initializeScanningTools(); // Store initialization status await this.storeSharedMemory('status', { initialized: true, tools: this.config.tools, thresholds: this.config.thresholds }); console.log('[SecurityScanner] Initialization complete'); } async performTask(task) { console.log(`[SecurityScanner] Performing task: ${task.type}`); switch (task.type) { case 'run-security-scan': return await this.runSecurityScan(task.payload); case 'scan-dependencies': return await this.scanDependencies(task.payload); case 'scan-containers': return await this.scanContainers(task.payload); case 'check-compliance': return await this.checkCompliance(task.payload); case 'enforce-security-gate': return await this.enforceSecurityGate(task.payload); case 'generate-security-report': return await this.generateSecurityReport(task.payload); case 'update-baseline': return await this.updateSecurityBaseline(task.payload); default: throw new Error(`Unknown task type: ${task.type}`); } } async loadKnowledge() { console.log('[SecurityScanner] Loading security knowledge from memory'); try { // Restore baseline findings const savedBaseline = await this.memoryStore.retrieve('aqe/security/baselines'); if (savedBaseline && savedBaseline.findings) { this.baselineFindings = new Map(Object.entries(savedBaseline.findings)); } // Restore scan history const savedHistory = await this.memoryStore.retrieve('aqe/security/scan-history'); if (savedHistory && Array.isArray(savedHistory)) { this.scanHistory = savedHistory; } // Restore CVE database const savedCVE = await this.memoryStore.retrieve('aqe/security/cve-database'); if (savedCVE) { this.cveDatabase = new Map(Object.entries(savedCVE)); } } catch (error) { console.warn('[SecurityScanner] Could not restore full state, using defaults:', error); } } async cleanup() { console.log('[SecurityScanner] Cleaning up security scanner resources'); // Save baseline findings await this.memoryStore.store('aqe/security/baselines', { findings: Object.fromEntries(this.baselineFindings), timestamp: new Date() }); // Save scan history (keep last 50 scans) await this.memoryStore.store('aqe/security/scan-history', this.scanHistory.slice(-50)); // Save CVE database await this.memoryStore.store('aqe/security/cve-database', Object.fromEntries(this.cveDatabase)); // Clear in-memory data this.cveDatabase.clear(); this.scanHistory = []; this.baselineFindings.clear(); } // ============================================================================ // Core Security Scanning Methods // ============================================================================ async runSecurityScan(metadata) { const startTime = Date.now(); const scanId = `scan-${Date.now()}`; console.log(`[SecurityScanner] Running comprehensive security scan: ${scanId}`); const allFindings = []; // Run SAST scan if (this.config.scanScope?.includeCode) { const sastResults = await this.runSASTScan(metadata); allFindings.push(...sastResults.findings); } // Run DAST scan if (this.config.scanScope?.includeDynamic) { const dastResults = await this.runDASTScan(metadata); allFindings.push(...dastResults.findings); } // Run dependency scan if (this.config.scanScope?.includeDependencies) { const depResults = await this.scanDependencies(metadata); allFindings.push(...depResults.findings); } // Run container scan if (this.config.scanScope?.includeContainers) { const containerResults = await this.scanContainers(metadata); allFindings.push(...containerResults.findings); } // Calculate summary const summary = this.calculateSummary(allFindings); const securityScore = this.calculateSecurityScore(summary); const passed = this.evaluateSecurityGate(summary, securityScore); const result = { scanId, timestamp: new Date(), scanType: 'comprehensive', findings: allFindings, summary, securityScore, passed, duration: Date.now() - startTime }; // Store scan result this.scanHistory.push(result); await this.memoryStore.store(`aqe/security/scans/${scanId}`, result); // Emit events based on findings if (!passed) { this.emitEvent('security.scan.failed', { scanId, summary, securityScore }, 'critical'); } else { this.emitEvent('security.scan.completed', { scanId, summary, securityScore }, 'medium'); } // Alert on critical vulnerabilities const criticalFindings = allFindings.filter(f => f.severity === 'critical'); if (criticalFindings.length > 0) { this.emitEvent('security.critical.found', { scanId, count: criticalFindings.length, findings: criticalFindings }, 'critical'); } return result; } async runSASTScan(metadata) { console.log(`[SecurityScanner] Running SAST scan with ${this.config.tools?.sast}`); const startTime = Date.now(); const findings = []; try { // Determine scan target const target = metadata.path || metadata.target || 'src'; // Run ESLint security scan console.log(`[SecurityScanner] Running ESLint security scan on ${target}`); const eslintResult = await this.realScanner.runESLintScan(target); if (eslintResult.success) { findings.push(...eslintResult.findings); console.log(`[SecurityScanner] ESLint found ${eslintResult.findings.length} issues`); } else { console.warn(`[SecurityScanner] ESLint scan failed: ${eslintResult.error}`); } // Run Semgrep scan if available console.log(`[SecurityScanner] Running Semgrep SAST scan on ${target}`); const semgrepResult = await this.realScanner.runSemgrepScan(target); if (semgrepResult.success) { findings.push(...semgrepResult.findings); console.log(`[SecurityScanner] Semgrep found ${semgrepResult.findings.length} issues`); } else if (semgrepResult.error) { console.warn(`[SecurityScanner] Semgrep scan failed: ${semgrepResult.error}`); } } catch (error) { console.error('[SecurityScanner] SAST scan error:', error); } const summary = this.calculateSummary(findings); return { scanId: `sast-${Date.now()}`, timestamp: new Date(), scanType: 'sast', findings, summary, securityScore: this.calculateSecurityScore(summary), passed: summary.critical === 0, duration: Date.now() - startTime }; } async runDASTScan(metadata) { console.log(`[SecurityScanner] Running DAST scan with ${this.config.tools?.dast}`); // Mock DAST scan implementation const findings = []; // Simulate runtime vulnerability detection if (metadata.target && metadata.includeFindings !== false) { // Mock: Simulate finding vulnerabilities at runtime findings.push({ id: `dast-${Date.now()}-1`, type: 'dast', severity: 'medium', title: 'Insecure HTTP Header', description: 'Missing security headers detected', location: `${metadata.target}/api/endpoint`, remediation: 'Add security headers: X-Frame-Options, X-Content-Type-Options' }); } const summary = this.calculateSummary(findings); return { scanId: `dast-${Date.now()}`, timestamp: new Date(), scanType: 'dast', findings, summary, securityScore: this.calculateSecurityScore(summary), passed: summary.critical === 0, duration: 2000 }; } async scanDependencies(metadata) { console.log(`[SecurityScanner] Scanning dependencies with ${this.config.tools?.dependencies}`); const startTime = Date.now(); const findings = []; try { // Run NPM audit console.log('[SecurityScanner] Running NPM audit scan'); const auditResult = await this.realScanner.runNPMAuditScan(); if (auditResult.success) { findings.push(...auditResult.findings); console.log(`[SecurityScanner] NPM audit found ${auditResult.findings.length} vulnerabilities`); } else { console.warn(`[SecurityScanner] NPM audit failed: ${auditResult.error}`); } } catch (error) { console.error('[SecurityScanner] Dependency scan error:', error); } const summary = this.calculateSummary(findings); // Store dependency scan results await this.memoryStore.store('aqe/security/dependencies', { findings, timestamp: new Date(), summary }); return { scanId: `dep-${Date.now()}`, timestamp: new Date(), scanType: 'dependency', findings, summary, securityScore: this.calculateSecurityScore(summary), passed: summary.critical === 0 && summary.high <= this.config.thresholds.maxHighVulnerabilities, duration: Date.now() - startTime }; } async scanContainers(metadata) { console.log(`[SecurityScanner] Scanning containers with ${this.config.tools?.containers}`); const findings = []; // Mock container scan if (metadata.image && metadata.includeFindings !== false) { findings.push({ id: `container-${Date.now()}-1`, type: 'container', severity: 'medium', title: 'Outdated Base Image', description: 'Base image contains known vulnerabilities', location: `${metadata.image}:latest`, remediation: 'Update to latest base image version' }); } const summary = this.calculateSummary(findings); return { scanId: `container-${Date.now()}`, timestamp: new Date(), scanType: 'container', findings, summary, securityScore: this.calculateSecurityScore(summary), passed: summary.critical === 0, duration: 1500 }; } // ============================================================================ // Compliance Checking // ============================================================================ async checkCompliance(metadata) { console.log(`[SecurityScanner] Checking compliance for standards:`, this.config.compliance?.standards); const reports = []; for (const standard of this.config.compliance?.standards || []) { const report = await this.checkStandardCompliance(standard, metadata); reports.push(report); // Store compliance report await this.memoryStore.store(`aqe/security/compliance/${standard}`, report); // Emit events if (!report.passed && this.config.compliance?.enforceCompliance) { this.emitEvent('security.compliance.failed', { standard, compliance: report.overallCompliance, violations: report.requirements.filter(r => r.status === 'non-compliant').length }, 'high'); } } return reports; } async checkStandardCompliance(standard, metadata) { console.log(`[SecurityScanner] Checking ${standard} compliance`); const requirements = this.getStandardRequirements(standard); const report = { standard, requirements: [], overallCompliance: 0, passed: false }; // Check each requirement for (const req of requirements) { const status = await this.checkRequirement(req, metadata); report.requirements.push({ id: req.id, description: req.description, status }); } // Calculate overall compliance const compliantCount = report.requirements.filter(r => r.status === 'compliant').length; report.overallCompliance = (compliantCount / report.requirements.length) * 100; report.passed = report.overallCompliance >= 95; // 95% compliance threshold return report; } getStandardRequirements(standard) { // Mock compliance requirements const requirementsMap = { 'OWASP-Top-10': [ { id: 'A01', description: 'Broken Access Control' }, { id: 'A02', description: 'Cryptographic Failures' }, { id: 'A03', description: 'Injection' }, { id: 'A04', description: 'Insecure Design' }, { id: 'A05', description: 'Security Misconfiguration' } ], 'CWE-25': [ { id: 'CWE-79', description: 'Cross-site Scripting (XSS)' }, { id: 'CWE-89', description: 'SQL Injection' }, { id: 'CWE-22', description: 'Path Traversal' } ], 'GDPR': [ { id: 'Art-25', description: 'Data Protection by Design' }, { id: 'Art-32', description: 'Security of Processing' } ], 'SOC2': [ { id: 'CC6.1', description: 'Logical and Physical Access Controls' }, { id: 'CC7.1', description: 'System Operations' } ] }; return requirementsMap[standard] || []; } async checkRequirement(req, metadata) { // Mock requirement checking // In production, this would perform actual compliance checks return Math.random() > 0.1 ? 'compliant' : 'non-compliant'; } // ============================================================================ // Security Gate Enforcement // ============================================================================ async enforceSecurityGate(metadata) { console.log(`[SecurityScanner] Enforcing security gate`); // Run security scan const scanResult = await this.runSecurityScan(metadata); // Check thresholds const blockers = []; if (scanResult.summary.critical > this.config.thresholds.maxCriticalVulnerabilities) { blockers.push(...scanResult.findings.filter(f => f.severity === 'critical')); } if (scanResult.summary.high > this.config.thresholds.maxHighVulnerabilities) { const highBlockers = scanResult.findings .filter(f => f.severity === 'high') .slice(0, scanResult.summary.high - this.config.thresholds.maxHighVulnerabilities); blockers.push(...highBlockers); } if (scanResult.securityScore < this.config.thresholds.minSecurityScore) { // Security score too low } const passed = blockers.length === 0 && scanResult.securityScore >= this.config.thresholds.minSecurityScore; if (!passed) { this.emitEvent('security.gate.failed', { scanId: scanResult.scanId, blockers: blockers.length, securityScore: scanResult.securityScore }, 'critical'); } return { passed, reason: passed ? undefined : `${blockers.length} blocker(s) found, security score: ${scanResult.securityScore}`, blockers }; } // ============================================================================ // Reporting & Analysis // ============================================================================ async generateSecurityReport(metadata) { console.log(`[SecurityScanner] Generating security report`); const recentScans = this.scanHistory.slice(-10); const latestScan = recentScans[recentScans.length - 1]; // Calculate trends const avgSecurityScore = recentScans.reduce((sum, scan) => sum + scan.securityScore, 0) / recentScans.length; const totalFindings = recentScans.reduce((sum, scan) => sum + scan.summary.total, 0); const report = { generatedAt: new Date(), period: { from: recentScans[0]?.timestamp, to: latestScan?.timestamp }, summary: { totalScans: recentScans.length, averageSecurityScore: avgSecurityScore, totalFindings, criticalFindings: recentScans.reduce((sum, scan) => sum + scan.summary.critical, 0) }, latestScan: latestScan ? { scanId: latestScan.scanId, timestamp: latestScan.timestamp, securityScore: latestScan.securityScore, findings: latestScan.summary } : null, trends: { securityScoreImprovement: recentScans.length > 1 ? latestScan.securityScore - recentScans[0].securityScore : 0 }, recommendations: this.generateRecommendations(latestScan) }; // Store report await this.memoryStore.store('aqe/security/reports/latest', report); return report; } async updateSecurityBaseline(metadata) { console.log(`[SecurityScanner] Updating security baseline`); const latestScan = this.scanHistory[this.scanHistory.length - 1]; if (!latestScan) { throw new Error('No scan results available to set as baseline'); } // Store findings as baseline this.baselineFindings.clear(); for (const finding of latestScan.findings) { this.baselineFindings.set(finding.id, finding); } // Store baseline in memory await this.memoryStore.store('aqe/security/baselines', { scanId: latestScan.scanId, timestamp: new Date(), findings: Object.fromEntries(this.baselineFindings), summary: latestScan.summary, securityScore: latestScan.securityScore }); this.emitEvent('security.baseline.updated', { scanId: latestScan.scanId, findingsCount: this.baselineFindings.size, securityScore: latestScan.securityScore }, 'medium'); } // ============================================================================ // Helper Methods // ============================================================================ calculateSummary(findings) { return { critical: findings.filter(f => f.severity === 'critical').length, high: findings.filter(f => f.severity === 'high').length, medium: findings.filter(f => f.severity === 'medium').length, low: findings.filter(f => f.severity === 'low').length, info: findings.filter(f => f.severity === 'info').length, total: findings.length }; } calculateSecurityScore(summary) { // Calculate security score (0-100) // Weighted scoring: critical=-50, high=-10, medium=-3, low=-1 const score = 100 - (summary.critical * 50) - (summary.high * 10) - (summary.medium * 3) - (summary.low * 1); return Math.max(0, Math.min(100, score)); } evaluateSecurityGate(summary, securityScore) { return (summary.critical <= this.config.thresholds.maxCriticalVulnerabilities && summary.high <= this.config.thresholds.maxHighVulnerabilities && summary.medium <= this.config.thresholds.maxMediumVulnerabilities && securityScore >= this.config.thresholds.minSecurityScore); } generateRecommendations(scanResult) { const recommendations = []; if (!scanResult) { recommendations.push('Run initial security scan to establish baseline'); return recommendations; } if (scanResult.summary.critical > 0) { recommendations.push(`Address ${scanResult.summary.critical} critical vulnerabilities immediately`); } if (scanResult.summary.high > 5) { recommendations.push(`Prioritize fixing high severity vulnerabilities (${scanResult.summary.high} found)`); } if (scanResult.securityScore < 80) { recommendations.push(`Improve security score to above 80 (current: ${scanResult.securityScore})`); } if (this.config.scanScope?.includeDependencies && scanResult.findings.some(f => f.type === 'dependency')) { recommendations.push('Update vulnerable dependencies to latest secure versions'); } if (recommendations.length === 0) { recommendations.push('Maintain current security posture with regular scans'); } return recommendations; } async loadCVEDatabase() { // Mock CVE database loading // In production, this would fetch from NVD or similar console.log('[SecurityScanner] Loading CVE database'); const mockCVEs = [ { id: 'cve-2020-8203', cve: 'CVE-2020-8203', severity: 'high', description: 'Prototype pollution in lodash', affectedPackages: ['lodash'], publishedDate: new Date('2020-07-15'), lastModifiedDate: new Date('2021-07-21') } ]; for (const cve of mockCVEs) { this.cveDatabase.set(cve.id, cve); } } async initializeScanningTools() { // Mock tool initialization console.log('[SecurityScanner] Initializing scanning tools:', this.config.tools); // In production, this would set up connections to actual scanning tools } async handleTestGenerated(data) { console.log('[SecurityScanner] Auto-scanning newly generated tests'); // Automatically scan new test code for security issues } async handleDeploymentRequest(data) { console.log('[SecurityScanner] Enforcing security gate for deployment'); const gateResult = await this.enforceSecurityGate(data); if (!gateResult.passed) { this.emitEvent('deployment.blocked', { reason: 'security-gate-failed', blockers: gateResult.blockers.length }, 'critical'); } } async handleNewCVE(data) { console.log('[SecurityScanner] Processing new CVE:', data.cve); if (data.cve) { this.cveDatabase.set(data.cve.id, data.cve); // Check if any dependencies are affected const affectedDeps = await this.checkAffectedDependencies(data.cve); if (affectedDeps.length > 0) { this.emitEvent('security.cve.affected', { cve: data.cve.cve, affectedPackages: affectedDeps }, 'high'); } } } async checkAffectedDependencies(cve) { // Mock: Check if project dependencies are affected by CVE return []; } /** * Get detailed security scanner status */ async getDetailedStatus() { return { ...this.getStatus(), scanHistory: this.scanHistory.slice(-10), baselineFindings: this.baselineFindings.size, cveDatabase: this.cveDatabase.size, config: { tools: this.config.tools, thresholds: this.config.thresholds, compliance: this.config.compliance, scanScope: this.config.scanScope } }; } } exports.SecurityScannerAgent = SecurityScannerAgent; //# sourceMappingURL=SecurityScannerAgent.js.map