agentic-qe
Version:
Agentic Quality Engineering Fleet System - AI-driven quality management platform
700 lines • 30.3 kB
JavaScript
"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