claude-flow-tbowman01
Version:
Enterprise-grade AI agent orchestration with ruv-swarm integration (Alpha Release)
1,498 lines (1,348 loc) • 46 kB
text/typescript
import { EventEmitter } from 'events';
import { writeFile, readFile, mkdir, readdir } from 'fs/promises';
import { join } from 'path';
import { spawn } from 'child_process';
import { Logger } from '../core/logger.js';
import { ConfigManager } from '../core/config.js';
export interface SecurityScan {
id: string;
name: string;
type:
| 'vulnerability'
| 'dependency'
| 'code-quality'
| 'secrets'
| 'compliance'
| 'infrastructure'
| 'container';
status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
projectId?: string;
target: {
type: 'repository' | 'container' | 'infrastructure' | 'application' | 'dependencies';
path: string;
branch?: string;
commit?: string;
image?: string;
tag?: string;
};
configuration: {
scanner: string;
rules: string[];
excludes: string[];
severity: SecuritySeverity[];
formats: string[];
outputPath: string;
};
results: SecurityFinding[];
metrics: {
totalFindings: number;
criticalFindings: number;
highFindings: number;
mediumFindings: number;
lowFindings: number;
falsePositives: number;
suppressed: number;
scanDuration: number;
filesScanned: number;
linesScanned: number;
};
compliance: {
frameworks: string[];
requirements: ComplianceCheck[];
overallScore: number;
passedChecks: number;
failedChecks: number;
};
remediation: {
autoFixAvailable: SecurityFinding[];
manualReview: SecurityFinding[];
recommendations: SecurityRecommendation[];
};
schedule?: {
frequency: 'manual' | 'daily' | 'weekly' | 'monthly' | 'on-commit' | 'on-deploy';
nextRun?: Date;
lastRun?: Date;
};
notifications: {
channels: string[];
thresholds: {
critical: number;
high: number;
medium: number;
};
};
createdAt: Date;
updatedAt: Date;
createdBy: string;
auditLog: SecurityAuditEntry[];
}
export type SecuritySeverity = 'critical' | 'high' | 'medium' | 'low' | 'info';
export interface SecurityFinding {
id: string;
title: string;
description: string;
severity: SecuritySeverity;
category:
| 'vulnerability'
| 'secret'
| 'misconfiguration'
| 'compliance'
| 'code-quality'
| 'license';
cwe?: string; // Common Weakness Enumeration
cve?: string; // Common Vulnerabilities and Exposures
cvss?: {
score: number;
vector: string;
version: string;
};
location: {
file: string;
line?: number;
column?: number;
function?: string;
component?: string;
};
evidence: {
snippet?: string;
context?: string;
references?: string[];
};
impact: string;
remediation: {
description: string;
effort: 'low' | 'medium' | 'high';
priority: 'low' | 'medium' | 'high' | 'critical';
autoFixable: boolean;
steps: string[];
references: string[];
};
status: 'open' | 'triaged' | 'in-progress' | 'resolved' | 'suppressed' | 'false-positive';
assignedTo?: string;
dueDate?: Date;
tags: string[];
metadata: Record<string, any>;
firstSeen: Date;
lastSeen: Date;
occurrences: number;
}
export interface ComplianceCheck {
id: string;
framework: string; // e.g., 'SOC2', 'GDPR', 'HIPAA', 'PCI-DSS', 'CIS', 'NIST'
control: string;
description: string;
status: 'passed' | 'failed' | 'not-applicable' | 'manual-review';
severity: SecuritySeverity;
evidence?: string;
remediation?: string;
lastChecked: Date;
}
export interface SecurityRecommendation {
id: string;
title: string;
description: string;
category:
| 'security-hardening'
| 'vulnerability-management'
| 'access-control'
| 'monitoring'
| 'compliance';
priority: 'low' | 'medium' | 'high' | 'critical';
effort: 'low' | 'medium' | 'high';
impact: string;
implementation: {
steps: string[];
tools: string[];
timeEstimate: string;
cost: string;
};
references: string[];
applicableFrameworks: string[];
}
export interface SecurityPolicy {
id: string;
name: string;
description: string;
type: 'scanning' | 'access-control' | 'compliance' | 'incident-response' | 'data-protection';
version: string;
status: 'draft' | 'active' | 'deprecated';
rules: SecurityRule[];
enforcement: {
level: 'advisory' | 'warning' | 'blocking';
exceptions: string[];
approvers: string[];
};
applicability: {
projects: string[];
environments: string[];
resources: string[];
};
schedule: {
reviewFrequency: 'quarterly' | 'annually' | 'as-needed';
nextReview: Date;
lastReview?: Date;
reviewer: string;
};
metrics: {
violations: number;
compliance: number;
exceptions: number;
};
createdAt: Date;
updatedAt: Date;
createdBy: string;
}
export interface SecurityRule {
id: string;
name: string;
description: string;
condition: string; // Query or condition syntax
action: 'allow' | 'deny' | 'alert' | 'audit';
severity: SecuritySeverity;
parameters: Record<string, any>;
enabled: boolean;
}
export interface SecurityIncident {
id: string;
title: string;
description: string;
severity: SecuritySeverity;
status: 'open' | 'investigating' | 'contained' | 'resolved' | 'closed';
type:
| 'security-breach'
| 'vulnerability-exploit'
| 'policy-violation'
| 'suspicious-activity'
| 'compliance-violation';
source: {
type: 'scan' | 'alert' | 'user-report' | 'automated-detection';
details: Record<string, any>;
};
affected: {
systems: string[];
data: string[];
users: string[];
};
timeline: {
detected: Date;
reported: Date;
acknowledged: Date;
contained?: Date;
resolved?: Date;
closed?: Date;
};
response: {
assignedTo: string[];
actions: SecurityAction[];
communications: SecurityCommunication[];
lessons: string[];
};
evidence: {
logs: string[];
files: string[];
screenshots: string[];
forensics: string[];
};
impact: {
confidentiality: 'none' | 'low' | 'medium' | 'high';
integrity: 'none' | 'low' | 'medium' | 'high';
availability: 'none' | 'low' | 'medium' | 'high';
financialLoss?: number;
reputationalDamage?: string;
regulatoryImplications?: string[];
};
rootCause: {
primary: string;
contributing: string[];
analysis: string;
};
remediation: {
immediate: string[];
shortTerm: string[];
longTerm: string[];
preventive: string[];
};
createdAt: Date;
updatedAt: Date;
createdBy: string;
auditLog: SecurityAuditEntry[];
}
export interface SecurityAction {
id: string;
type:
| 'investigation'
| 'containment'
| 'eradication'
| 'recovery'
| 'notification'
| 'documentation';
description: string;
assignedTo: string;
status: 'pending' | 'in-progress' | 'completed' | 'cancelled';
dueDate?: Date;
completedAt?: Date;
notes: string;
}
export interface SecurityCommunication {
id: string;
type: 'internal' | 'external' | 'regulatory' | 'customer' | 'media';
audience: string[];
subject: string;
message: string;
sentAt: Date;
sentBy: string;
channel: 'email' | 'phone' | 'meeting' | 'document' | 'portal';
}
export interface SecurityAuditEntry {
id: string;
timestamp: Date;
userId: string;
action: string;
target: string;
details: Record<string, any>;
ipAddress?: string;
userAgent?: string;
}
export interface VulnerabilityDatabase {
id: string;
name: string;
type: 'nvd' | 'github' | 'snyk' | 'custom';
url: string;
updateFrequency: 'hourly' | 'daily' | 'weekly';
lastUpdate: Date;
status: 'active' | 'inactive' | 'error';
configuration: Record<string, any>;
}
export interface SecurityMetrics {
scans: {
total: number;
completed: number;
failed: number;
inProgress: number;
byType: Record<string, number>;
averageDuration: number;
};
findings: {
total: number;
open: number;
resolved: number;
suppressed: number;
bySeverity: Record<SecuritySeverity, number>;
byCategory: Record<string, number>;
meanTimeToResolution: number;
};
compliance: {
frameworks: Record<
string,
{
total: number;
passed: number;
failed: number;
score: number;
}
>;
overallScore: number;
trending: 'improving' | 'stable' | 'declining';
};
incidents: {
total: number;
open: number;
resolved: number;
bySeverity: Record<SecuritySeverity, number>;
meanTimeToDetection: number;
meanTimeToResponse: number;
meanTimeToResolution: number;
};
policies: {
total: number;
active: number;
violations: number;
compliance: number;
};
trends: {
findingsTrend: Array<{ date: Date; count: number }>;
complianceTrend: Array<{ date: Date; score: number }>;
incidentsTrend: Array<{ date: Date; count: number }>;
};
}
export class SecurityManager extends EventEmitter {
private scans: Map<string, SecurityScan> = new Map();
private policies: Map<string, SecurityPolicy> = new Map();
private incidents: Map<string, SecurityIncident> = new Map();
private vulnerabilityDatabases: Map<string, VulnerabilityDatabase> = new Map();
private securityPath: string;
private logger: Logger;
private config: ConfigManager;
constructor(securityPath: string = './security', logger?: Logger, config?: ConfigManager) {
super();
this.securityPath = securityPath;
this.logger = logger || new Logger({ level: 'info', format: 'text', destination: 'console' });
this.config = config || ConfigManager.getInstance();
}
async initialize(): Promise<void> {
try {
await mkdir(this.securityPath, { recursive: true });
await mkdir(join(this.securityPath, 'scans'), { recursive: true });
await mkdir(join(this.securityPath, 'policies'), { recursive: true });
await mkdir(join(this.securityPath, 'incidents'), { recursive: true });
await mkdir(join(this.securityPath, 'reports'), { recursive: true });
await mkdir(join(this.securityPath, 'databases'), { recursive: true });
await this.loadConfigurations();
await this.initializeDefaultPolicies();
await this.initializeVulnerabilityDatabases();
this.logger.info('Security Manager initialized successfully');
} catch (error) {
this.logger.error('Failed to initialize Security Manager', { error });
throw error;
}
}
async createSecurityScan(scanData: {
name: string;
type: SecurityScan['type'];
target: SecurityScan['target'];
configuration?: Partial<SecurityScan['configuration']>;
projectId?: string;
schedule?: SecurityScan['schedule'];
}): Promise<SecurityScan> {
const scan: SecurityScan = {
id: `scan-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
name: scanData.name,
type: scanData.type,
status: 'pending',
projectId: scanData.projectId,
target: scanData.target,
configuration: {
scanner: this.getDefaultScanner(scanData.type),
rules: [],
excludes: [],
severity: ['critical', 'high', 'medium', 'low'],
formats: ['json', 'html'],
outputPath: join(this.securityPath, 'reports'),
...scanData.configuration,
},
results: [],
metrics: {
totalFindings: 0,
criticalFindings: 0,
highFindings: 0,
mediumFindings: 0,
lowFindings: 0,
falsePositives: 0,
suppressed: 0,
scanDuration: 0,
filesScanned: 0,
linesScanned: 0,
},
compliance: {
frameworks: [],
requirements: [],
overallScore: 0,
passedChecks: 0,
failedChecks: 0,
},
remediation: {
autoFixAvailable: [],
manualReview: [],
recommendations: [],
},
schedule: scanData.schedule,
notifications: {
channels: [],
thresholds: {
critical: 1,
high: 5,
medium: 10,
},
},
createdAt: new Date(),
updatedAt: new Date(),
createdBy: 'system',
auditLog: [],
};
this.addAuditEntry(scan, 'system', 'scan_created', 'scan', {
scanId: scan.id,
scanName: scan.name,
scanType: scan.type,
});
this.scans.set(scan.id, scan);
await this.saveScan(scan);
this.emit('scan:created', scan);
this.logger.info(`Security scan created: ${scan.name} (${scan.id})`);
return scan;
}
async executeScan(scanId: string): Promise<void> {
const scan = this.scans.get(scanId);
if (!scan) {
throw new Error(`Scan not found: ${scanId}`);
}
if (scan.status !== 'pending') {
throw new Error(`Scan ${scanId} is not in pending status`);
}
scan.status = 'running';
scan.updatedAt = new Date();
this.addAuditEntry(scan, 'system', 'scan_started', 'scan', {
scanId,
target: scan.target,
});
await this.saveScan(scan);
this.emit('scan:started', scan);
try {
const startTime = Date.now();
// Execute the appropriate scanner
const findings = await this.executeScanEngine(scan);
const endTime = Date.now();
scan.metrics.scanDuration = endTime - startTime;
scan.results = findings;
scan.status = 'completed';
// Calculate metrics
this.calculateScanMetrics(scan);
// Run compliance checks
await this.runComplianceChecks(scan);
// Generate remediation recommendations
await this.generateRemediationRecommendations(scan);
// Check notification thresholds
await this.checkNotificationThresholds(scan);
scan.updatedAt = new Date();
this.addAuditEntry(scan, 'system', 'scan_completed', 'scan', {
scanId,
duration: scan.metrics.scanDuration,
findingsCount: scan.results.length,
});
await this.saveScan(scan);
this.emit('scan:completed', scan);
this.logger.info(
`Security scan completed: ${scan.name} (${scan.id}) - ${scan.results.length} findings`,
);
} catch (error) {
scan.status = 'failed';
scan.updatedAt = new Date();
this.addAuditEntry(scan, 'system', 'scan_failed', 'scan', {
scanId,
error: error instanceof Error ? error.message : String(error),
});
await this.saveScan(scan);
this.emit('scan:failed', { scan, error });
this.logger.error(`Security scan failed: ${scan.name} (${scanId})`, { error });
throw error;
}
}
async createSecurityIncident(incidentData: {
title: string;
description: string;
severity: SecuritySeverity;
type: SecurityIncident['type'];
source: SecurityIncident['source'];
affected?: Partial<SecurityIncident['affected']>;
}): Promise<SecurityIncident> {
const incident: SecurityIncident = {
id: `incident-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
title: incidentData.title,
description: incidentData.description,
severity: incidentData.severity,
status: 'open',
type: incidentData.type,
source: incidentData.source,
affected: {
systems: [],
data: [],
users: [],
...incidentData.affected,
},
timeline: {
detected: new Date(),
reported: new Date(),
acknowledged: new Date(),
},
response: {
assignedTo: [],
actions: [],
communications: [],
lessons: [],
},
evidence: {
logs: [],
files: [],
screenshots: [],
forensics: [],
},
impact: {
confidentiality: 'none',
integrity: 'none',
availability: 'none',
},
rootCause: {
primary: '',
contributing: [],
analysis: '',
},
remediation: {
immediate: [],
shortTerm: [],
longTerm: [],
preventive: [],
},
createdAt: new Date(),
updatedAt: new Date(),
createdBy: 'system',
auditLog: [],
};
this.addAuditEntry(incident, 'system', 'incident_created', 'incident', {
incidentId: incident.id,
severity: incident.severity,
type: incident.type,
});
this.incidents.set(incident.id, incident);
await this.saveIncident(incident);
// Auto-assign based on severity and type
await this.autoAssignIncident(incident);
// Send immediate notifications for high/critical incidents
if (incident.severity === 'critical' || incident.severity === 'high') {
await this.sendIncidentNotification(incident);
}
this.emit('incident:created', incident);
this.logger.info(`Security incident created: ${incident.title} (${incident.id})`);
return incident;
}
async updateIncident(
incidentId: string,
updates: Partial<SecurityIncident>,
userId: string = 'system',
): Promise<SecurityIncident> {
const incident = this.incidents.get(incidentId);
if (!incident) {
throw new Error(`Incident not found: ${incidentId}`);
}
const oldStatus = incident.status;
Object.assign(incident, updates);
incident.updatedAt = new Date();
// Update timeline based on status changes
if (updates.status && updates.status !== oldStatus) {
this.updateIncidentTimeline(incident, updates.status);
}
this.addAuditEntry(incident, userId, 'incident_updated', 'incident', {
incidentId,
changes: Object.keys(updates),
oldStatus,
newStatus: incident.status,
});
await this.saveIncident(incident);
this.emit('incident:updated', { incident, updates });
this.logger.info(`Security incident updated: ${incident.title} (${incidentId})`);
return incident;
}
async runComplianceAssessment(
frameworks: string[],
scope?: {
projectId?: string;
environment?: string;
resources?: string[];
},
): Promise<ComplianceCheck[]> {
const checks: ComplianceCheck[] = [];
for (const framework of frameworks) {
const frameworkChecks = await this.runFrameworkChecks(framework, scope);
checks.push(...frameworkChecks);
}
this.logger.info(
`Compliance assessment completed: ${checks.length} checks across ${frameworks.length} frameworks`,
);
this.emit('compliance:assessed', { frameworks, checks, scope });
return checks;
}
async createSecurityPolicy(policyData: {
name: string;
description: string;
type: SecurityPolicy['type'];
rules: Omit<SecurityRule, 'id'>[];
enforcement?: Partial<SecurityPolicy['enforcement']>;
applicability?: Partial<SecurityPolicy['applicability']>;
}): Promise<SecurityPolicy> {
const policy: SecurityPolicy = {
id: `policy-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
name: policyData.name,
description: policyData.description,
type: policyData.type,
version: '1.0.0',
status: 'draft',
rules: policyData.rules.map((rule) => ({
id: `rule-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
...rule,
})),
enforcement: {
level: 'warning',
exceptions: [],
approvers: [],
...policyData.enforcement,
},
applicability: {
projects: [],
environments: [],
resources: [],
...policyData.applicability,
},
schedule: {
reviewFrequency: 'annually',
nextReview: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year
reviewer: 'security-team',
},
metrics: {
violations: 0,
compliance: 100,
exceptions: 0,
},
createdAt: new Date(),
updatedAt: new Date(),
createdBy: 'system',
};
this.policies.set(policy.id, policy);
await this.savePolicy(policy);
this.emit('policy:created', policy);
this.logger.info(`Security policy created: ${policy.name} (${policy.id})`);
return policy;
}
async getSecurityMetrics(filters?: {
timeRange?: { start: Date; end: Date };
projectId?: string;
environment?: string;
severity?: SecuritySeverity[];
}): Promise<SecurityMetrics> {
let scans = Array.from(this.scans.values());
let incidents = Array.from(this.incidents.values());
// Apply filters
if (filters) {
if (filters.timeRange) {
scans = scans.filter(
(s) => s.createdAt >= filters.timeRange!.start && s.createdAt <= filters.timeRange!.end,
);
incidents = incidents.filter(
(i) => i.createdAt >= filters.timeRange!.start && i.createdAt <= filters.timeRange!.end,
);
}
if (filters.projectId) {
scans = scans.filter((s) => s.projectId === filters.projectId);
}
}
// Calculate scan metrics
const scanMetrics = {
total: scans.length,
completed: scans.filter((s) => s.status === 'completed').length,
failed: scans.filter((s) => s.status === 'failed').length,
inProgress: scans.filter((s) => s.status === 'running').length,
byType: this.groupBy(scans, 'type'),
averageDuration:
scans.length > 0
? scans.reduce((sum, s) => sum + s.metrics.scanDuration, 0) / scans.length
: 0,
};
// Calculate finding metrics
const allFindings = scans.flatMap((s) => s.results);
const findingMetrics = {
total: allFindings.length,
open: allFindings.filter((f) => f.status === 'open').length,
resolved: allFindings.filter((f) => f.status === 'resolved').length,
suppressed: allFindings.filter((f) => f.status === 'suppressed').length,
bySeverity: this.groupBy(allFindings, 'severity') as Record<SecuritySeverity, number>,
byCategory: this.groupBy(allFindings, 'category'),
meanTimeToResolution: this.calculateMTTR(allFindings),
};
// Calculate compliance metrics
const allComplianceChecks = scans.flatMap((s) => s.compliance.requirements);
const complianceFrameworks: Record<string, any> = {};
for (const check of allComplianceChecks) {
if (!complianceFrameworks[check.framework]) {
complianceFrameworks[check.framework] = {
total: 0,
passed: 0,
failed: 0,
score: 0,
};
}
complianceFrameworks[check.framework].total++;
if (check.status === 'passed') {
complianceFrameworks[check.framework].passed++;
} else if (check.status === 'failed') {
complianceFrameworks[check.framework].failed++;
}
}
// Calculate scores
for (const framework in complianceFrameworks) {
const fw = complianceFrameworks[framework];
fw.score = fw.total > 0 ? (fw.passed / fw.total) * 100 : 0;
}
const overallComplianceScore =
Object.values(complianceFrameworks).length > 0
? Object.values(complianceFrameworks).reduce((sum: number, fw: any) => sum + fw.score, 0) /
Object.values(complianceFrameworks).length
: 0;
// Calculate incident metrics
const incidentMetrics = {
total: incidents.length,
open: incidents.filter((i) => i.status === 'open' || i.status === 'investigating').length,
resolved: incidents.filter((i) => i.status === 'resolved' || i.status === 'closed').length,
bySeverity: this.groupBy(incidents, 'severity') as Record<SecuritySeverity, number>,
meanTimeToDetection: this.calculateMTTD(incidents),
meanTimeToResponse: this.calculateMTTResponse(incidents),
meanTimeToResolution: this.calculateIncidentMTTR(incidents),
};
// Policy metrics
const policies = Array.from(this.policies.values());
const policyMetrics = {
total: policies.length,
active: policies.filter((p) => p.status === 'active').length,
violations: policies.reduce((sum, p) => sum + p.metrics.violations, 0),
compliance:
policies.length > 0
? policies.reduce((sum, p) => sum + p.metrics.compliance, 0) / policies.length
: 0,
};
return {
scans: scanMetrics,
findings: findingMetrics,
compliance: {
frameworks: complianceFrameworks,
overallScore: overallComplianceScore,
trending: 'stable', // Would be calculated from historical data
},
incidents: incidentMetrics,
policies: policyMetrics,
trends: {
findingsTrend: [], // Would be calculated from historical data
complianceTrend: [], // Would be calculated from historical data
incidentsTrend: [], // Would be calculated from historical data
},
};
}
// Private helper methods
private async loadConfigurations(): Promise<void> {
try {
// Load scans
const scanFiles = await readdir(join(this.securityPath, 'scans'));
for (const file of scanFiles.filter((f) => f.endsWith('.json'))) {
const content = await readFile(join(this.securityPath, 'scans', file), 'utf-8');
const scan: SecurityScan = JSON.parse(content);
this.scans.set(scan.id, scan);
}
// Load policies
const policyFiles = await readdir(join(this.securityPath, 'policies'));
for (const file of policyFiles.filter((f) => f.endsWith('.json'))) {
const content = await readFile(join(this.securityPath, 'policies', file), 'utf-8');
const policy: SecurityPolicy = JSON.parse(content);
this.policies.set(policy.id, policy);
}
// Load incidents
const incidentFiles = await readdir(join(this.securityPath, 'incidents'));
for (const file of incidentFiles.filter((f) => f.endsWith('.json'))) {
const content = await readFile(join(this.securityPath, 'incidents', file), 'utf-8');
const incident: SecurityIncident = JSON.parse(content);
this.incidents.set(incident.id, incident);
}
this.logger.info(
`Loaded ${this.scans.size} scans, ${this.policies.size} policies, ${this.incidents.size} incidents`,
);
} catch (error) {
this.logger.warn('Failed to load some security configurations', { error });
}
}
private async initializeDefaultPolicies(): Promise<void> {
const defaultPolicies = [
{
name: 'Critical Vulnerability Policy',
description: 'Immediate action required for critical vulnerabilities',
type: 'scanning' as const,
rules: [
{
name: 'Critical CVSS Score',
description: 'Alert on vulnerabilities with CVSS score >= 9.0',
condition: 'cvss.score >= 9.0',
action: 'alert' as const,
severity: 'critical' as const,
parameters: { threshold: 9.0 },
enabled: true,
},
],
enforcement: {
level: 'blocking' as const,
exceptions: [],
approvers: ['security-lead'],
},
},
{
name: 'Secret Detection Policy',
description: 'Detect exposed secrets and credentials',
type: 'scanning' as const,
rules: [
{
name: 'API Key Detection',
description: 'Detect exposed API keys',
condition: 'category == "secret" && type == "api-key"',
action: 'deny' as const,
severity: 'high' as const,
parameters: {},
enabled: true,
},
],
},
];
for (const policyData of defaultPolicies) {
if (!Array.from(this.policies.values()).some((p) => p.name === policyData.name)) {
await this.createSecurityPolicy(policyData);
}
}
}
private async initializeVulnerabilityDatabases(): Promise<void> {
const databases: VulnerabilityDatabase[] = [
{
id: 'nvd',
name: 'National Vulnerability Database',
type: 'nvd',
url: 'https://nvd.nist.gov/feeds/json/cve/1.1/',
updateFrequency: 'daily',
lastUpdate: new Date(),
status: 'active',
configuration: {},
},
{
id: 'github-advisories',
name: 'GitHub Security Advisories',
type: 'github',
url: 'https://api.github.com/advisories',
updateFrequency: 'daily',
lastUpdate: new Date(),
status: 'active',
configuration: {},
},
];
for (const db of databases) {
this.vulnerabilityDatabases.set(db.id, db);
}
}
private getDefaultScanner(type: SecurityScan['type']): string {
const scanners: Record<SecurityScan['type'], string> = {
vulnerability: 'trivy',
dependency: 'npm-audit',
'code-quality': 'sonarqube',
secrets: 'gitleaks',
compliance: 'inspec',
infrastructure: 'checkov',
container: 'clair',
};
return scanners[type] || 'generic';
}
private async executeScanEngine(scan: SecurityScan): Promise<SecurityFinding[]> {
const findings: SecurityFinding[] = [];
switch (scan.configuration.scanner) {
case 'trivy':
return this.executeTrivyScan(scan);
case 'npm-audit':
return this.executeNpmAuditScan(scan);
case 'gitleaks':
return this.executeGitleaksScan(scan);
case 'checkov':
return this.executeCheckovScan(scan);
default:
return this.executeGenericScan(scan);
}
}
private async executeTrivyScan(scan: SecurityScan): Promise<SecurityFinding[]> {
return new Promise((resolve, reject) => {
const findings: SecurityFinding[] = [];
// Mock Trivy execution
const mockFindings = [
{
id: `finding-${Date.now()}-1`,
title: 'CVE-2023-12345: Remote Code Execution in libxml2',
description: 'A buffer overflow vulnerability in libxml2 allows remote code execution',
severity: 'critical' as const,
category: 'vulnerability' as const,
cve: 'CVE-2023-12345',
cvss: {
score: 9.8,
vector: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H',
version: '3.1',
},
location: {
file: 'package-lock.json',
line: 125,
component: 'libxml2@2.9.10',
},
evidence: {
snippet: '"libxml2": "2.9.10"',
context: 'Dependency declaration',
references: ['https://nvd.nist.gov/vuln/detail/CVE-2023-12345'],
},
impact: 'Remote attackers could execute arbitrary code',
remediation: {
description: 'Update libxml2 to version 2.9.14 or later',
effort: 'low' as const,
priority: 'critical' as const,
autoFixable: true,
steps: ['npm update libxml2'],
references: ['https://github.com/GNOME/libxml2/releases'],
},
status: 'open' as const,
tags: ['cve', 'rce', 'dependency'],
metadata: {},
firstSeen: new Date(),
lastSeen: new Date(),
occurrences: 1,
},
];
// Simulate scan delay
setTimeout(() => {
resolve(mockFindings);
}, 2000);
});
}
private async executeNpmAuditScan(scan: SecurityScan): Promise<SecurityFinding[]> {
return new Promise((resolve, reject) => {
const command = 'npm';
const args = ['audit', '--json'];
const child = spawn(command, args, {
cwd: scan.target.path,
stdio: ['pipe', 'pipe', 'pipe'],
});
let stdout = '';
let stderr = '';
child.stdout?.on('data', (data) => {
stdout += data.toString();
});
child.stderr?.on('data', (data) => {
stderr += data.toString();
});
child.on('close', (code) => {
try {
const auditResult = JSON.parse(stdout);
const findings = this.parseNpmAuditResults(auditResult);
resolve(findings);
} catch (error) {
reject(
new Error(
`Failed to parse npm audit results: ${error instanceof Error ? error.message : String(error)}`,
),
);
}
});
child.on('error', (error) => {
reject(error);
});
});
}
private async executeGitleaksScan(scan: SecurityScan): Promise<SecurityFinding[]> {
// Mock Gitleaks scan for secrets detection
return [
{
id: `finding-${Date.now()}-2`,
title: 'Exposed AWS Access Key',
description: 'AWS access key found in source code',
severity: 'high' as const,
category: 'secret' as const,
location: {
file: 'config/aws.js',
line: 12,
column: 20,
},
evidence: {
snippet: 'const accessKey = "AKIA123456789..."',
context: 'Hardcoded AWS credentials',
},
impact: 'Unauthorized access to AWS resources',
remediation: {
description: 'Remove hardcoded credentials and use environment variables or IAM roles',
effort: 'medium' as const,
priority: 'high' as const,
autoFixable: false,
steps: [
'Remove hardcoded credentials',
'Use environment variables',
'Rotate compromised keys',
],
references: ['https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html'],
},
status: 'open' as const,
tags: ['secret', 'aws', 'credentials'],
metadata: {},
firstSeen: new Date(),
lastSeen: new Date(),
occurrences: 1,
},
];
}
private async executeCheckovScan(scan: SecurityScan): Promise<SecurityFinding[]> {
// Mock Checkov scan for infrastructure as code
return [];
}
private async executeGenericScan(scan: SecurityScan): Promise<SecurityFinding[]> {
// Generic scan implementation
return [];
}
private parseNpmAuditResults(auditResult: any): SecurityFinding[] {
const findings: SecurityFinding[] = [];
if (auditResult.vulnerabilities) {
for (const [packageName, vulnData] of Object.entries(auditResult.vulnerabilities)) {
const vuln = vulnData as any;
findings.push({
id: `finding-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
title: `${vuln.severity} vulnerability in ${packageName}`,
description: vuln.title || 'Vulnerability detected',
severity: vuln.severity as SecuritySeverity,
category: 'vulnerability',
cve: vuln.cve,
location: {
file: 'package.json',
component: packageName,
},
evidence: {
snippet: `"${packageName}": "${vuln.range}"`,
references: vuln.url ? [vuln.url] : [],
},
impact: vuln.overview || 'Security vulnerability',
remediation: {
description: vuln.recommendation || 'Update to a secure version',
effort: 'low' as const,
priority:
vuln.severity === 'info'
? 'low'
: (vuln.severity as 'low' | 'medium' | 'high' | 'critical'),
autoFixable: true,
steps: [`npm update ${packageName}`],
references: vuln.url ? [vuln.url] : [],
},
status: 'open',
tags: ['npm', 'dependency'],
metadata: { packageName, range: vuln.range },
firstSeen: new Date(),
lastSeen: new Date(),
occurrences: 1,
});
}
}
return findings;
}
private calculateScanMetrics(scan: SecurityScan): void {
const findings = scan.results;
scan.metrics.totalFindings = findings.length;
scan.metrics.criticalFindings = findings.filter((f) => f.severity === 'critical').length;
scan.metrics.highFindings = findings.filter((f) => f.severity === 'high').length;
scan.metrics.mediumFindings = findings.filter((f) => f.severity === 'medium').length;
scan.metrics.lowFindings = findings.filter((f) => f.severity === 'low').length;
scan.metrics.falsePositives = findings.filter((f) => f.status === 'false-positive').length;
scan.metrics.suppressed = findings.filter((f) => f.status === 'suppressed').length;
}
private async runComplianceChecks(scan: SecurityScan): Promise<void> {
// Mock compliance checks
const frameworks = ['SOC2', 'GDPR', 'PCI-DSS'];
for (const framework of frameworks) {
const checks = await this.runFrameworkChecks(framework, { projectId: scan.projectId });
scan.compliance.requirements.push(...checks);
}
scan.compliance.frameworks = frameworks;
scan.compliance.passedChecks = scan.compliance.requirements.filter(
(r) => r.status === 'passed',
).length;
scan.compliance.failedChecks = scan.compliance.requirements.filter(
(r) => r.status === 'failed',
).length;
scan.compliance.overallScore =
scan.compliance.requirements.length > 0
? (scan.compliance.passedChecks / scan.compliance.requirements.length) * 100
: 0;
}
private async runFrameworkChecks(framework: string, scope?: any): Promise<ComplianceCheck[]> {
// Mock compliance checks for different frameworks
const mockChecks: ComplianceCheck[] = [
{
id: `check-${Date.now()}-1`,
framework,
control: 'CC6.1',
description: 'Encryption in transit',
status: 'passed',
severity: 'high',
evidence: 'TLS 1.2+ configured',
lastChecked: new Date(),
},
{
id: `check-${Date.now()}-2`,
framework,
control: 'CC6.7',
description: 'Encryption at rest',
status: 'failed',
severity: 'medium',
remediation: 'Enable database encryption',
lastChecked: new Date(),
},
];
return mockChecks;
}
private async generateRemediationRecommendations(scan: SecurityScan): Promise<void> {
const autoFixable = scan.results.filter((f) => f.remediation.autoFixable);
const manualReview = scan.results.filter((f) => !f.remediation.autoFixable);
scan.remediation.autoFixAvailable = autoFixable;
scan.remediation.manualReview = manualReview;
// Generate general recommendations
scan.remediation.recommendations = [
{
id: `rec-${Date.now()}-1`,
title: 'Implement Automated Dependency Updates',
description: 'Set up automated dependency updates to reduce vulnerability exposure',
category: 'vulnerability-management',
priority: 'high',
effort: 'medium',
impact: 'Reduces time to patch vulnerabilities',
implementation: {
steps: [
'Configure Dependabot or Renovate',
'Set up automated testing pipeline',
'Enable auto-merge for low-risk updates',
],
tools: ['Dependabot', 'Renovate', 'GitHub Actions'],
timeEstimate: '2-4 hours',
cost: 'Free',
},
references: [
'https://docs.github.com/en/code-security/dependabot',
'https://renovatebot.com/',
],
applicableFrameworks: ['SOC2', 'ISO27001'],
},
];
}
private async checkNotificationThresholds(scan: SecurityScan): Promise<void> {
const thresholds = scan.notifications.thresholds;
if (
scan.metrics.criticalFindings >= thresholds.critical ||
scan.metrics.highFindings >= thresholds.high ||
scan.metrics.mediumFindings >= thresholds.medium
) {
await this.sendScanNotification(scan);
}
}
private async sendScanNotification(scan: SecurityScan): Promise<void> {
const message = `Security scan '${scan.name}' completed with ${scan.metrics.totalFindings} findings (${scan.metrics.criticalFindings} critical, ${scan.metrics.highFindings} high)`;
this.emit('notification:scan', {
scan,
message,
severity:
scan.metrics.criticalFindings > 0
? 'critical'
: scan.metrics.highFindings > 0
? 'high'
: 'medium',
});
this.logger.warn(message);
}
private async autoAssignIncident(incident: SecurityIncident): Promise<void> {
// Auto-assign based on severity and type
const assignmentRules: Record<string, string[]> = {
critical: ['security-lead', 'ciso'],
high: ['security-team'],
medium: ['security-analyst'],
low: ['security-analyst'],
};
incident.response.assignedTo = assignmentRules[incident.severity] || ['security-team'];
}
private async sendIncidentNotification(incident: SecurityIncident): Promise<void> {
const message = `SECURITY INCIDENT: ${incident.title} (${incident.severity.toUpperCase()})`;
this.emit('notification:incident', {
incident,
message,
urgency: incident.severity === 'critical' ? 'immediate' : 'high',
});
this.logger.error(message);
}
private updateIncidentTimeline(incident: SecurityIncident, newStatus: string): void {
const now = new Date();
switch (newStatus) {
case 'investigating':
incident.timeline.acknowledged = now;
break;
case 'contained':
incident.timeline.contained = now;
break;
case 'resolved':
incident.timeline.resolved = now;
break;
case 'closed':
incident.timeline.closed = now;
break;
}
}
private async saveScan(scan: SecurityScan): Promise<void> {
const filePath = join(this.securityPath, 'scans', `${scan.id}.json`);
await writeFile(filePath, JSON.stringify(scan, null, 2));
}
private async savePolicy(policy: SecurityPolicy): Promise<void> {
const filePath = join(this.securityPath, 'policies', `${policy.id}.json`);
await writeFile(filePath, JSON.stringify(policy, null, 2));
}
private async saveIncident(incident: SecurityIncident): Promise<void> {
const filePath = join(this.securityPath, 'incidents', `${incident.id}.json`);
await writeFile(filePath, JSON.stringify(incident, null, 2));
}
private addAuditEntry(
target: SecurityScan | SecurityIncident,
userId: string,
action: string,
targetType: string,
details: Record<string, any>,
): void {
const entry: SecurityAuditEntry = {
id: `audit-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
timestamp: new Date(),
userId,
action,
target: targetType,
details,
};
target.auditLog.push(entry);
}
private groupBy<T>(array: T[], key: keyof T): Record<string, number> {
return array.reduce(
(groups, item) => {
const value = String(item[key]);
groups[value] = (groups[value] || 0) + 1;
return groups;
},
{} as Record<string, number>,
);
}
private calculateMTTR(findings: SecurityFinding[]): number {
const resolvedFindings = findings.filter(
(f) => f.status === 'resolved' && f.firstSeen && f.lastSeen,
);
if (resolvedFindings.length === 0) return 0;
const totalTime = resolvedFindings.reduce(
(sum, f) => sum + (f.lastSeen.getTime() - f.firstSeen.getTime()),
0,
);
return totalTime / resolvedFindings.length;
}
private calculateMTTD(incidents: SecurityIncident[]): number {
const detectedIncidents = incidents.filter((i) => i.timeline.detected && i.timeline.reported);
if (detectedIncidents.length === 0) return 0;
const totalTime = detectedIncidents.reduce(
(sum, i) => sum + (i.timeline.reported.getTime() - i.timeline.detected.getTime()),
0,
);
return totalTime / detectedIncidents.length;
}
private calculateMTTResponse(incidents: SecurityIncident[]): number {
const respondedIncidents = incidents.filter(
(i) => i.timeline.reported && i.timeline.acknowledged,
);
if (respondedIncidents.length === 0) return 0;
const totalTime = respondedIncidents.reduce(
(sum, i) => sum + (i.timeline.acknowledged.getTime() - i.timeline.reported.getTime()),
0,
);
return totalTime / respondedIncidents.length;
}
private calculateIncidentMTTR(incidents: SecurityIncident[]): number {
const resolvedIncidents = incidents.filter((i) => i.timeline.reported && i.timeline.resolved);
if (resolvedIncidents.length === 0) return 0;
const totalTime = resolvedIncidents.reduce(
(sum, i) => sum + (i.timeline.resolved!.getTime() - i.timeline.reported.getTime()),
0,
);
return totalTime / resolvedIncidents.length;
}
}