ship-safe 3.2.0 → 4.1.0

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.
@@ -0,0 +1,363 @@
1
+ /**
2
+ * HTML Report Generator
3
+ * ======================
4
+ *
5
+ * Generates a standalone HTML security report.
6
+ * No external dependencies — everything inline.
7
+ */
8
+
9
+ import fs from 'fs';
10
+ import path from 'path';
11
+
12
+ export class HTMLReporter {
13
+ /**
14
+ * Generate an HTML report from scan results.
15
+ *
16
+ * @param {object} scoreResult — From ScoringEngine.compute()
17
+ * @param {object[]} findings — Array of finding objects
18
+ * @param {object} recon — ReconAgent output
19
+ * @param {string} rootPath — Project root
20
+ * @returns {string} — HTML string
21
+ */
22
+ generate(scoreResult, findings, recon, rootPath) {
23
+ const projectName = path.basename(rootPath);
24
+ const date = new Date().toLocaleDateString('en-US', {
25
+ year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit',
26
+ });
27
+
28
+ const gradeColors = { A: '#22c55e', B: '#06b6d4', C: '#eab308', D: '#ef4444', F: '#dc2626' };
29
+ const sevColors = { critical: '#dc2626', high: '#f97316', medium: '#eab308', low: '#3b82f6' };
30
+
31
+ const bySeverity = { critical: 0, high: 0, medium: 0, low: 0 };
32
+ for (const f of findings) bySeverity[f.severity] = (bySeverity[f.severity] || 0) + 1;
33
+
34
+ const categoryRows = Object.entries(scoreResult.categories)
35
+ .map(([key, cat]) => {
36
+ const count = Object.values(cat.counts).reduce((a, b) => a + b, 0);
37
+ return `<tr>
38
+ <td>${cat.label}</td>
39
+ <td>${count}</td>
40
+ <td style="color:${cat.deduction > 0 ? '#ef4444' : '#22c55e'}">${cat.deduction > 0 ? '-' + cat.deduction : '0'}</td>
41
+ </tr>`;
42
+ }).join('\n');
43
+
44
+ const findingRows = findings.slice(0, 200).map(f => {
45
+ const relFile = path.relative(rootPath, f.file).replace(/\\/g, '/');
46
+ return `<tr>
47
+ <td><span class="sev sev-${f.severity}">${f.severity.toUpperCase()}</span></td>
48
+ <td><code>${relFile}:${f.line}</code></td>
49
+ <td><strong>${f.title || f.rule}</strong><br><small>${f.description?.slice(0, 120) || ''}</small></td>
50
+ <td><code>${(f.matched || '').slice(0, 60)}</code></td>
51
+ <td>${f.fix ? `<small>${f.fix.slice(0, 100)}</small>` : ''}</td>
52
+ </tr>`;
53
+ }).join('\n');
54
+
55
+ return `<!DOCTYPE html>
56
+ <html lang="en">
57
+ <head>
58
+ <meta charset="utf-8">
59
+ <meta name="viewport" content="width=device-width,initial-scale=1">
60
+ <title>Ship Safe Security Report — ${projectName}</title>
61
+ <style>
62
+ *{margin:0;padding:0;box-sizing:border-box}
63
+ body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#0f172a;color:#e2e8f0;padding:2rem}
64
+ .container{max-width:1200px;margin:0 auto}
65
+ h1{font-size:2rem;margin-bottom:0.5rem;color:#38bdf8}
66
+ h2{font-size:1.3rem;margin:2rem 0 1rem;color:#94a3b8;border-bottom:1px solid #1e293b;padding-bottom:0.5rem}
67
+ .meta{color:#64748b;margin-bottom:2rem}
68
+ .score-card{display:flex;align-items:center;gap:2rem;background:#1e293b;padding:2rem;border-radius:12px;margin-bottom:2rem}
69
+ .score-number{font-size:4rem;font-weight:bold}
70
+ .grade{font-size:3rem;font-weight:bold;width:80px;height:80px;display:flex;align-items:center;justify-content:center;border-radius:12px}
71
+ .stats{display:grid;grid-template-columns:repeat(4,1fr);gap:1rem;margin-bottom:2rem}
72
+ .stat{background:#1e293b;padding:1.5rem;border-radius:8px;text-align:center}
73
+ .stat-number{font-size:2rem;font-weight:bold}
74
+ .stat-label{color:#64748b;font-size:0.85rem}
75
+ table{width:100%;border-collapse:collapse;background:#1e293b;border-radius:8px;overflow:hidden;margin-bottom:2rem}
76
+ th{background:#334155;text-align:left;padding:0.75rem 1rem;font-size:0.8rem;text-transform:uppercase;color:#94a3b8}
77
+ td{padding:0.75rem 1rem;border-top:1px solid #1e293b;font-size:0.85rem;vertical-align:top}
78
+ tr:hover{background:#334155}
79
+ code{background:#0f172a;padding:2px 6px;border-radius:4px;font-size:0.8rem;color:#38bdf8}
80
+ small{color:#64748b}
81
+ .sev{padding:2px 8px;border-radius:4px;font-size:0.7rem;font-weight:bold;text-transform:uppercase}
82
+ .sev-critical{background:#dc262633;color:#fca5a5}
83
+ .sev-high{background:#f9731633;color:#fdba74}
84
+ .sev-medium{background:#eab30833;color:#fde047}
85
+ .sev-low{background:#3b82f633;color:#93c5fd}
86
+ .footer{text-align:center;color:#475569;margin-top:3rem;padding:2rem;border-top:1px solid #1e293b}
87
+ </style>
88
+ </head>
89
+ <body>
90
+ <div class="container">
91
+ <h1>Ship Safe Security Report</h1>
92
+ <p class="meta">${projectName} — ${date}</p>
93
+
94
+ <div class="score-card">
95
+ <div class="grade" style="background:${gradeColors[scoreResult.grade.letter]}22;color:${gradeColors[scoreResult.grade.letter]}">${scoreResult.grade.letter}</div>
96
+ <div>
97
+ <div class="score-number" style="color:${gradeColors[scoreResult.grade.letter]}">${scoreResult.score}/100</div>
98
+ <div style="color:#94a3b8">${scoreResult.grade.label}</div>
99
+ </div>
100
+ </div>
101
+
102
+ <div class="stats">
103
+ <div class="stat"><div class="stat-number" style="color:${sevColors.critical}">${bySeverity.critical}</div><div class="stat-label">Critical</div></div>
104
+ <div class="stat"><div class="stat-number" style="color:${sevColors.high}">${bySeverity.high}</div><div class="stat-label">High</div></div>
105
+ <div class="stat"><div class="stat-number" style="color:${sevColors.medium}">${bySeverity.medium}</div><div class="stat-label">Medium</div></div>
106
+ <div class="stat"><div class="stat-number" style="color:${sevColors.low}">${bySeverity.low}</div><div class="stat-label">Low</div></div>
107
+ </div>
108
+
109
+ <h2>Category Breakdown</h2>
110
+ <table>
111
+ <thead><tr><th>Category</th><th>Findings</th><th>Deduction</th></tr></thead>
112
+ <tbody>${categoryRows}</tbody>
113
+ </table>
114
+
115
+ <h2>Findings (${findings.length})</h2>
116
+ <table>
117
+ <thead><tr><th>Severity</th><th>Location</th><th>Issue</th><th>Code</th><th>Fix</th></tr></thead>
118
+ <tbody>${findingRows || '<tr><td colspan="5" style="text-align:center;color:#22c55e">No findings — clean!</td></tr>'}</tbody>
119
+ </table>
120
+
121
+ ${recon ? `<h2>Attack Surface</h2>
122
+ <table>
123
+ <tbody>
124
+ <tr><td>Frameworks</td><td>${(recon.frameworks || []).join(', ') || 'None detected'}</td></tr>
125
+ <tr><td>Languages</td><td>${(recon.languages || []).join(', ') || 'None detected'}</td></tr>
126
+ <tr><td>Databases</td><td>${(recon.databases || []).join(', ') || 'None detected'}</td></tr>
127
+ <tr><td>Cloud Providers</td><td>${(recon.cloudProviders || []).join(', ') || 'None detected'}</td></tr>
128
+ <tr><td>Auth Patterns</td><td>${(recon.authPatterns || []).join(', ') || 'None detected'}</td></tr>
129
+ <tr><td>CI/CD</td><td>${(recon.cicd || []).map(c => c.platform).join(', ') || 'None detected'}</td></tr>
130
+ <tr><td>API Routes</td><td>${(recon.apiRoutes || []).length} discovered</td></tr>
131
+ </tbody>
132
+ </table>` : ''}
133
+
134
+ <div class="footer">
135
+ Generated by <strong>Ship Safe v4.0</strong> — Security toolkit for developers<br>
136
+ <a href="https://github.com/asamassekou10/ship-safe" style="color:#38bdf8">github.com/asamassekou10/ship-safe</a>
137
+ </div>
138
+ </div>
139
+ </body>
140
+ </html>`;
141
+ }
142
+
143
+ /**
144
+ * Generate and write HTML report to file.
145
+ */
146
+ generateToFile(scoreResult, findings, recon, rootPath, outputPath) {
147
+ const html = this.generate(scoreResult, findings, recon, rootPath);
148
+ fs.writeFileSync(outputPath, html);
149
+ return outputPath;
150
+ }
151
+
152
+ /**
153
+ * Generate a full audit report including deps and remediation plan.
154
+ */
155
+ generateFullReport(scoreResult, findings, depVulns, recon, remediationPlan, rootPath, outputPath) {
156
+ const projectName = path.basename(rootPath);
157
+ const date = new Date().toLocaleDateString('en-US', {
158
+ year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit',
159
+ });
160
+
161
+ const gradeColors = { A: '#22c55e', B: '#06b6d4', C: '#eab308', D: '#ef4444', F: '#dc2626' };
162
+ const sevColors = { critical: '#dc2626', high: '#f97316', medium: '#eab308', low: '#3b82f6' };
163
+
164
+ const bySeverity = { critical: 0, high: 0, medium: 0, low: 0 };
165
+ for (const f of findings) bySeverity[f.severity] = (bySeverity[f.severity] || 0) + 1;
166
+
167
+ const categoryRows = Object.entries(scoreResult.categories)
168
+ .map(([key, cat]) => {
169
+ const count = Object.values(cat.counts).reduce((a, b) => a + b, 0);
170
+ return `<tr>
171
+ <td>${cat.label}</td>
172
+ <td>${count}</td>
173
+ <td style="color:${cat.deduction > 0 ? '#ef4444' : '#22c55e'}">${cat.deduction > 0 ? '-' + cat.deduction : '0'}</td>
174
+ </tr>`;
175
+ }).join('\n');
176
+
177
+ const findingRows = findings.slice(0, 200).map(f => {
178
+ const relFile = path.relative(rootPath, f.file).replace(/\\/g, '/');
179
+ return `<tr>
180
+ <td><span class="sev sev-${f.severity}">${f.severity.toUpperCase()}</span></td>
181
+ <td><code>${relFile}:${f.line}</code></td>
182
+ <td><strong>${this.esc(f.title || f.rule)}</strong><br><small>${this.esc((f.description || '').slice(0, 120))}</small></td>
183
+ <td><code>${this.esc((f.matched || '').slice(0, 60))}</code></td>
184
+ <td>${f.fix ? `<small>${this.esc(f.fix.slice(0, 100))}</small>` : ''}</td>
185
+ </tr>`;
186
+ }).join('\n');
187
+
188
+ // Dep vuln rows
189
+ const depRows = (depVulns || []).slice(0, 100).map(d => {
190
+ const sev = d.severity === 'moderate' ? 'medium' : d.severity;
191
+ return `<tr>
192
+ <td><span class="sev sev-${sev}">${(d.severity || 'unknown').toUpperCase()}</span></td>
193
+ <td><code>${this.esc(d.package || d.id || 'unknown')}</code></td>
194
+ <td>${this.esc((d.description || '').slice(0, 150))}</td>
195
+ </tr>`;
196
+ }).join('\n');
197
+
198
+ // Remediation plan rows
199
+ const sevIcons = { critical: '&#x1F534;', high: '&#x1F7E0;', medium: '&#x1F7E1;', low: '&#x1F535;' };
200
+ let currentSev = null;
201
+ let planHTML = '';
202
+ for (const item of (remediationPlan || []).slice(0, 100)) {
203
+ if (item.severity !== currentSev) {
204
+ currentSev = item.severity;
205
+ const label = { critical: 'CRITICAL — fix immediately', high: 'HIGH — fix before deploy', medium: 'MEDIUM — fix soon', low: 'LOW — review when possible' };
206
+ planHTML += `<tr class="sev-header"><td colspan="5" style="background:#1e293b;padding:1rem;font-weight:bold;color:${sevColors[currentSev] || '#94a3b8'}">${sevIcons[currentSev] || ''} ${label[currentSev] || currentSev.toUpperCase()}</td></tr>\n`;
207
+ }
208
+ planHTML += `<tr>
209
+ <td>${item.priority}</td>
210
+ <td><span class="sev sev-${item.severity}">${item.categoryLabel}</span></td>
211
+ <td><strong>${this.esc(item.title)}</strong></td>
212
+ <td><code>${this.esc(item.file)}</code></td>
213
+ <td><small>${this.esc((item.action || '').slice(0, 120))}</small></td>
214
+ </tr>\n`;
215
+ }
216
+
217
+ const html = `<!DOCTYPE html>
218
+ <html lang="en">
219
+ <head>
220
+ <meta charset="utf-8">
221
+ <meta name="viewport" content="width=device-width,initial-scale=1">
222
+ <title>Ship Safe Full Audit Report — ${this.esc(projectName)}</title>
223
+ <style>
224
+ *{margin:0;padding:0;box-sizing:border-box}
225
+ body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#0f172a;color:#e2e8f0;padding:2rem}
226
+ .container{max-width:1200px;margin:0 auto}
227
+ h1{font-size:2rem;margin-bottom:0.5rem;color:#38bdf8}
228
+ h2{font-size:1.3rem;margin:2rem 0 1rem;color:#94a3b8;border-bottom:1px solid #1e293b;padding-bottom:0.5rem}
229
+ .meta{color:#64748b;margin-bottom:2rem}
230
+ .score-card{display:flex;align-items:center;gap:2rem;background:#1e293b;padding:2rem;border-radius:12px;margin-bottom:2rem}
231
+ .score-number{font-size:4rem;font-weight:bold}
232
+ .grade{font-size:3rem;font-weight:bold;width:80px;height:80px;display:flex;align-items:center;justify-content:center;border-radius:12px}
233
+ .stats{display:grid;grid-template-columns:repeat(4,1fr);gap:1rem;margin-bottom:2rem}
234
+ .stat{background:#1e293b;padding:1.5rem;border-radius:8px;text-align:center}
235
+ .stat-number{font-size:2rem;font-weight:bold}
236
+ .stat-label{color:#64748b;font-size:0.85rem}
237
+ .summary-grid{display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-bottom:2rem}
238
+ .summary-card{background:#1e293b;padding:1.5rem;border-radius:8px}
239
+ .summary-card h3{color:#38bdf8;font-size:1rem;margin-bottom:0.5rem}
240
+ .summary-card .big{font-size:2.5rem;font-weight:bold}
241
+ table{width:100%;border-collapse:collapse;background:#1e293b;border-radius:8px;overflow:hidden;margin-bottom:2rem}
242
+ th{background:#334155;text-align:left;padding:0.75rem 1rem;font-size:0.8rem;text-transform:uppercase;color:#94a3b8}
243
+ td{padding:0.75rem 1rem;border-top:1px solid #0f172a;font-size:0.85rem;vertical-align:top}
244
+ tr:hover{background:#334155}
245
+ code{background:#0f172a;padding:2px 6px;border-radius:4px;font-size:0.8rem;color:#38bdf8}
246
+ small{color:#94a3b8}
247
+ .sev{padding:2px 8px;border-radius:4px;font-size:0.7rem;font-weight:bold;text-transform:uppercase}
248
+ .sev-critical{background:#dc262633;color:#fca5a5}
249
+ .sev-high{background:#f9731633;color:#fdba74}
250
+ .sev-medium,.sev-moderate{background:#eab30833;color:#fde047}
251
+ .sev-low{background:#3b82f633;color:#93c5fd}
252
+ .toc{background:#1e293b;padding:1.5rem 2rem;border-radius:8px;margin-bottom:2rem}
253
+ .toc a{color:#38bdf8;text-decoration:none;display:block;padding:0.3rem 0}
254
+ .toc a:hover{text-decoration:underline}
255
+ .footer{text-align:center;color:#475569;margin-top:3rem;padding:2rem;border-top:1px solid #1e293b}
256
+ .footer a{color:#38bdf8}
257
+ @media print{body{background:#fff;color:#1e293b}table,th,td{border:1px solid #e2e8f0}.score-card,.stat,.summary-card,.toc{background:#f8fafc}}
258
+ </style>
259
+ </head>
260
+ <body>
261
+ <div class="container">
262
+ <h1>Ship Safe — Full Security Audit Report</h1>
263
+ <p class="meta">${this.esc(projectName)} — ${date}</p>
264
+
265
+ <div class="toc">
266
+ <strong>Contents</strong>
267
+ <a href="#score">1. Security Score</a>
268
+ <a href="#summary">2. Executive Summary</a>
269
+ <a href="#categories">3. Category Breakdown</a>
270
+ <a href="#plan">4. Remediation Plan (${(remediationPlan || []).length} items)</a>
271
+ <a href="#findings">5. All Findings (${findings.length})</a>
272
+ <a href="#deps">6. Dependency Vulnerabilities (${(depVulns || []).length})</a>
273
+ <a href="#surface">7. Attack Surface</a>
274
+ </div>
275
+
276
+ <h2 id="score">1. Security Score</h2>
277
+ <div class="score-card">
278
+ <div class="grade" style="background:${gradeColors[scoreResult.grade.letter]}22;color:${gradeColors[scoreResult.grade.letter]}">${scoreResult.grade.letter}</div>
279
+ <div>
280
+ <div class="score-number" style="color:${gradeColors[scoreResult.grade.letter]}">${scoreResult.score}/100</div>
281
+ <div style="color:#94a3b8">${scoreResult.grade.label}</div>
282
+ </div>
283
+ </div>
284
+
285
+ <div class="stats">
286
+ <div class="stat"><div class="stat-number" style="color:${sevColors.critical}">${bySeverity.critical}</div><div class="stat-label">Critical</div></div>
287
+ <div class="stat"><div class="stat-number" style="color:${sevColors.high}">${bySeverity.high}</div><div class="stat-label">High</div></div>
288
+ <div class="stat"><div class="stat-number" style="color:${sevColors.medium}">${bySeverity.medium}</div><div class="stat-label">Medium</div></div>
289
+ <div class="stat"><div class="stat-number" style="color:${sevColors.low}">${bySeverity.low}</div><div class="stat-label">Low</div></div>
290
+ </div>
291
+
292
+ <h2 id="summary">2. Executive Summary</h2>
293
+ <div class="summary-grid">
294
+ <div class="summary-card">
295
+ <h3>Code Findings</h3>
296
+ <div class="big" style="color:${findings.length > 0 ? '#ef4444' : '#22c55e'}">${findings.length}</div>
297
+ <small>Across ${Object.keys(scoreResult.categories).length} categories</small>
298
+ </div>
299
+ <div class="summary-card">
300
+ <h3>Dependency CVEs</h3>
301
+ <div class="big" style="color:${(depVulns || []).length > 0 ? '#ef4444' : '#22c55e'}">${(depVulns || []).length}</div>
302
+ <small>From npm/pip/bundler audit</small>
303
+ </div>
304
+ </div>
305
+
306
+ <h2 id="categories">3. Category Breakdown</h2>
307
+ <table>
308
+ <thead><tr><th>Category</th><th>Findings</th><th>Score Deduction</th></tr></thead>
309
+ <tbody>${categoryRows}</tbody>
310
+ </table>
311
+
312
+ <h2 id="plan">4. Remediation Plan</h2>
313
+ <p style="color:#94a3b8;margin-bottom:1rem">Prioritized list of fixes. Address critical items first.</p>
314
+ ${(remediationPlan || []).length > 0 ? `<table>
315
+ <thead><tr><th>#</th><th>Category</th><th>Issue</th><th>Location</th><th>Fix</th></tr></thead>
316
+ <tbody>${planHTML}</tbody>
317
+ </table>` : '<p style="color:#22c55e;font-weight:bold">No issues found — all clear!</p>'}
318
+
319
+ <h2 id="findings">5. All Findings (${findings.length})</h2>
320
+ <table>
321
+ <thead><tr><th>Severity</th><th>Location</th><th>Issue</th><th>Code</th><th>Fix</th></tr></thead>
322
+ <tbody>${findingRows || '<tr><td colspan="5" style="text-align:center;color:#22c55e">No findings — clean!</td></tr>'}</tbody>
323
+ </table>
324
+
325
+ <h2 id="deps">6. Dependency Vulnerabilities (${(depVulns || []).length})</h2>
326
+ ${(depVulns || []).length > 0 ? `<table>
327
+ <thead><tr><th>Severity</th><th>Package</th><th>Description</th></tr></thead>
328
+ <tbody>${depRows}</tbody>
329
+ </table>` : '<p style="color:#22c55e;font-weight:bold">No vulnerable dependencies found.</p>'}
330
+
331
+ ${recon ? `<h2 id="surface">7. Attack Surface</h2>
332
+ <table>
333
+ <tbody>
334
+ <tr><td>Frameworks</td><td>${(recon.frameworks || []).join(', ') || 'None detected'}</td></tr>
335
+ <tr><td>Languages</td><td>${(recon.languages || []).join(', ') || 'None detected'}</td></tr>
336
+ <tr><td>Databases</td><td>${(recon.databases || []).join(', ') || 'None detected'}</td></tr>
337
+ <tr><td>Cloud Providers</td><td>${(recon.cloudProviders || []).join(', ') || 'None detected'}</td></tr>
338
+ <tr><td>Auth Patterns</td><td>${(recon.authPatterns || []).join(', ') || 'None detected'}</td></tr>
339
+ <tr><td>CI/CD</td><td>${(recon.cicd || []).map(c => c.platform).join(', ') || 'None detected'}</td></tr>
340
+ <tr><td>API Routes</td><td>${(recon.apiRoutes || []).length} discovered</td></tr>
341
+ </tbody>
342
+ </table>` : ''}
343
+
344
+ <div class="footer">
345
+ Generated by <strong>Ship Safe v4.0</strong> — Full Security Audit<br>
346
+ <a href="https://github.com/asamassekou10/ship-safe">github.com/asamassekou10/ship-safe</a>
347
+ </div>
348
+ </div>
349
+ </body>
350
+ </html>`;
351
+
352
+ fs.writeFileSync(outputPath, html);
353
+ return outputPath;
354
+ }
355
+
356
+ /** Escape HTML entities */
357
+ esc(str) {
358
+ if (!str) return '';
359
+ return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
360
+ }
361
+ }
362
+
363
+ export default HTMLReporter;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Agent Registry
3
+ * ===============
4
+ *
5
+ * Central export of all agents and supporting classes.
6
+ */
7
+
8
+ export { BaseAgent, createFinding } from './base-agent.js';
9
+ export { Orchestrator } from './orchestrator.js';
10
+ export { ReconAgent } from './recon-agent.js';
11
+ export { InjectionTester } from './injection-tester.js';
12
+ export { AuthBypassAgent } from './auth-bypass-agent.js';
13
+ export { SSRFProber } from './ssrf-prober.js';
14
+ export { SupplyChainAudit } from './supply-chain-agent.js';
15
+ export { ConfigAuditor } from './config-auditor.js';
16
+ export { LLMRedTeam } from './llm-redteam.js';
17
+ export { MobileScanner } from './mobile-scanner.js';
18
+ export { GitHistoryScanner } from './git-history-scanner.js';
19
+ export { CICDScanner } from './cicd-scanner.js';
20
+ export { APIFuzzer } from './api-fuzzer.js';
21
+ export { ScoringEngine, GRADES, CATEGORIES } from './scoring-engine.js';
22
+ export { SBOMGenerator } from './sbom-generator.js';
23
+ export { PolicyEngine } from './policy-engine.js';
24
+ export { HTMLReporter } from './html-reporter.js';
25
+
26
+ /**
27
+ * Create a fully configured orchestrator with all 12 agents.
28
+ */
29
+ import { Orchestrator as OrchestratorClass } from './orchestrator.js';
30
+ import { InjectionTester as InjectionTesterClass } from './injection-tester.js';
31
+ import { AuthBypassAgent as AuthBypassAgentClass } from './auth-bypass-agent.js';
32
+ import { SSRFProber as SSRFProberClass } from './ssrf-prober.js';
33
+ import { SupplyChainAudit as SupplyChainAuditClass } from './supply-chain-agent.js';
34
+ import { ConfigAuditor as ConfigAuditorClass } from './config-auditor.js';
35
+ import { LLMRedTeam as LLMRedTeamClass } from './llm-redteam.js';
36
+ import { MobileScanner as MobileScannerClass } from './mobile-scanner.js';
37
+ import { GitHistoryScanner as GitHistoryScannerClass } from './git-history-scanner.js';
38
+ import { CICDScanner as CICDScannerClass } from './cicd-scanner.js';
39
+ import { APIFuzzer as APIFuzzerClass } from './api-fuzzer.js';
40
+
41
+ export function buildOrchestrator() {
42
+ const orchestrator = new OrchestratorClass();
43
+ orchestrator.registerAll([
44
+ new InjectionTesterClass(),
45
+ new AuthBypassAgentClass(),
46
+ new SSRFProberClass(),
47
+ new SupplyChainAuditClass(),
48
+ new ConfigAuditorClass(),
49
+ new LLMRedTeamClass(),
50
+ new MobileScannerClass(),
51
+ new GitHistoryScannerClass(),
52
+ new CICDScannerClass(),
53
+ new APIFuzzerClass(),
54
+ ]);
55
+ return orchestrator;
56
+ }