@nahisaho/musubix-security 1.8.0 → 1.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/dist/analyzers/ai/index.d.ts +6 -0
- package/dist/analyzers/ai/index.d.ts.map +1 -0
- package/dist/analyzers/ai/index.js +6 -0
- package/dist/analyzers/ai/index.js.map +1 -0
- package/dist/analyzers/ai/prompt-injection-detector.d.ts +152 -0
- package/dist/analyzers/ai/prompt-injection-detector.d.ts.map +1 -0
- package/dist/analyzers/ai/prompt-injection-detector.js +468 -0
- package/dist/analyzers/ai/prompt-injection-detector.js.map +1 -0
- package/dist/analyzers/api/api-security-analyzer.d.ts +263 -0
- package/dist/analyzers/api/api-security-analyzer.d.ts.map +1 -0
- package/dist/analyzers/api/api-security-analyzer.js +581 -0
- package/dist/analyzers/api/api-security-analyzer.js.map +1 -0
- package/dist/analyzers/compliance/compliance-checker.d.ts +201 -0
- package/dist/analyzers/compliance/compliance-checker.d.ts.map +1 -0
- package/dist/analyzers/compliance/compliance-checker.js +772 -0
- package/dist/analyzers/compliance/compliance-checker.js.map +1 -0
- package/dist/analyzers/container/image-scanner.d.ts +163 -0
- package/dist/analyzers/container/image-scanner.d.ts.map +1 -0
- package/dist/analyzers/container/image-scanner.js +459 -0
- package/dist/analyzers/container/image-scanner.js.map +1 -0
- package/dist/analyzers/container/index.d.ts +6 -0
- package/dist/analyzers/container/index.d.ts.map +1 -0
- package/dist/analyzers/container/index.js +6 -0
- package/dist/analyzers/container/index.js.map +1 -0
- package/dist/analyzers/dashboard/security-dashboard.d.ts +286 -0
- package/dist/analyzers/dashboard/security-dashboard.d.ts.map +1 -0
- package/dist/analyzers/dashboard/security-dashboard.js +796 -0
- package/dist/analyzers/dashboard/security-dashboard.js.map +1 -0
- package/dist/analyzers/iac/iac-checker.d.ts +124 -0
- package/dist/analyzers/iac/iac-checker.d.ts.map +1 -0
- package/dist/analyzers/iac/iac-checker.js +755 -0
- package/dist/analyzers/iac/iac-checker.js.map +1 -0
- package/dist/analyzers/iac/index.d.ts +6 -0
- package/dist/analyzers/iac/index.d.ts.map +1 -0
- package/dist/analyzers/iac/index.js +6 -0
- package/dist/analyzers/iac/index.js.map +1 -0
- package/dist/analyzers/index.d.ts +9 -0
- package/dist/analyzers/index.d.ts.map +1 -0
- package/dist/analyzers/index.js +13 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/analyzers/monitor/realtime-monitor.d.ts +216 -0
- package/dist/analyzers/monitor/realtime-monitor.d.ts.map +1 -0
- package/dist/analyzers/monitor/realtime-monitor.js +601 -0
- package/dist/analyzers/monitor/realtime-monitor.js.map +1 -0
- package/dist/analyzers/sast/index.d.ts +7 -0
- package/dist/analyzers/sast/index.d.ts.map +1 -0
- package/dist/analyzers/sast/index.js +7 -0
- package/dist/analyzers/sast/index.js.map +1 -0
- package/dist/analyzers/sast/interprocedural-analyzer.d.ts +276 -0
- package/dist/analyzers/sast/interprocedural-analyzer.d.ts.map +1 -0
- package/dist/analyzers/sast/interprocedural-analyzer.js +635 -0
- package/dist/analyzers/sast/interprocedural-analyzer.js.map +1 -0
- package/dist/analyzers/sast/zero-day-detector.d.ts +183 -0
- package/dist/analyzers/sast/zero-day-detector.d.ts.map +1 -0
- package/dist/analyzers/sast/zero-day-detector.js +593 -0
- package/dist/analyzers/sast/zero-day-detector.js.map +1 -0
- package/dist/analyzers/sca/dependency-scanner.d.ts +275 -0
- package/dist/analyzers/sca/dependency-scanner.d.ts.map +1 -0
- package/dist/analyzers/sca/dependency-scanner.js +642 -0
- package/dist/analyzers/sca/dependency-scanner.js.map +1 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +10 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/pipeline-manager.d.ts +105 -0
- package/dist/core/pipeline-manager.d.ts.map +1 -0
- package/dist/core/pipeline-manager.js +449 -0
- package/dist/core/pipeline-manager.js.map +1 -0
- package/dist/core/result-aggregator.d.ts +96 -0
- package/dist/core/result-aggregator.d.ts.map +1 -0
- package/dist/core/result-aggregator.js +462 -0
- package/dist/core/result-aggregator.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -1
- package/dist/integrations/ci-integration.d.ts +227 -0
- package/dist/integrations/ci-integration.d.ts.map +1 -0
- package/dist/integrations/ci-integration.js +472 -0
- package/dist/integrations/ci-integration.js.map +1 -0
- package/dist/integrations/git-hooks.d.ts +155 -0
- package/dist/integrations/git-hooks.d.ts.map +1 -0
- package/dist/integrations/git-hooks.js +425 -0
- package/dist/integrations/git-hooks.js.map +1 -0
- package/dist/integrations/index.d.ts +9 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +9 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/integrations/report-aggregator.d.ts +250 -0
- package/dist/integrations/report-aggregator.d.ts.map +1 -0
- package/dist/integrations/report-aggregator.js +488 -0
- package/dist/integrations/report-aggregator.js.map +1 -0
- package/dist/integrations/vscode-integration.d.ts +245 -0
- package/dist/integrations/vscode-integration.d.ts.map +1 -0
- package/dist/integrations/vscode-integration.js +449 -0
- package/dist/integrations/vscode-integration.js.map +1 -0
- package/dist/intelligence/attack-pattern-matcher.d.ts +217 -0
- package/dist/intelligence/attack-pattern-matcher.d.ts.map +1 -0
- package/dist/intelligence/attack-pattern-matcher.js +887 -0
- package/dist/intelligence/attack-pattern-matcher.js.map +1 -0
- package/dist/intelligence/index.d.ts +12 -0
- package/dist/intelligence/index.d.ts.map +1 -0
- package/dist/intelligence/index.js +18 -0
- package/dist/intelligence/index.js.map +1 -0
- package/dist/intelligence/neuro-symbolic-core.d.ts +88 -0
- package/dist/intelligence/neuro-symbolic-core.d.ts.map +1 -0
- package/dist/intelligence/neuro-symbolic-core.js +403 -0
- package/dist/intelligence/neuro-symbolic-core.js.map +1 -0
- package/dist/intelligence/predictive-analyzer.d.ts +317 -0
- package/dist/intelligence/predictive-analyzer.d.ts.map +1 -0
- package/dist/intelligence/predictive-analyzer.js +714 -0
- package/dist/intelligence/predictive-analyzer.js.map +1 -0
- package/dist/intelligence/risk-scorer.d.ts +333 -0
- package/dist/intelligence/risk-scorer.d.ts.map +1 -0
- package/dist/intelligence/risk-scorer.js +824 -0
- package/dist/intelligence/risk-scorer.js.map +1 -0
- package/dist/intelligence/security-analytics.d.ts +349 -0
- package/dist/intelligence/security-analytics.d.ts.map +1 -0
- package/dist/intelligence/security-analytics.js +813 -0
- package/dist/intelligence/security-analytics.js.map +1 -0
- package/dist/intelligence/threat-intelligence.d.ts +288 -0
- package/dist/intelligence/threat-intelligence.d.ts.map +1 -0
- package/dist/intelligence/threat-intelligence.js +639 -0
- package/dist/intelligence/threat-intelligence.js.map +1 -0
- package/dist/policy/index.d.ts +6 -0
- package/dist/policy/index.d.ts.map +1 -0
- package/dist/policy/index.js +6 -0
- package/dist/policy/index.js.map +1 -0
- package/dist/policy/policy-engine.d.ts +254 -0
- package/dist/policy/policy-engine.d.ts.map +1 -0
- package/dist/policy/policy-engine.js +651 -0
- package/dist/policy/policy-engine.js.map +1 -0
- package/dist/remediation/auto-fixer.d.ts +179 -0
- package/dist/remediation/auto-fixer.d.ts.map +1 -0
- package/dist/remediation/auto-fixer.js +540 -0
- package/dist/remediation/auto-fixer.js.map +1 -0
- package/dist/remediation/fix-validator.d.ts +195 -0
- package/dist/remediation/fix-validator.d.ts.map +1 -0
- package/dist/remediation/fix-validator.js +462 -0
- package/dist/remediation/fix-validator.js.map +1 -0
- package/dist/remediation/index.d.ts +10 -0
- package/dist/remediation/index.d.ts.map +1 -0
- package/dist/remediation/index.js +15 -0
- package/dist/remediation/index.js.map +1 -0
- package/dist/remediation/patch-generator.d.ts +203 -0
- package/dist/remediation/patch-generator.d.ts.map +1 -0
- package/dist/remediation/patch-generator.js +533 -0
- package/dist/remediation/patch-generator.js.map +1 -0
- package/dist/remediation/remediation-planner.d.ts +262 -0
- package/dist/remediation/remediation-planner.d.ts.map +1 -0
- package/dist/remediation/remediation-planner.js +531 -0
- package/dist/remediation/remediation-planner.js.map +1 -0
- package/dist/remediation/secure-code-transformer.d.ts +222 -0
- package/dist/remediation/secure-code-transformer.d.ts.map +1 -0
- package/dist/remediation/secure-code-transformer.js +625 -0
- package/dist/remediation/secure-code-transformer.js.map +1 -0
- package/dist/types/fix.d.ts +3 -1
- package/dist/types/fix.d.ts.map +1 -1
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/interprocedural.d.ts +203 -0
- package/dist/types/interprocedural.d.ts.map +1 -0
- package/dist/types/interprocedural.js +7 -0
- package/dist/types/interprocedural.js.map +1 -0
- package/dist/types/neuro-symbolic.d.ts +179 -0
- package/dist/types/neuro-symbolic.d.ts.map +1 -0
- package/dist/types/neuro-symbolic.js +7 -0
- package/dist/types/neuro-symbolic.js.map +1 -0
- package/dist/types/pipeline.d.ts +173 -0
- package/dist/types/pipeline.d.ts.map +1 -0
- package/dist/types/pipeline.js +7 -0
- package/dist/types/pipeline.js.map +1 -0
- package/dist/types/result.d.ts +134 -0
- package/dist/types/result.d.ts.map +1 -0
- package/dist/types/result.js +25 -0
- package/dist/types/result.js.map +1 -0
- package/dist/types/vulnerability.d.ts +2 -2
- package/dist/types/vulnerability.d.ts.map +1 -1
- package/dist/types/zero-day.d.ts +146 -0
- package/dist/types/zero-day.d.ts.map +1 -0
- package/dist/types/zero-day.js +7 -0
- package/dist/types/zero-day.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Security Dashboard - Integrated security reporting and visualization
|
|
3
|
+
* @module @nahisaho/musubix-security/analyzers/dashboard/security-dashboard
|
|
4
|
+
* @trace DES-SEC3-DASH-001, REQ-SEC3-DASH-001
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* OWASP Top 10 2021 categories
|
|
8
|
+
*/
|
|
9
|
+
const OWASP_TOP_10 = [
|
|
10
|
+
{
|
|
11
|
+
id: 'A01:2021',
|
|
12
|
+
name: 'Broken Access Control',
|
|
13
|
+
patterns: ['authorization', 'access-control', 'idor', 'privilege'],
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: 'A02:2021',
|
|
17
|
+
name: 'Cryptographic Failures',
|
|
18
|
+
patterns: ['crypto', 'encryption', 'weak-cipher', 'hardcoded-key'],
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 'A03:2021',
|
|
22
|
+
name: 'Injection',
|
|
23
|
+
patterns: ['sql-injection', 'command-injection', 'xss', 'code-injection', 'ldap-injection'],
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'A04:2021',
|
|
27
|
+
name: 'Insecure Design',
|
|
28
|
+
patterns: ['insecure-design', 'missing-auth', 'business-logic'],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'A05:2021',
|
|
32
|
+
name: 'Security Misconfiguration',
|
|
33
|
+
patterns: ['misconfiguration', 'default-credentials', 'verbose-errors'],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'A06:2021',
|
|
37
|
+
name: 'Vulnerable and Outdated Components',
|
|
38
|
+
patterns: ['vulnerable-dependency', 'outdated', 'component'],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: 'A07:2021',
|
|
42
|
+
name: 'Identification and Authentication Failures',
|
|
43
|
+
patterns: ['authentication', 'session', 'weak-password', 'brute-force'],
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: 'A08:2021',
|
|
47
|
+
name: 'Software and Data Integrity Failures',
|
|
48
|
+
patterns: ['integrity', 'deserialization', 'ci-cd', 'supply-chain'],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'A09:2021',
|
|
52
|
+
name: 'Security Logging and Monitoring Failures',
|
|
53
|
+
patterns: ['logging', 'monitoring', 'audit'],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 'A10:2021',
|
|
57
|
+
name: 'Server-Side Request Forgery (SSRF)',
|
|
58
|
+
patterns: ['ssrf', 'request-forgery'],
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
/**
|
|
62
|
+
* CWE names mapping (common ones)
|
|
63
|
+
*/
|
|
64
|
+
const CWE_NAMES = {
|
|
65
|
+
'CWE-20': 'Improper Input Validation',
|
|
66
|
+
'CWE-22': 'Path Traversal',
|
|
67
|
+
'CWE-78': 'OS Command Injection',
|
|
68
|
+
'CWE-79': 'Cross-site Scripting (XSS)',
|
|
69
|
+
'CWE-89': 'SQL Injection',
|
|
70
|
+
'CWE-94': 'Code Injection',
|
|
71
|
+
'CWE-200': 'Information Exposure',
|
|
72
|
+
'CWE-287': 'Improper Authentication',
|
|
73
|
+
'CWE-306': 'Missing Authentication',
|
|
74
|
+
'CWE-311': 'Missing Encryption',
|
|
75
|
+
'CWE-327': 'Weak Cryptography',
|
|
76
|
+
'CWE-352': 'Cross-Site Request Forgery (CSRF)',
|
|
77
|
+
'CWE-400': 'Resource Exhaustion',
|
|
78
|
+
'CWE-502': 'Deserialization of Untrusted Data',
|
|
79
|
+
'CWE-522': 'Insufficiently Protected Credentials',
|
|
80
|
+
'CWE-601': 'Open Redirect',
|
|
81
|
+
'CWE-611': 'XXE',
|
|
82
|
+
'CWE-798': 'Hardcoded Credentials',
|
|
83
|
+
'CWE-918': 'Server-Side Request Forgery (SSRF)',
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Security Dashboard
|
|
87
|
+
* @trace DES-SEC3-DASH-001
|
|
88
|
+
*/
|
|
89
|
+
export class SecurityDashboard {
|
|
90
|
+
config;
|
|
91
|
+
vulnerabilities = [];
|
|
92
|
+
scanResults = [];
|
|
93
|
+
trendHistory = [];
|
|
94
|
+
constructor(config) {
|
|
95
|
+
this.config = {
|
|
96
|
+
projectName: config.projectName,
|
|
97
|
+
format: config.format ?? 'json',
|
|
98
|
+
includeTrends: config.includeTrends ?? true,
|
|
99
|
+
includeRecommendations: config.includeRecommendations ?? true,
|
|
100
|
+
branding: config.branding ?? {},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Add scan results to dashboard
|
|
105
|
+
* @trace REQ-SEC3-DASH-001
|
|
106
|
+
*/
|
|
107
|
+
addScanResult(result) {
|
|
108
|
+
this.scanResults.push(result);
|
|
109
|
+
this.vulnerabilities.push(...result.vulnerabilities);
|
|
110
|
+
// Add to trend history
|
|
111
|
+
this.trendHistory.push({
|
|
112
|
+
timestamp: result.scannedAt,
|
|
113
|
+
totalVulnerabilities: this.vulnerabilities.length,
|
|
114
|
+
critical: result.summary.critical,
|
|
115
|
+
high: result.summary.high,
|
|
116
|
+
medium: result.summary.medium,
|
|
117
|
+
low: result.summary.low,
|
|
118
|
+
securityScore: this.calculateSecurityScore(this.vulnerabilities),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Add vulnerabilities directly
|
|
123
|
+
*/
|
|
124
|
+
addVulnerabilities(vulnerabilities) {
|
|
125
|
+
this.vulnerabilities.push(...vulnerabilities);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Clear all data
|
|
129
|
+
*/
|
|
130
|
+
clear() {
|
|
131
|
+
this.vulnerabilities = [];
|
|
132
|
+
this.scanResults = [];
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Generate dashboard report
|
|
136
|
+
*/
|
|
137
|
+
generateReport() {
|
|
138
|
+
const metrics = this.calculateMetrics();
|
|
139
|
+
const topVulnerabilities = this.getTopVulnerabilities();
|
|
140
|
+
const componentRisks = this.calculateComponentRisks();
|
|
141
|
+
const summary = this.generateExecutiveSummary(metrics, topVulnerabilities);
|
|
142
|
+
const report = {
|
|
143
|
+
generatedAt: new Date(),
|
|
144
|
+
projectName: this.config.projectName,
|
|
145
|
+
summary,
|
|
146
|
+
metrics,
|
|
147
|
+
topVulnerabilities,
|
|
148
|
+
componentRisks,
|
|
149
|
+
vulnerabilities: this.vulnerabilities,
|
|
150
|
+
scanSummary: this.generateScanSummary(),
|
|
151
|
+
};
|
|
152
|
+
if (this.config.includeTrends) {
|
|
153
|
+
report.trends = this.calculateTrends();
|
|
154
|
+
}
|
|
155
|
+
if (this.config.includeRecommendations) {
|
|
156
|
+
report.recommendations = this.generateRecommendations(metrics, topVulnerabilities);
|
|
157
|
+
}
|
|
158
|
+
return report;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Export report in specified format
|
|
162
|
+
*/
|
|
163
|
+
exportReport(format) {
|
|
164
|
+
const report = this.generateReport();
|
|
165
|
+
const outputFormat = format ?? this.config.format;
|
|
166
|
+
switch (outputFormat) {
|
|
167
|
+
case 'html':
|
|
168
|
+
return this.toHTML(report);
|
|
169
|
+
case 'markdown':
|
|
170
|
+
return this.toMarkdown(report);
|
|
171
|
+
default:
|
|
172
|
+
return JSON.stringify(report, null, 2);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Calculate security metrics
|
|
177
|
+
*/
|
|
178
|
+
calculateMetrics() {
|
|
179
|
+
const bySeverity = {
|
|
180
|
+
critical: 0,
|
|
181
|
+
high: 0,
|
|
182
|
+
medium: 0,
|
|
183
|
+
low: 0,
|
|
184
|
+
info: 0,
|
|
185
|
+
};
|
|
186
|
+
const byType = {};
|
|
187
|
+
const byComponent = {};
|
|
188
|
+
const cweCount = {};
|
|
189
|
+
const owaspCount = {};
|
|
190
|
+
for (const vuln of this.vulnerabilities) {
|
|
191
|
+
// By severity
|
|
192
|
+
bySeverity[vuln.severity]++;
|
|
193
|
+
// By type
|
|
194
|
+
byType[vuln.type] = (byType[vuln.type] ?? 0) + 1;
|
|
195
|
+
// By component (file)
|
|
196
|
+
const component = vuln.location.file;
|
|
197
|
+
byComponent[component] = (byComponent[component] ?? 0) + 1;
|
|
198
|
+
// CWE distribution
|
|
199
|
+
for (const cwe of vuln.cwes ?? []) {
|
|
200
|
+
cweCount[cwe] = (cweCount[cwe] ?? 0) + 1;
|
|
201
|
+
}
|
|
202
|
+
// OWASP coverage
|
|
203
|
+
for (const owasp of vuln.owasp ?? []) {
|
|
204
|
+
owaspCount[owasp] = (owaspCount[owasp] ?? 0) + 1;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const total = this.vulnerabilities.length;
|
|
208
|
+
// Calculate OWASP coverage
|
|
209
|
+
const owaspCoverage = OWASP_TOP_10.map(owasp => ({
|
|
210
|
+
id: owasp.id,
|
|
211
|
+
name: owasp.name,
|
|
212
|
+
count: owaspCount[owasp.id] ?? 0,
|
|
213
|
+
percentage: total > 0 ? Math.round(((owaspCount[owasp.id] ?? 0) / total) * 100) : 0,
|
|
214
|
+
}));
|
|
215
|
+
// Calculate CWE distribution
|
|
216
|
+
const cweDistribution = Object.entries(cweCount)
|
|
217
|
+
.sort(([, a], [, b]) => b - a)
|
|
218
|
+
.slice(0, 10)
|
|
219
|
+
.map(([cwe, count]) => ({
|
|
220
|
+
cwe,
|
|
221
|
+
name: CWE_NAMES[cwe] ?? cwe,
|
|
222
|
+
count,
|
|
223
|
+
percentage: total > 0 ? Math.round((count / total) * 100) : 0,
|
|
224
|
+
}));
|
|
225
|
+
const securityScore = this.calculateSecurityScore(this.vulnerabilities);
|
|
226
|
+
const riskLevel = this.determineRiskLevel(bySeverity, securityScore);
|
|
227
|
+
return {
|
|
228
|
+
totalVulnerabilities: total,
|
|
229
|
+
bySeverity,
|
|
230
|
+
byType,
|
|
231
|
+
byComponent,
|
|
232
|
+
owaspCoverage,
|
|
233
|
+
cweDistribution,
|
|
234
|
+
securityScore,
|
|
235
|
+
riskLevel,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Calculate security score
|
|
240
|
+
*/
|
|
241
|
+
calculateSecurityScore(vulnerabilities) {
|
|
242
|
+
let score = 100;
|
|
243
|
+
for (const vuln of vulnerabilities) {
|
|
244
|
+
switch (vuln.severity) {
|
|
245
|
+
case 'critical':
|
|
246
|
+
score -= 15;
|
|
247
|
+
break;
|
|
248
|
+
case 'high':
|
|
249
|
+
score -= 10;
|
|
250
|
+
break;
|
|
251
|
+
case 'medium':
|
|
252
|
+
score -= 5;
|
|
253
|
+
break;
|
|
254
|
+
case 'low':
|
|
255
|
+
score -= 2;
|
|
256
|
+
break;
|
|
257
|
+
case 'info':
|
|
258
|
+
score -= 1;
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return Math.max(0, Math.min(100, score));
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Determine risk level
|
|
266
|
+
*/
|
|
267
|
+
determineRiskLevel(bySeverity, score) {
|
|
268
|
+
if (bySeverity.critical > 0 || score < 30)
|
|
269
|
+
return 'critical';
|
|
270
|
+
if (bySeverity.high > 2 || score < 50)
|
|
271
|
+
return 'high';
|
|
272
|
+
if (bySeverity.high > 0 || bySeverity.medium > 5 || score < 70)
|
|
273
|
+
return 'medium';
|
|
274
|
+
if (bySeverity.medium > 0 || score < 90)
|
|
275
|
+
return 'low';
|
|
276
|
+
return 'minimal';
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Get top vulnerabilities
|
|
280
|
+
*/
|
|
281
|
+
getTopVulnerabilities() {
|
|
282
|
+
const typeMap = new Map();
|
|
283
|
+
for (const vuln of this.vulnerabilities) {
|
|
284
|
+
const existing = typeMap.get(vuln.type);
|
|
285
|
+
if (existing) {
|
|
286
|
+
existing.count++;
|
|
287
|
+
for (const cwe of vuln.cwes ?? []) {
|
|
288
|
+
existing.cwes.add(cwe);
|
|
289
|
+
}
|
|
290
|
+
// Keep highest severity
|
|
291
|
+
if (this.severityValue(vuln.severity) > this.severityValue(existing.severity)) {
|
|
292
|
+
existing.severity = vuln.severity;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
typeMap.set(vuln.type, {
|
|
297
|
+
count: 1,
|
|
298
|
+
severity: vuln.severity,
|
|
299
|
+
cwes: new Set(vuln.cwes ?? []),
|
|
300
|
+
recommendation: vuln.recommendation ?? 'Review and fix the vulnerability',
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return Array.from(typeMap.entries())
|
|
305
|
+
.map(([type, data]) => ({
|
|
306
|
+
type,
|
|
307
|
+
count: data.count,
|
|
308
|
+
severity: data.severity,
|
|
309
|
+
cwes: Array.from(data.cwes),
|
|
310
|
+
recommendation: data.recommendation,
|
|
311
|
+
}))
|
|
312
|
+
.sort((a, b) => {
|
|
313
|
+
const severityDiff = this.severityValue(b.severity) - this.severityValue(a.severity);
|
|
314
|
+
return severityDiff !== 0 ? severityDiff : b.count - a.count;
|
|
315
|
+
})
|
|
316
|
+
.slice(0, 10);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Calculate component risks
|
|
320
|
+
*/
|
|
321
|
+
calculateComponentRisks() {
|
|
322
|
+
const componentMap = new Map();
|
|
323
|
+
for (const vuln of this.vulnerabilities) {
|
|
324
|
+
const component = vuln.location.file;
|
|
325
|
+
const existing = componentMap.get(component) ?? { total: 0, critical: 0, high: 0 };
|
|
326
|
+
existing.total++;
|
|
327
|
+
if (vuln.severity === 'critical')
|
|
328
|
+
existing.critical++;
|
|
329
|
+
if (vuln.severity === 'high')
|
|
330
|
+
existing.high++;
|
|
331
|
+
componentMap.set(component, existing);
|
|
332
|
+
}
|
|
333
|
+
return Array.from(componentMap.entries())
|
|
334
|
+
.map(([component, data]) => ({
|
|
335
|
+
component,
|
|
336
|
+
vulnerabilityCount: data.total,
|
|
337
|
+
criticalCount: data.critical,
|
|
338
|
+
highCount: data.high,
|
|
339
|
+
riskScore: data.critical * 15 + data.high * 10 + (data.total - data.critical - data.high) * 3,
|
|
340
|
+
}))
|
|
341
|
+
.sort((a, b) => b.riskScore - a.riskScore)
|
|
342
|
+
.slice(0, 10);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Generate executive summary
|
|
346
|
+
*/
|
|
347
|
+
generateExecutiveSummary(metrics, topVulns) {
|
|
348
|
+
const { bySeverity, securityScore, riskLevel } = metrics;
|
|
349
|
+
let status;
|
|
350
|
+
let statusMessage;
|
|
351
|
+
if (riskLevel === 'critical') {
|
|
352
|
+
status = 'critical';
|
|
353
|
+
statusMessage = `Critical security issues detected. Immediate attention required.`;
|
|
354
|
+
}
|
|
355
|
+
else if (riskLevel === 'high') {
|
|
356
|
+
status = 'warning';
|
|
357
|
+
statusMessage = `High-priority security issues found. Prompt remediation recommended.`;
|
|
358
|
+
}
|
|
359
|
+
else if (riskLevel === 'medium') {
|
|
360
|
+
status = 'attention';
|
|
361
|
+
statusMessage = `Medium-priority security issues identified. Plan for remediation.`;
|
|
362
|
+
}
|
|
363
|
+
else if (riskLevel === 'low') {
|
|
364
|
+
status = 'good';
|
|
365
|
+
statusMessage = `Security posture is good with minor issues to address.`;
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
status = 'excellent';
|
|
369
|
+
statusMessage = `Excellent security posture. Continue monitoring.`;
|
|
370
|
+
}
|
|
371
|
+
const highlights = [];
|
|
372
|
+
highlights.push(`Security Score: ${securityScore}/100`);
|
|
373
|
+
highlights.push(`Total Vulnerabilities: ${metrics.totalVulnerabilities}`);
|
|
374
|
+
if (bySeverity.critical > 0) {
|
|
375
|
+
highlights.push(`⚠️ ${bySeverity.critical} critical issue(s) require immediate attention`);
|
|
376
|
+
}
|
|
377
|
+
if (bySeverity.high > 0) {
|
|
378
|
+
highlights.push(`🔴 ${bySeverity.high} high severity issue(s) found`);
|
|
379
|
+
}
|
|
380
|
+
const keyFindings = [];
|
|
381
|
+
for (const vuln of topVulns.slice(0, 3)) {
|
|
382
|
+
keyFindings.push(`${vuln.type}: ${vuln.count} occurrence(s) (${vuln.severity})`);
|
|
383
|
+
}
|
|
384
|
+
const immediateActions = [];
|
|
385
|
+
if (bySeverity.critical > 0) {
|
|
386
|
+
immediateActions.push('Address all critical vulnerabilities immediately');
|
|
387
|
+
}
|
|
388
|
+
if (bySeverity.high > 0) {
|
|
389
|
+
immediateActions.push('Prioritize remediation of high-severity issues');
|
|
390
|
+
}
|
|
391
|
+
const topType = topVulns[0];
|
|
392
|
+
if (topType) {
|
|
393
|
+
immediateActions.push(`Focus on most common issue type: ${topType.type}`);
|
|
394
|
+
}
|
|
395
|
+
return {
|
|
396
|
+
status,
|
|
397
|
+
statusMessage,
|
|
398
|
+
highlights,
|
|
399
|
+
keyFindings,
|
|
400
|
+
immediateActions,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Generate scan summary
|
|
405
|
+
*/
|
|
406
|
+
generateScanSummary() {
|
|
407
|
+
const totalFiles = new Set(this.vulnerabilities.map(v => v.location.file)).size;
|
|
408
|
+
const lastScan = this.scanResults.length > 0
|
|
409
|
+
? this.scanResults[this.scanResults.length - 1].scannedAt
|
|
410
|
+
: new Date();
|
|
411
|
+
return {
|
|
412
|
+
totalScans: this.scanResults.length,
|
|
413
|
+
filesScanned: this.scanResults.reduce((sum, r) => sum + r.filesScanned, 0) || totalFiles,
|
|
414
|
+
lastScanDate: lastScan,
|
|
415
|
+
coverage: 100, // Placeholder
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Calculate trends
|
|
420
|
+
*/
|
|
421
|
+
calculateTrends() {
|
|
422
|
+
const dataPoints = this.trendHistory.slice(-30); // Last 30 data points
|
|
423
|
+
const firstScore = dataPoints[0]?.securityScore ?? 100;
|
|
424
|
+
const lastScore = dataPoints[dataPoints.length - 1]?.securityScore ?? 100;
|
|
425
|
+
const improvement = lastScore - firstScore;
|
|
426
|
+
let direction;
|
|
427
|
+
if (improvement > 5)
|
|
428
|
+
direction = 'improving';
|
|
429
|
+
else if (improvement < -5)
|
|
430
|
+
direction = 'degrading';
|
|
431
|
+
else
|
|
432
|
+
direction = 'stable';
|
|
433
|
+
return {
|
|
434
|
+
period: 'day',
|
|
435
|
+
dataPoints,
|
|
436
|
+
improvement,
|
|
437
|
+
direction,
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Generate recommendations
|
|
442
|
+
*/
|
|
443
|
+
generateRecommendations(metrics, topVulns) {
|
|
444
|
+
const recommendations = [];
|
|
445
|
+
// Critical/High severity recommendations
|
|
446
|
+
if (metrics.bySeverity.critical > 0) {
|
|
447
|
+
recommendations.push({
|
|
448
|
+
priority: 'critical',
|
|
449
|
+
category: 'Remediation',
|
|
450
|
+
title: 'Address Critical Vulnerabilities',
|
|
451
|
+
description: `${metrics.bySeverity.critical} critical vulnerabilities require immediate attention`,
|
|
452
|
+
impact: 'System compromise, data breach risk',
|
|
453
|
+
effort: 'high',
|
|
454
|
+
affectedCount: metrics.bySeverity.critical,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
if (metrics.bySeverity.high > 0) {
|
|
458
|
+
recommendations.push({
|
|
459
|
+
priority: 'high',
|
|
460
|
+
category: 'Remediation',
|
|
461
|
+
title: 'Fix High-Severity Issues',
|
|
462
|
+
description: `${metrics.bySeverity.high} high-severity vulnerabilities should be addressed promptly`,
|
|
463
|
+
impact: 'Significant security exposure',
|
|
464
|
+
effort: 'medium',
|
|
465
|
+
affectedCount: metrics.bySeverity.high,
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
// Type-specific recommendations
|
|
469
|
+
for (const vuln of topVulns.slice(0, 3)) {
|
|
470
|
+
if (vuln.count >= 3) {
|
|
471
|
+
recommendations.push({
|
|
472
|
+
priority: this.severityToPriority(vuln.severity),
|
|
473
|
+
category: 'Pattern',
|
|
474
|
+
title: `Reduce ${vuln.type} Vulnerabilities`,
|
|
475
|
+
description: vuln.recommendation,
|
|
476
|
+
impact: `${vuln.count} occurrences across the codebase`,
|
|
477
|
+
effort: 'medium',
|
|
478
|
+
affectedCount: vuln.count,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
// General recommendations
|
|
483
|
+
if (metrics.securityScore < 80) {
|
|
484
|
+
recommendations.push({
|
|
485
|
+
priority: 'medium',
|
|
486
|
+
category: 'Process',
|
|
487
|
+
title: 'Implement Security Code Review',
|
|
488
|
+
description: 'Establish mandatory security review for all code changes',
|
|
489
|
+
impact: 'Prevent introduction of new vulnerabilities',
|
|
490
|
+
effort: 'medium',
|
|
491
|
+
affectedCount: 0,
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
if (metrics.totalVulnerabilities > 20) {
|
|
495
|
+
recommendations.push({
|
|
496
|
+
priority: 'medium',
|
|
497
|
+
category: 'Process',
|
|
498
|
+
title: 'Security Sprint Planning',
|
|
499
|
+
description: 'Dedicate engineering time specifically for security remediation',
|
|
500
|
+
impact: 'Systematic reduction of vulnerability backlog',
|
|
501
|
+
effort: 'high',
|
|
502
|
+
affectedCount: metrics.totalVulnerabilities,
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
return recommendations.slice(0, 10);
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Convert severity to numeric value
|
|
509
|
+
*/
|
|
510
|
+
severityValue(severity) {
|
|
511
|
+
const values = {
|
|
512
|
+
critical: 4,
|
|
513
|
+
high: 3,
|
|
514
|
+
medium: 2,
|
|
515
|
+
low: 1,
|
|
516
|
+
info: 0,
|
|
517
|
+
};
|
|
518
|
+
return values[severity];
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Convert severity to priority
|
|
522
|
+
*/
|
|
523
|
+
severityToPriority(severity) {
|
|
524
|
+
const mapping = {
|
|
525
|
+
critical: 'critical',
|
|
526
|
+
high: 'high',
|
|
527
|
+
medium: 'medium',
|
|
528
|
+
low: 'low',
|
|
529
|
+
info: 'low',
|
|
530
|
+
};
|
|
531
|
+
return mapping[severity];
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Export to HTML format
|
|
535
|
+
*/
|
|
536
|
+
toHTML(report) {
|
|
537
|
+
const colors = this.config.branding.colors ?? {
|
|
538
|
+
critical: '#dc2626',
|
|
539
|
+
high: '#ea580c',
|
|
540
|
+
medium: '#ca8a04',
|
|
541
|
+
low: '#16a34a',
|
|
542
|
+
info: '#2563eb',
|
|
543
|
+
};
|
|
544
|
+
return `<!DOCTYPE html>
|
|
545
|
+
<html lang="en">
|
|
546
|
+
<head>
|
|
547
|
+
<meta charset="UTF-8">
|
|
548
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
549
|
+
<title>${this.config.branding.title ?? 'Security Dashboard'} - ${report.projectName}</title>
|
|
550
|
+
<style>
|
|
551
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
552
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f3f4f6; color: #1f2937; line-height: 1.5; }
|
|
553
|
+
.container { max-width: 1200px; margin: 0 auto; padding: 2rem; }
|
|
554
|
+
.header { background: #1f2937; color: white; padding: 2rem; margin-bottom: 2rem; border-radius: 8px; }
|
|
555
|
+
.header h1 { font-size: 1.5rem; }
|
|
556
|
+
.header p { color: #9ca3af; margin-top: 0.5rem; }
|
|
557
|
+
.card { background: white; border-radius: 8px; padding: 1.5rem; margin-bottom: 1rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
|
|
558
|
+
.card h2 { font-size: 1.125rem; margin-bottom: 1rem; color: #374151; }
|
|
559
|
+
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; }
|
|
560
|
+
.metric { text-align: center; padding: 1rem; }
|
|
561
|
+
.metric .value { font-size: 2rem; font-weight: bold; }
|
|
562
|
+
.metric .label { color: #6b7280; font-size: 0.875rem; }
|
|
563
|
+
.severity-critical { color: ${colors.critical}; }
|
|
564
|
+
.severity-high { color: ${colors.high}; }
|
|
565
|
+
.severity-medium { color: ${colors.medium}; }
|
|
566
|
+
.severity-low { color: ${colors.low}; }
|
|
567
|
+
.severity-info { color: ${colors.info}; }
|
|
568
|
+
.badge { display: inline-block; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem; font-weight: 600; }
|
|
569
|
+
.badge-critical { background: ${colors.critical}20; color: ${colors.critical}; }
|
|
570
|
+
.badge-high { background: ${colors.high}20; color: ${colors.high}; }
|
|
571
|
+
.badge-medium { background: ${colors.medium}20; color: ${colors.medium}; }
|
|
572
|
+
.badge-low { background: ${colors.low}20; color: ${colors.low}; }
|
|
573
|
+
.badge-info { background: ${colors.info}20; color: ${colors.info}; }
|
|
574
|
+
table { width: 100%; border-collapse: collapse; }
|
|
575
|
+
th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #e5e7eb; }
|
|
576
|
+
th { background: #f9fafb; font-weight: 600; }
|
|
577
|
+
.status-${report.summary.status} { border-left: 4px solid ${this.getStatusColor(report.summary.status, colors)}; }
|
|
578
|
+
.score-circle { width: 120px; height: 120px; border-radius: 50%; background: conic-gradient(#10b981 ${report.metrics.securityScore * 3.6}deg, #e5e7eb 0deg); display: flex; align-items: center; justify-content: center; margin: 0 auto; }
|
|
579
|
+
.score-inner { width: 100px; height: 100px; border-radius: 50%; background: white; display: flex; align-items: center; justify-content: center; flex-direction: column; }
|
|
580
|
+
.score-value { font-size: 2rem; font-weight: bold; }
|
|
581
|
+
.list { list-style: none; }
|
|
582
|
+
.list li { padding: 0.5rem 0; border-bottom: 1px solid #f3f4f6; }
|
|
583
|
+
.list li:last-child { border-bottom: none; }
|
|
584
|
+
</style>
|
|
585
|
+
</head>
|
|
586
|
+
<body>
|
|
587
|
+
<div class="container">
|
|
588
|
+
<div class="header">
|
|
589
|
+
<h1>${this.config.branding.title ?? 'Security Dashboard'}</h1>
|
|
590
|
+
<p>${report.projectName} - Generated: ${report.generatedAt.toISOString()}</p>
|
|
591
|
+
</div>
|
|
592
|
+
|
|
593
|
+
<div class="card status-${report.summary.status}">
|
|
594
|
+
<h2>Executive Summary</h2>
|
|
595
|
+
<p><strong>${report.summary.statusMessage}</strong></p>
|
|
596
|
+
<ul class="list" style="margin-top: 1rem;">
|
|
597
|
+
${report.summary.highlights.map(h => `<li>${h}</li>`).join('')}
|
|
598
|
+
</ul>
|
|
599
|
+
</div>
|
|
600
|
+
|
|
601
|
+
<div class="grid">
|
|
602
|
+
<div class="card">
|
|
603
|
+
<h2>Security Score</h2>
|
|
604
|
+
<div class="score-circle">
|
|
605
|
+
<div class="score-inner">
|
|
606
|
+
<div class="score-value">${report.metrics.securityScore}</div>
|
|
607
|
+
<div class="label">/100</div>
|
|
608
|
+
</div>
|
|
609
|
+
</div>
|
|
610
|
+
</div>
|
|
611
|
+
|
|
612
|
+
<div class="card">
|
|
613
|
+
<h2>Vulnerabilities by Severity</h2>
|
|
614
|
+
<div class="metric">
|
|
615
|
+
<div class="value severity-critical">${report.metrics.bySeverity.critical}</div>
|
|
616
|
+
<div class="label">Critical</div>
|
|
617
|
+
</div>
|
|
618
|
+
<div class="metric">
|
|
619
|
+
<div class="value severity-high">${report.metrics.bySeverity.high}</div>
|
|
620
|
+
<div class="label">High</div>
|
|
621
|
+
</div>
|
|
622
|
+
<div class="metric">
|
|
623
|
+
<div class="value severity-medium">${report.metrics.bySeverity.medium}</div>
|
|
624
|
+
<div class="label">Medium</div>
|
|
625
|
+
</div>
|
|
626
|
+
<div class="metric">
|
|
627
|
+
<div class="value severity-low">${report.metrics.bySeverity.low}</div>
|
|
628
|
+
<div class="label">Low</div>
|
|
629
|
+
</div>
|
|
630
|
+
</div>
|
|
631
|
+
</div>
|
|
632
|
+
|
|
633
|
+
<div class="card">
|
|
634
|
+
<h2>Top Vulnerabilities</h2>
|
|
635
|
+
<table>
|
|
636
|
+
<thead>
|
|
637
|
+
<tr><th>Type</th><th>Severity</th><th>Count</th><th>Recommendation</th></tr>
|
|
638
|
+
</thead>
|
|
639
|
+
<tbody>
|
|
640
|
+
${report.topVulnerabilities.map(v => `
|
|
641
|
+
<tr>
|
|
642
|
+
<td>${v.type}</td>
|
|
643
|
+
<td><span class="badge badge-${v.severity}">${v.severity.toUpperCase()}</span></td>
|
|
644
|
+
<td>${v.count}</td>
|
|
645
|
+
<td>${v.recommendation}</td>
|
|
646
|
+
</tr>
|
|
647
|
+
`).join('')}
|
|
648
|
+
</tbody>
|
|
649
|
+
</table>
|
|
650
|
+
</div>
|
|
651
|
+
|
|
652
|
+
<div class="card">
|
|
653
|
+
<h2>Component Risks</h2>
|
|
654
|
+
<table>
|
|
655
|
+
<thead>
|
|
656
|
+
<tr><th>Component</th><th>Vulnerabilities</th><th>Critical</th><th>High</th><th>Risk Score</th></tr>
|
|
657
|
+
</thead>
|
|
658
|
+
<tbody>
|
|
659
|
+
${report.componentRisks.map(c => `
|
|
660
|
+
<tr>
|
|
661
|
+
<td><code>${c.component}</code></td>
|
|
662
|
+
<td>${c.vulnerabilityCount}</td>
|
|
663
|
+
<td class="severity-critical">${c.criticalCount}</td>
|
|
664
|
+
<td class="severity-high">${c.highCount}</td>
|
|
665
|
+
<td>${c.riskScore}</td>
|
|
666
|
+
</tr>
|
|
667
|
+
`).join('')}
|
|
668
|
+
</tbody>
|
|
669
|
+
</table>
|
|
670
|
+
</div>
|
|
671
|
+
|
|
672
|
+
${report.recommendations ? `
|
|
673
|
+
<div class="card">
|
|
674
|
+
<h2>Recommendations</h2>
|
|
675
|
+
<ul class="list">
|
|
676
|
+
${report.recommendations.map(r => `
|
|
677
|
+
<li>
|
|
678
|
+
<span class="badge badge-${r.priority}">${r.priority.toUpperCase()}</span>
|
|
679
|
+
<strong>${r.title}</strong>: ${r.description}
|
|
680
|
+
</li>
|
|
681
|
+
`).join('')}
|
|
682
|
+
</ul>
|
|
683
|
+
</div>
|
|
684
|
+
` : ''}
|
|
685
|
+
</div>
|
|
686
|
+
</body>
|
|
687
|
+
</html>`;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Export to Markdown format
|
|
691
|
+
*/
|
|
692
|
+
toMarkdown(report) {
|
|
693
|
+
return `# Security Dashboard - ${report.projectName}
|
|
694
|
+
|
|
695
|
+
Generated: ${report.generatedAt.toISOString()}
|
|
696
|
+
|
|
697
|
+
## Executive Summary
|
|
698
|
+
|
|
699
|
+
**Status: ${report.summary.status.toUpperCase()}**
|
|
700
|
+
|
|
701
|
+
${report.summary.statusMessage}
|
|
702
|
+
|
|
703
|
+
### Highlights
|
|
704
|
+
${report.summary.highlights.map(h => `- ${h}`).join('\n')}
|
|
705
|
+
|
|
706
|
+
### Key Findings
|
|
707
|
+
${report.summary.keyFindings.map(f => `- ${f}`).join('\n')}
|
|
708
|
+
|
|
709
|
+
### Immediate Actions
|
|
710
|
+
${report.summary.immediateActions.map(a => `- ${a}`).join('\n')}
|
|
711
|
+
|
|
712
|
+
---
|
|
713
|
+
|
|
714
|
+
## Security Metrics
|
|
715
|
+
|
|
716
|
+
| Metric | Value |
|
|
717
|
+
|--------|-------|
|
|
718
|
+
| Security Score | ${report.metrics.securityScore}/100 |
|
|
719
|
+
| Risk Level | ${report.metrics.riskLevel} |
|
|
720
|
+
| Total Vulnerabilities | ${report.metrics.totalVulnerabilities} |
|
|
721
|
+
|
|
722
|
+
### By Severity
|
|
723
|
+
|
|
724
|
+
| Severity | Count |
|
|
725
|
+
|----------|-------|
|
|
726
|
+
| Critical | ${report.metrics.bySeverity.critical} |
|
|
727
|
+
| High | ${report.metrics.bySeverity.high} |
|
|
728
|
+
| Medium | ${report.metrics.bySeverity.medium} |
|
|
729
|
+
| Low | ${report.metrics.bySeverity.low} |
|
|
730
|
+
| Info | ${report.metrics.bySeverity.info} |
|
|
731
|
+
|
|
732
|
+
---
|
|
733
|
+
|
|
734
|
+
## Top Vulnerabilities
|
|
735
|
+
|
|
736
|
+
| Type | Severity | Count | Recommendation |
|
|
737
|
+
|------|----------|-------|----------------|
|
|
738
|
+
${report.topVulnerabilities.map(v => `| ${v.type} | ${v.severity} | ${v.count} | ${v.recommendation} |`).join('\n')}
|
|
739
|
+
|
|
740
|
+
---
|
|
741
|
+
|
|
742
|
+
## Component Risks
|
|
743
|
+
|
|
744
|
+
| Component | Vulnerabilities | Critical | High | Risk Score |
|
|
745
|
+
|-----------|-----------------|----------|------|------------|
|
|
746
|
+
${report.componentRisks.map(c => `| \`${c.component}\` | ${c.vulnerabilityCount} | ${c.criticalCount} | ${c.highCount} | ${c.riskScore} |`).join('\n')}
|
|
747
|
+
|
|
748
|
+
---
|
|
749
|
+
|
|
750
|
+
${report.recommendations ? `
|
|
751
|
+
## Recommendations
|
|
752
|
+
|
|
753
|
+
${report.recommendations.map(r => `
|
|
754
|
+
### [${r.priority.toUpperCase()}] ${r.title}
|
|
755
|
+
|
|
756
|
+
- **Category:** ${r.category}
|
|
757
|
+
- **Description:** ${r.description}
|
|
758
|
+
- **Impact:** ${r.impact}
|
|
759
|
+
- **Effort:** ${r.effort}
|
|
760
|
+
- **Affected:** ${r.affectedCount} items
|
|
761
|
+
`).join('\n')}
|
|
762
|
+
` : ''}
|
|
763
|
+
|
|
764
|
+
---
|
|
765
|
+
|
|
766
|
+
## Scan Summary
|
|
767
|
+
|
|
768
|
+
| Metric | Value |
|
|
769
|
+
|--------|-------|
|
|
770
|
+
| Total Scans | ${report.scanSummary.totalScans} |
|
|
771
|
+
| Files Scanned | ${report.scanSummary.filesScanned} |
|
|
772
|
+
| Last Scan | ${report.scanSummary.lastScanDate.toISOString()} |
|
|
773
|
+
| Coverage | ${report.scanSummary.coverage}% |
|
|
774
|
+
`;
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Get status color
|
|
778
|
+
*/
|
|
779
|
+
getStatusColor(status, colors) {
|
|
780
|
+
const mapping = {
|
|
781
|
+
critical: colors.critical ?? '#dc2626',
|
|
782
|
+
warning: colors.high ?? '#ea580c',
|
|
783
|
+
attention: colors.medium ?? '#ca8a04',
|
|
784
|
+
good: colors.low ?? '#16a34a',
|
|
785
|
+
excellent: colors.info ?? '#2563eb',
|
|
786
|
+
};
|
|
787
|
+
return mapping[status];
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Create security dashboard instance
|
|
792
|
+
*/
|
|
793
|
+
export function createSecurityDashboard(config) {
|
|
794
|
+
return new SecurityDashboard(config);
|
|
795
|
+
}
|
|
796
|
+
//# sourceMappingURL=security-dashboard.js.map
|