cyberaudit-skill 3.1.5 → 3.2.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.
Files changed (91) hide show
  1. package/README.md +84 -25
  2. package/dist/cli.d.ts +10 -0
  3. package/dist/cli.js +552 -172
  4. package/dist/cli.test.d.ts +1 -0
  5. package/dist/cli.test.js +100 -0
  6. package/dist/mcp-server.d.ts +135 -1
  7. package/dist/mcp-server.js +185 -96
  8. package/dist/mcp-server.test.d.ts +1 -0
  9. package/dist/mcp-server.test.js +77 -0
  10. package/dist/report/generator.d.ts +2 -0
  11. package/dist/report/generator.js +260 -0
  12. package/dist/report/generator.test.d.ts +1 -0
  13. package/dist/report/generator.test.js +73 -0
  14. package/dist/report/types.d.ts +31 -0
  15. package/dist/report/types.js +1 -0
  16. package/dist/scanners/secrets.d.ts +32 -0
  17. package/dist/scanners/secrets.js +437 -0
  18. package/dist/scanners/secrets.test.d.ts +1 -0
  19. package/dist/scanners/secrets.test.js +96 -0
  20. package/dist/scanners/types.d.ts +22 -0
  21. package/dist/scanners/types.js +1 -0
  22. package/dist/scanners/web.d.ts +37 -0
  23. package/dist/scanners/web.js +327 -0
  24. package/dist/scanners/web.test.d.ts +1 -0
  25. package/dist/scanners/web.test.js +67 -0
  26. package/package.json +15 -3
  27. package/skills/cyberaudit/AGENT-BOOT.md +1 -1
  28. package/skills/cyberaudit/COMMANDS.md +11 -1
  29. package/skills/cyberaudit/INSTALL.md +7 -6
  30. package/skills/cyberaudit/README.md +20 -5
  31. package/skills/cyberaudit/SKILL.md +20 -5
  32. package/skills/cyberaudit/cloud/CLOUD-CHECKLIST.md +85 -0
  33. package/skills/cyberaudit/cloud/CLOUD-PHILOSOPHY.md +91 -0
  34. package/skills/cyberaudit/cloud/CLOUD-REMEDIATION-LIBRARY.md +145 -0
  35. package/skills/cyberaudit/commands/audit-angular.md +11 -0
  36. package/skills/cyberaudit/commands/audit-api-bola.md +11 -0
  37. package/skills/cyberaudit/commands/audit-api-bopla.md +11 -0
  38. package/skills/cyberaudit/commands/audit-api-graphql.md +11 -0
  39. package/skills/cyberaudit/commands/audit-api-inventory.md +11 -0
  40. package/skills/cyberaudit/commands/audit-api-rate-limit.md +11 -0
  41. package/skills/cyberaudit/commands/audit-api-rest.md +11 -0
  42. package/skills/cyberaudit/commands/audit-api-third-party.md +11 -0
  43. package/skills/cyberaudit/commands/audit-api-ws.md +11 -0
  44. package/skills/cyberaudit/commands/audit-auth-api.md +11 -0
  45. package/skills/cyberaudit/commands/audit-auth-mobile.md +11 -0
  46. package/skills/cyberaudit/commands/audit-auth.md +12 -0
  47. package/skills/cyberaudit/commands/audit-binary.md +11 -0
  48. package/skills/cyberaudit/commands/audit-cis.md +11 -0
  49. package/skills/cyberaudit/commands/audit-cloud-iam.md +11 -0
  50. package/skills/cyberaudit/commands/audit-cloud-network.md +11 -0
  51. package/skills/cyberaudit/commands/audit-cloud-s3.md +11 -0
  52. package/skills/cyberaudit/commands/audit-cloud.md +10 -0
  53. package/skills/cyberaudit/commands/audit-cors.md +11 -0
  54. package/skills/cyberaudit/commands/audit-crypto-mobile.md +11 -0
  55. package/skills/cyberaudit/commands/audit-crypto.md +10 -0
  56. package/skills/cyberaudit/commands/audit-csrf.md +11 -0
  57. package/skills/cyberaudit/commands/audit-deeplinks.md +11 -0
  58. package/skills/cyberaudit/commands/audit-deps.md +11 -0
  59. package/skills/cyberaudit/commands/audit-deserial.md +11 -0
  60. package/skills/cyberaudit/commands/audit-exec.md +11 -0
  61. package/skills/cyberaudit/commands/audit-expo.md +11 -0
  62. package/skills/cyberaudit/commands/audit-express.md +11 -0
  63. package/skills/cyberaudit/commands/audit-flutter.md +11 -0
  64. package/skills/cyberaudit/commands/audit-headers.md +11 -0
  65. package/skills/cyberaudit/commands/audit-hipaa.md +11 -0
  66. package/skills/cyberaudit/commands/audit-idor.md +11 -0
  67. package/skills/cyberaudit/commands/audit-injection.md +12 -0
  68. package/skills/cyberaudit/commands/audit-ionic.md +11 -0
  69. package/skills/cyberaudit/commands/audit-laravel.md +11 -0
  70. package/skills/cyberaudit/commands/audit-logic.md +11 -0
  71. package/skills/cyberaudit/commands/audit-masvs.md +11 -0
  72. package/skills/cyberaudit/commands/audit-nestjs.md +11 -0
  73. package/skills/cyberaudit/commands/audit-network.md +11 -0
  74. package/skills/cyberaudit/commands/audit-nextjs.md +11 -0
  75. package/skills/cyberaudit/commands/audit-owasp.md +11 -0
  76. package/skills/cyberaudit/commands/audit-pci.md +11 -0
  77. package/skills/cyberaudit/commands/audit-permissions.md +11 -0
  78. package/skills/cyberaudit/commands/audit-react-native.md +11 -0
  79. package/skills/cyberaudit/commands/audit-react.md +11 -0
  80. package/skills/cyberaudit/commands/audit-rgpd.md +11 -0
  81. package/skills/cyberaudit/commands/audit-runtime.md +11 -0
  82. package/skills/cyberaudit/commands/audit-secrets.md +11 -0
  83. package/skills/cyberaudit/commands/audit-ssrf.md +11 -0
  84. package/skills/cyberaudit/commands/audit-storage.md +11 -0
  85. package/skills/cyberaudit/commands/audit-vue.md +11 -0
  86. package/skills/cyberaudit/commands/audit-xss.md +11 -0
  87. package/skills/cyberaudit/commands/audit-xxe.md +11 -0
  88. package/skills/cyberaudit/reports/EXECUTIVE-SUMMARY-TEMPLATE.md +1 -1
  89. package/skills/cyberaudit/reports/REPORT-TEMPLATE-CLOUD.md +103 -0
  90. package/skills/cyberaudit/reports/REPORT-TEMPLATE-MOBILE.md +2 -2
  91. package/skills/cyberaudit/reports/REPORT-TEMPLATE-WEB.md +2 -2
