@sethdouglasford/claude-flow
Version:
Claude Code Flow - Advanced AI-powered development workflows with SPARC methodology
906 lines • 37.6 kB
JavaScript
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 class SecurityManager extends EventEmitter {
scans = new Map();
policies = new Map();
incidents = new Map();
vulnerabilityDatabases = new Map();
securityPath;
logger;
config;
constructor(securityPath = "./security", logger, config) {
super();
this.securityPath = securityPath;
this.logger = logger || new Logger({ level: "info", format: "text", destination: "console" });
this.config = config || ConfigManager.getInstance();
}
async initialize() {
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) {
const scan = {
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) {
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.message,
});
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) {
const incident = {
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, updates, userId = "system") {
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, scope) {
const checks = [];
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) {
const policy = {
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) {
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"),
byCategory: this.groupBy(allFindings, "category"),
meanTimeToResolution: this.calculateMTTR(allFindings),
};
// Calculate compliance metrics
const allComplianceChecks = scans.flatMap(s => s.compliance.requirements);
const complianceFrameworks = {};
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, fw) => 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"),
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
async loadConfigurations() {
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 = 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 = 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 = 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 });
}
}
async initializeDefaultPolicies() {
const defaultPolicies = [
{
name: "Critical Vulnerability Policy",
description: "Immediate action required for critical vulnerabilities",
type: "scanning",
rules: [
{
name: "Critical CVSS Score",
description: "Alert on vulnerabilities with CVSS score >= 9.0",
condition: "cvss.score >= 9.0",
action: "alert",
severity: "critical",
parameters: { threshold: 9.0 },
enabled: true,
},
],
enforcement: {
level: "blocking",
exceptions: [],
approvers: ["security-lead"],
},
},
{
name: "Secret Detection Policy",
description: "Detect exposed secrets and credentials",
type: "scanning",
rules: [
{
name: "API Key Detection",
description: "Detect exposed API keys",
condition: "category == \"secret\" && type == \"api-key\"",
action: "deny",
severity: "high",
parameters: {},
enabled: true,
},
],
},
];
for (const policyData of defaultPolicies) {
if (!Array.from(this.policies.values()).some(p => p.name === policyData.name)) {
await this.createSecurityPolicy(policyData);
}
}
}
async initializeVulnerabilityDatabases() {
const databases = [
{
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);
}
}
getDefaultScanner(type) {
const scanners = {
"vulnerability": "trivy",
"dependency": "npm-audit",
"code-quality": "sonarqube",
"secrets": "gitleaks",
"compliance": "inspec",
"infrastructure": "checkov",
"container": "clair",
};
return scanners[type] || "generic";
}
async executeScanEngine(scan) {
const findings = [];
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);
}
}
async executeTrivyScan(scan) {
return new Promise((resolve, reject) => {
const findings = [];
// 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",
category: "vulnerability",
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",
priority: "critical",
autoFixable: true,
steps: ["npm update libxml2"],
references: ["https://github.com/GNOME/libxml2/releases"],
},
status: "open",
tags: ["cve", "rce", "dependency"],
metadata: {},
firstSeen: new Date(),
lastSeen: new Date(),
occurrences: 1,
},
];
// Simulate scan delay
setTimeout(() => {
resolve(mockFindings);
}, 2000);
});
}
async executeNpmAuditScan(scan) {
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.message}`));
}
});
child.on("error", (error) => {
reject(error);
});
});
}
async executeGitleaksScan(scan) {
// 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",
category: "secret",
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",
priority: "high",
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",
tags: ["secret", "aws", "credentials"],
metadata: {},
firstSeen: new Date(),
lastSeen: new Date(),
occurrences: 1,
},
];
}
async executeCheckovScan(scan) {
// Mock Checkov scan for infrastructure as code
return [];
}
async executeGenericScan(scan) {
// Generic scan implementation
return [];
}
parseNpmAuditResults(auditResult) {
const findings = [];
if (auditResult.vulnerabilities) {
for (const [packageName, vulnData] of Object.entries(auditResult.vulnerabilities)) {
const vuln = vulnData;
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,
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",
priority: vuln.severity === "info" ? "low" : vuln.severity,
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;
}
calculateScanMetrics(scan) {
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;
}
async runComplianceChecks(scan) {
// 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;
}
async runFrameworkChecks(framework, scope) {
// Mock compliance checks for different frameworks
const mockChecks = [
{
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;
}
async generateRemediationRecommendations(scan) {
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"],
},
];
}
async checkNotificationThresholds(scan) {
const { thresholds } = scan.notifications;
if (scan.metrics.criticalFindings >= thresholds.critical ||
scan.metrics.highFindings >= thresholds.high ||
scan.metrics.mediumFindings >= thresholds.medium) {
await this.sendScanNotification(scan);
}
}
async sendScanNotification(scan) {
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);
}
async autoAssignIncident(incident) {
// Auto-assign based on severity and type
const assignmentRules = {
"critical": ["security-lead", "ciso"],
"high": ["security-team"],
"medium": ["security-analyst"],
"low": ["security-analyst"],
};
incident.response.assignedTo = assignmentRules[incident.severity] || ["security-team"];
}
async sendIncidentNotification(incident) {
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);
}
updateIncidentTimeline(incident, newStatus) {
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;
}
}
async saveScan(scan) {
const filePath = join(this.securityPath, "scans", `${scan.id}.json`);
await writeFile(filePath, JSON.stringify(scan, null, 2));
}
async savePolicy(policy) {
const filePath = join(this.securityPath, "policies", `${policy.id}.json`);
await writeFile(filePath, JSON.stringify(policy, null, 2));
}
async saveIncident(incident) {
const filePath = join(this.securityPath, "incidents", `${incident.id}.json`);
await writeFile(filePath, JSON.stringify(incident, null, 2));
}
addAuditEntry(target, userId, action, targetType, details) {
const entry = {
id: `audit-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
timestamp: new Date(),
userId,
action,
target: targetType,
details,
};
target.auditLog.push(entry);
}
groupBy(array, key) {
return array.reduce((groups, item) => {
const value = String(item[key]);
groups[value] = (groups[value] || 0) + 1;
return groups;
}, {});
}
calculateMTTR(findings) {
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;
}
calculateMTTD(incidents) {
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;
}
calculateMTTResponse(incidents) {
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;
}
calculateIncidentMTTR(incidents) {
const resolvedIncidents = incidents.filter(i => i.timeline.reported && i.timeline.resolved);
if (resolvedIncidents.length === 0)
return 0;
const totalTime = resolvedIncidents.reduce((sum, i) => {
if (!i.timeline.resolved)
return sum;
return sum + (i.timeline.resolved.getTime() - i.timeline.reported.getTime());
}, 0);
return totalTime / resolvedIncidents.length;
}
}
//# sourceMappingURL=security-manager.js.map