@@ -0,0 +1,260 @@
1
+ function calculateScore(findings) {
2
+ let score = 100;
3
+ for (const f of findings) {
4
+ switch (f.severity) {
5
+ case 'CRITICAL':
6
+ score -= 20;
7
+ break;
8
+ case 'HIGH':
9
+ score -= 10;
10
+ break;
11
+ case 'MEDIUM':
12
+ score -= 5;
13
+ break;
14
+ case 'LOW':
15
+ score -= 1;
16
+ break;
17
+ case 'INFO':
18
+ score -= 0;
19
+ break;
20
+ }
21
+ }
22
+ // Extra penalty for many findings
23
+ if (findings.length > 10)
24
+ score -= Math.min(20, findings.length - 10);
25
+ return Math.max(0, Math.min(100, score));
26
+ }
27
+ function getVerdict(score, hasCritical) {
28
+ if (hasCritical || score <= 20)
29
+ return 'CRITICAL';
30
+ if (score <= 40)
31
+ return 'HIGH';
32
+ if (score <= 60)
33
+ return 'MODERATE';
34
+ if (score <= 80)
35
+ return 'GOOD';
36
+ return 'EXCELLENT';
37
+ }
38
+ function verdictIcon(v) {
39
+ switch (v) {
40
+ case 'CRITICAL': return '🔴 CRITICAL';
41
+ case 'HIGH': return '🟠 HIGH';
42
+ case 'MODERATE': return '🟡 MODERATE';
43
+ case 'GOOD': return '🟢 GOOD';
44
+ case 'EXCELLENT': return '✅ EXCELLENT';
45
+ default: return v;
46
+ }
47
+ }
48
+ function groupBySeverity(findings) {
49
+ const map = { CRITICAL: 0, HIGH: 0, MEDIUM: 0, LOW: 0, INFO: 0 };
50
+ for (const f of findings) {
51
+ map[f.severity] = (map[f.severity] || 0) + 1;
52
+ }
53
+ return map;
54
+ }
55
+ function owaspComplianceFromFindings(findings) {
56
+ // Simplified mapping based on patternId -> OWASP
57
+ const owaspMap = {
58
+ 'A01 — Broken Access Control': ['CORS', 'IDOR', 'BOLA', 'MASS_ASSIGNMENT'],
59
+ 'A02 — Cryptographic Failures': ['JWT', 'CRYPTO', 'PRIVATE_KEY'],
60
+ 'A03 — Injection': ['SQLI', 'XSS', 'INJECTION', 'EVAL', 'EXEC', 'NOSQL', 'XXE', 'DESERIAL'],
61
+ 'A04 — Insecure Design': ['LOGIC', 'BUSINESS'],
62
+ 'A05 — Security Misconfiguration': ['HELMET', 'HEADERS', 'DEBUG', 'CORS'],
63
+ 'A06 — Vulnerable Components': ['DEPS', 'SUPPLY'],
64
+ 'A07 — Auth Failures': ['AUTH', 'SECRET', 'AWS', 'GITHUB', 'STRIPE', 'JWT'],
65
+ 'A08 — Data Integrity': ['DESERIAL', 'SUPPLY'],
66
+ 'A09 — Logging & Monitoring': [],
67
+ 'A10 — SSRF': ['SSRF'],
68
+ };
69
+ const result = {};
70
+ for (const [owasp, keywords] of Object.entries(owaspMap)) {
71
+ const hasFindings = findings.some(f => {
72
+ const pid = f.patternId.toUpperCase();
73
+ return keywords.some(k => pid.includes(k.toUpperCase()));
74
+ });
75
+ if (hasFindings)
76
+ result[owasp] = '❌ FAIL';
77
+ else
78
+ result[owasp] = '✅ PASS';
79
+ }
80
+ // If no findings at all, all PASS
81
+ return result;
82
+ }
83
+ function bar(score) {
84
+ const filled = Math.round(score / 5); // 20 chars max
85
+ const empty = 20 - filled;
86
+ return `[${'█'.repeat(filled)}${'░'.repeat(empty)}] ${score}%`;
87
+ }
88
+ export function generateReport(input) {
89
+ const { target, findings, type, version, framework } = input;
90
+ const date = input.date || new Date().toISOString().split('T')[0];
91
+ const score = calculateScore(findings);
92
+ const hasCritical = findings.some(f => f.severity === 'CRITICAL');
93
+ const verdict = getVerdict(score, hasCritical);
94
+ const dashboard = groupBySeverity(findings);
95
+ const owasp = owaspComplianceFromFindings(findings);
96
+ // Sort findings by severity then file
97
+ const order = { CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3, INFO: 4 };
98
+ const sorted = [...findings].sort((a, b) => {
99
+ const oa = order[a.severity];
100
+ const ob = order[b.severity];
101
+ if (oa !== ob)
102
+ return oa - ob;
103
+ return a.file.localeCompare(b.file);
104
+ });
105
+ const groupedBySeverity = {};
106
+ for (const f of sorted) {
107
+ if (!groupedBySeverity[f.severity])
108
+ groupedBySeverity[f.severity] = [];
109
+ groupedBySeverity[f.severity].push(f);
110
+ }
111
+ let md = `# 🛡️ SECURITY AUDIT REPORT — ${type.toUpperCase()}\n`;
112
+ md += `# Generated by CyberAudit v${version} — Deterministic Scanner\n\n`;
113
+ md += `═══════════════════════════════════════════════════════════════\n`;
114
+ md += ` REPORT HEADER\n`;
115
+ md += `═══════════════════════════════════════════════════════════════\n\n`;
116
+ md += `Application : ${target}\n`;
117
+ md += `Framework(s) : ${framework || 'auto-detected'}\n`;
118
+ md += `Audit type : ${type} — deterministic (secrets + web)\n`;
119
+ md += `Audit level : Standard (deterministic)\n`;
120
+ md += `Date : ${date}\n`;
121
+ md += `Auditor : CyberAudit Intelligence v${version} (deterministic scanner)\n`;
122
+ md += `Confidentiality : 🔴 CONFIDENTIAL — Internal use only\n\n`;
123
+ md += `═══════════════════════════════════════════════════════════════\n`;
124
+ md += ` EXECUTIVE SUMMARY\n`;
125
+ md += `═══════════════════════════════════════════════════════════════\n\n`;
126
+ md += `OVERALL VERDICT: ${verdictIcon(verdict)}\n\n`;
127
+ md += `SECURITY SCORE: ${score}/100\n\n`;
128
+ md += ` ${bar(score)}\n\n`;
129
+ md += ` 0-20 : 🔴 Critical — Do not deploy\n`;
130
+ md += ` 21-40 : 🟠 High — Major fixes required\n`;
131
+ md += ` 41-60 : 🟡 Moderate — Significant fixes before prod\n`;
132
+ md += ` 61-80 : 🟢 Good — Some fixes recommended\n`;
133
+ md += ` 81-100: ✅ Excellent — Ready for production\n\n`;
134
+ md += `VULNERABILITY DASHBOARD\n`;
135
+ md += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n`;
136
+ md += ` 🔴 CRITICAL : ${dashboard.CRITICAL || 0} → Block deployment\n`;
137
+ md += ` 🟠 HIGH : ${dashboard.HIGH || 0} → Fix before deployment\n`;
138
+ md += ` 🟡 MEDIUM : ${dashboard.MEDIUM || 0} → Fix within 30 days\n`;
139
+ md += ` 🟢 LOW : ${dashboard.LOW || 0} → Security backlog\n`;
140
+ md += ` ℹ️ INFO : ${dashboard.INFO || 0} → Recommendations\n\n`;
141
+ md += ` TOTAL : ${findings.length} findings (deterministic)\n\n`;
142
+ if (findings.length > 0) {
143
+ md += `IDENTIFIED BUSINESS RISKS\n`;
144
+ md += `━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
145
+ if (dashboard.CRITICAL > 0)
146
+ md += ` → Immediate compromise possible via ${sorted.filter(f => f.severity === 'CRITICAL').slice(0, 2).map(f => f.patternId).join(', ')}\n`;
147
+ if (findings.some(f => f.patternId.includes('SECRET') || f.patternId.includes('AWS')))
148
+ md += ` → Credential leak → account takeover, data breach\n`;
149
+ if (findings.some(f => f.patternId.includes('SQLI') || f.patternId.includes('XSS')))
150
+ md += ` → Injection → data exfiltration, user session hijack\n`;
151
+ if (findings.some(f => f.patternId.includes('JWT')))
152
+ md += ` → Auth bypass via JWT flaw\n`;
153
+ md += `\n`;
154
+ md += `IMMEDIATE ACTIONS REQUIRED\n`;
155
+ md += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
156
+ sorted.filter(f => f.severity === 'CRITICAL').slice(0, 3).forEach((f, i) => {
157
+ md += ` ${i + 1}. Fix ${f.patternId} in ${f.file}:${f.line} — ${f.severity}\n`;
158
+ });
159
+ if (dashboard.CRITICAL === 0 && dashboard.HIGH > 0) {
160
+ sorted.filter(f => f.severity === 'HIGH').slice(0, 3).forEach((f, i) => {
161
+ md += ` ${i + 1}. Fix ${f.patternId} in ${f.file}:${f.line}\n`;
162
+ });
163
+ }
164
+ md += `\n`;
165
+ }
166
+ else {
167
+ md += `✅ No deterministic findings. Good baseline, proceed to manual audit for business logic.\n\n`;
168
+ }
169
+ md += `═══════════════════════════════════════════════════════════════\n`;
170
+ md += ` DETAILED FINDINGS\n`;
171
+ md += `═══════════════════════════════════════════════════════════════\n\n`;
172
+ if (sorted.length === 0) {
173
+ md += `No findings from deterministic scanners.\n\n`;
174
+ }
175
+ else {
176
+ let counter = 1;
177
+ for (const sev of ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO']) {
178
+ const group = groupedBySeverity[sev];
179
+ if (!group || group.length === 0)
180
+ continue;
181
+ md += `━━ ${sev} (${group.length}) ━━\n\n`;
182
+ for (const f of group) {
183
+ md += `[VULN-${String(counter).padStart(3, '0')}] — ${f.patternId}\n`;
184
+ md += `─────────────────────────────────────────────\n`;
185
+ md += ` Severity : ${sev === 'CRITICAL' ? '🔴' : sev === 'HIGH' ? '🟠' : sev === 'MEDIUM' ? '🟡' : '🟢'} ${sev}\n`;
186
+ md += ` CVSS Score : ${f.cvss} (${f.cvssVector})\n`;
187
+ md += ` Scanner : ${f.scanner}\n`;
188
+ md += ` OWASP : ${f.owasp || 'N/A'}\n`;
189
+ md += ` CWE : ${f.cwe || 'N/A'}\n`;
190
+ md += ` File : ${f.file}\n`;
191
+ md += ` Line : ${f.line}\n`;
192
+ md += `\n`;
193
+ md += ` ┌─ MATCH ─────────────────────────────────────────────\n`;
194
+ md += ` │ ${f.match.slice(0, 200)}\n`;
195
+ md += ` └─────────────────────────────────────────────────────\n\n`;
196
+ md += ` DESCRIPTION\n`;
197
+ md += ` ───────────\n`;
198
+ md += ` ${f.description}\n\n`;
199
+ md += ` REMEDIATION (ready to copy-paste)\n`;
200
+ md += ` ───────────\n`;
201
+ md += ` ${f.remediation}\n\n`;
202
+ md += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n`;
203
+ counter++;
204
+ }
205
+ }
206
+ }
207
+ md += `═══════════════════════════════════════════════════════════════\n`;
208
+ md += ` OWASP TOP 10 2023 COMPLIANCE\n`;
209
+ md += `═══════════════════════════════════════════════════════════════\n\n`;
210
+ for (const [k, v] of Object.entries(owasp)) {
211
+ md += ` ${k.padEnd(35)} : ${v}\n`;
212
+ }
213
+ md += `\n`;
214
+ md += `═══════════════════════════════════════════════════════════════\n`;
215
+ md += ` PRIORITISED REMEDIATION PLAN\n`;
216
+ md += `═══════════════════════════════════════════════════════════════\n\n`;
217
+ const phases = {
218
+ 'PHASE 1 — IMMEDIATE (Week 1)': groupedBySeverity['CRITICAL'] || [],
219
+ 'PHASE 2 — SHORT TERM (Weeks 2-4)': groupedBySeverity['HIGH'] || [],
220
+ 'PHASE 3 — MEDIUM TERM (Months 2-3)': groupedBySeverity['MEDIUM'] || [],
221
+ 'PHASE 4 — LONG TERM (Backlog)': [...(groupedBySeverity['LOW'] || []), ...(groupedBySeverity['INFO'] || [])],
222
+ };
223
+ for (const [phase, items] of Object.entries(phases)) {
224
+ if (items.length === 0)
225
+ continue;
226
+ md += `${phase}\n`;
227
+ md += ` Objective: ${phase.includes('IMMEDIATE') ? 'Eliminate critical risks' : phase.includes('SHORT') ? 'Eliminate high risks' : phase.includes('MEDIUM') ? 'Address medium risks' : 'Polish'}\n`;
228
+ for (const f of items.slice(0, 10)) {
229
+ md += ` □ ${f.patternId} : ${f.file}:${f.line} (${f.severity})\n`;
230
+ }
231
+ if (items.length > 10)
232
+ md += ` ... and ${items.length - 10} more\n`;
233
+ md += `\n`;
234
+ }
235
+ md += `═══════════════════════════════════════════════════════════════\n`;
236
+ md += ` CONCLUSION\n`;
237
+ md += `═══════════════════════════════════════════════════════════════\n\n`;
238
+ if (verdict === 'CRITICAL')
239
+ md += `🔴 NO-GO for production. Critical findings must be fixed. Score ${score}/100.\n`;
240
+ else if (verdict === 'HIGH')
241
+ md += `🟠 NO-GO for production without fixing HIGH findings. Score ${score}/100.\n`;
242
+ else if (verdict === 'MODERATE')
243
+ md += `🟡 Conditional GO with remediation plan. Score ${score}/100.\n`;
244
+ else if (verdict === 'GOOD')
245
+ md += `🟢 GO with minor fixes in backlog. Score ${score}/100.\n`;
246
+ else
247
+ md += `✅ GO for production. Excellent posture. Score ${score}/100.\n`;
248
+ md += `\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
249
+ md += `Report generated by CyberAudit v${version} — Deterministic Scanner\n`;
250
+ md += `Findings are deterministic (regex + patterns), not LLM hallucinations.\n`;
251
+ md += `For full manual audit, run /audit:web, /audit:api, etc. via skill.\n`;
252
+ md += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
253
+ return {
254
+ markdown: md,
255
+ score,
256
+ verdict,
257
+ dashboard,
258
+ owaspCompliance: owasp,
259
+ };
260
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,73 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { generateReport } from './generator.js';
3
+ describe('Report Generator', () => {
4
+ it('should generate empty report with 100 score when no findings', () => {
5
+ const out = generateReport({
6
+ target: './src',
7
+ version: '3.1.5',
8
+ type: 'web',
9
+ findings: [],
10
+ });
11
+ expect(out.score).toBe(100);
12
+ expect(out.verdict).toBe('EXCELLENT');
13
+ expect(out.markdown).toContain('No deterministic findings');
14
+ expect(out.markdown).toContain('100/100');
15
+ });
16
+ it('should calculate score based on severity', () => {
17
+ const findings = [
18
+ { id: '1', patternId: 'AWS_ACCESS_KEY', scanner: 'secrets', severity: 'CRITICAL', cvss: 9.1, cvssVector: 'CVSS:3.1/...', file: 'a.js', line: 1, match: 'AKIA...', description: 'desc', remediation: 'fix', owasp: 'A07' },
19
+ { id: '2', patternId: 'WEB_XSS', scanner: 'web', severity: 'HIGH', cvss: 7.5, cvssVector: 'CVSS:3.1/...', file: 'b.js', line: 2, match: 'innerHTML', description: 'desc', remediation: 'fix', owasp: 'A03' },
20
+ ];
21
+ const out = generateReport({
22
+ target: './app',
23
+ version: '3.1.5',
24
+ type: 'web',
25
+ findings,
26
+ });
27
+ // 100 -20 -10 =70
28
+ expect(out.score).toBe(70);
29
+ expect(out.dashboard.CRITICAL).toBe(1);
30
+ expect(out.dashboard.HIGH).toBe(1);
31
+ expect(out.markdown).toContain('CRITICAL');
32
+ expect(out.markdown).toContain('AWS_ACCESS_KEY');
33
+ });
34
+ it('should produce markdown with required sections', () => {
35
+ const findings = [
36
+ { id: 'VULN-001', patternId: 'WEB_SQLI_CONCAT', scanner: 'web', severity: 'CRITICAL', cvss: 9.8, cvssVector: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H', file: 'db.js', line: 10, match: 'db.query("SELECT "+req.query.id)', description: 'SQLi via concat', remediation: 'Use param query', owasp: 'A03', cwe: 'CWE-89' },
37
+ ];
38
+ const out = generateReport({
39
+ target: 'myapp',
40
+ version: '3.1.5',
41
+ type: 'web',
42
+ findings,
43
+ framework: 'Express',
44
+ });
45
+ expect(out.markdown).toContain('REPORT HEADER');
46
+ expect(out.markdown).toContain('EXECUTIVE SUMMARY');
47
+ expect(out.markdown).toContain('DETAILED FINDINGS');
48
+ expect(out.markdown).toContain('OWASP TOP 10');
49
+ expect(out.markdown).toContain('PRIORITISED REMEDIATION PLAN');
50
+ expect(out.markdown).toContain('CONCLUSION');
51
+ expect(out.markdown).toContain('myapp');
52
+ expect(out.markdown).toContain('Express');
53
+ });
54
+ it('should set NO-GO for critical', () => {
55
+ const findings = [
56
+ { id: '1', patternId: 'WEB_EVAL', scanner: 'web', severity: 'CRITICAL', cvss: 9.8, cvssVector: 'CVSS:3.1/...', file: 'bad.js', line: 1, match: 'eval(', description: 'eval', remediation: 'remove', owasp: 'A03' },
57
+ ];
58
+ const out = generateReport({ target: '.', version: '3.1.5', type: 'web', findings });
59
+ expect(out.verdict).toBe('CRITICAL');
60
+ expect(out.markdown).toContain('NO-GO');
61
+ });
62
+ it('should contain bar visualization', () => {
63
+ const out = generateReport({ target: '.', version: '3.1.5', type: 'web', findings: [] });
64
+ expect(out.markdown).toContain('█');
65
+ // For 100% score, bar is fully filled, no ░ expected. Test with non-100 score for both chars
66
+ const findings = [
67
+ { id: '1', patternId: 'WEB_EVAL', scanner: 'web', severity: 'MEDIUM', cvss: 5.0, cvssVector: 'CVSS:3.1/...', file: 'a.js', line: 1, match: 'eval(', description: 'eval', remediation: 'remove', owasp: 'A03' },
68
+ ];
69
+ const out2 = generateReport({ target: '.', version: '3.1.5', type: 'web', findings });
70
+ expect(out2.markdown).toContain('█');
71
+ expect(out2.markdown).toContain('░');
72
+ });
73
+ });
@@ -0,0 +1,31 @@
1
+ export interface UnifiedFinding {
2
+ id: string;
3
+ patternId: string;
4
+ scanner: 'secrets' | 'web' | 'api' | 'cloud' | 'manual';
5
+ severity: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW' | 'INFO';
6
+ cvss: number;
7
+ cvssVector: string;
8
+ file: string;
9
+ line: number;
10
+ match: string;
11
+ description: string;
12
+ remediation: string;
13
+ owasp?: string;
14
+ cwe?: string;
15
+ framework?: string;
16
+ }
17
+ export interface ReportInput {
18
+ target: string;
19
+ version: string;
20
+ type: 'web' | 'api' | 'mobile' | 'cloud' | 'full';
21
+ findings: UnifiedFinding[];
22
+ framework?: string;
23
+ date?: string;
24
+ }
25
+ export interface ReportOutput {
26
+ markdown: string;
27
+ score: number;
28
+ verdict: 'CRITICAL' | 'HIGH' | 'MODERATE' | 'GOOD' | 'EXCELLENT';
29
+ dashboard: Record<string, number>;
30
+ owaspCompliance: Record<string, string>;
31
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,32 @@
1
+ import { SecretFinding, ScanOptions, Severity } from './types.js';
2
+ type Pattern = {
3
+ id: string;
4
+ name: string;
5
+ regex: RegExp;
6
+ severity: Severity;
7
+ cvss: number;
8
+ cvssVector: string;
9
+ description: string;
10
+ remediation: string;
11
+ owasp: string;
12
+ cwe: string;
13
+ redact?: (m: string) => string;
14
+ };
15
+ declare const PATTERNS: Pattern[];
16
+ export declare function scanSecrets(targetPath: string, opts?: ScanOptions): SecretFinding[];
17
+ export declare function formatFindingsText(findings: SecretFinding[], target: string): string;
18
+ export declare function formatFindingsJson(findings: SecretFinding[]): {
19
+ id: string;
20
+ patternId: string;
21
+ severity: Severity;
22
+ cvss: number;
23
+ cvssVector: string;
24
+ file: string;
25
+ line: number;
26
+ match: string;
27
+ description: string;
28
+ remediation: string;
29
+ owasp: string | undefined;
30
+ cwe: string | undefined;
31
+ }[];
32
+ export { PATTERNS };