opena2a-cli 0.1.2 → 0.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 (63) hide show
  1. package/README.md +225 -1
  2. package/dist/commands/guard-hooks.d.ts +27 -0
  3. package/dist/commands/guard-hooks.d.ts.map +1 -0
  4. package/dist/commands/guard-hooks.js +207 -0
  5. package/dist/commands/guard-hooks.js.map +1 -0
  6. package/dist/commands/guard-policy.d.ts +54 -0
  7. package/dist/commands/guard-policy.d.ts.map +1 -0
  8. package/dist/commands/guard-policy.js +251 -0
  9. package/dist/commands/guard-policy.js.map +1 -0
  10. package/dist/commands/guard-signing.d.ts +52 -0
  11. package/dist/commands/guard-signing.d.ts.map +1 -0
  12. package/dist/commands/guard-signing.js +185 -0
  13. package/dist/commands/guard-signing.js.map +1 -0
  14. package/dist/commands/guard-snapshots.d.ts +54 -0
  15. package/dist/commands/guard-snapshots.d.ts.map +1 -0
  16. package/dist/commands/guard-snapshots.js +346 -0
  17. package/dist/commands/guard-snapshots.js.map +1 -0
  18. package/dist/commands/guard.d.ts +60 -4
  19. package/dist/commands/guard.d.ts.map +1 -1
  20. package/dist/commands/guard.js +475 -95
  21. package/dist/commands/guard.js.map +1 -1
  22. package/dist/commands/init.js +3 -4
  23. package/dist/commands/init.js.map +1 -1
  24. package/dist/commands/shield.d.ts +3 -0
  25. package/dist/commands/shield.d.ts.map +1 -1
  26. package/dist/commands/shield.js +458 -30
  27. package/dist/commands/shield.js.map +1 -1
  28. package/dist/index.js +15 -6
  29. package/dist/index.js.map +1 -1
  30. package/dist/router.d.ts.map +1 -1
  31. package/dist/router.js +1 -0
  32. package/dist/router.js.map +1 -1
  33. package/dist/shield/arp-bridge.d.ts +62 -0
  34. package/dist/shield/arp-bridge.d.ts.map +1 -0
  35. package/dist/shield/arp-bridge.js +198 -0
  36. package/dist/shield/arp-bridge.js.map +1 -0
  37. package/dist/shield/baselines.d.ts +58 -0
  38. package/dist/shield/baselines.d.ts.map +1 -0
  39. package/dist/shield/baselines.js +371 -0
  40. package/dist/shield/baselines.js.map +1 -0
  41. package/dist/shield/findings.d.ts +52 -0
  42. package/dist/shield/findings.d.ts.map +1 -0
  43. package/dist/shield/findings.js +336 -0
  44. package/dist/shield/findings.js.map +1 -0
  45. package/dist/shield/integrity.d.ts.map +1 -1
  46. package/dist/shield/integrity.js +6 -2
  47. package/dist/shield/integrity.js.map +1 -1
  48. package/dist/shield/report-html.d.ts +29 -0
  49. package/dist/shield/report-html.d.ts.map +1 -0
  50. package/dist/shield/report-html.js +596 -0
  51. package/dist/shield/report-html.js.map +1 -0
  52. package/dist/shield/sarif.d.ts +65 -0
  53. package/dist/shield/sarif.d.ts.map +1 -0
  54. package/dist/shield/sarif.js +108 -0
  55. package/dist/shield/sarif.js.map +1 -0
  56. package/dist/shield/status.d.ts.map +1 -1
  57. package/dist/shield/status.js +6 -6
  58. package/dist/shield/status.js.map +1 -1
  59. package/dist/shield/types.d.ts +19 -1
  60. package/dist/shield/types.d.ts.map +1 -1
  61. package/dist/shield/types.js +2 -1
  62. package/dist/shield/types.js.map +1 -1
  63. package/package.json +1 -1
@@ -0,0 +1,596 @@
1
+ "use strict";
2
+ /**
3
+ * Shield HTML Posture Report Generator.
4
+ *
5
+ * Generates a self-contained HTML file with:
6
+ * - Dark theme (slate-900 background, slate-800 cards)
7
+ * - Posture score circular gauge with grade letter
8
+ * - Severity breakdown horizontal bar chart
9
+ * - Agent activity table
10
+ * - Policy violations table with severity filter
11
+ * - Runtime protection, credential exposure, supply chain cards
12
+ * - Event timeline / narrative section
13
+ *
14
+ * Design tokens:
15
+ * Background: #0f172a (slate-900), Card: #1e293b (slate-800)
16
+ * Primary: #06b6d4 (teal), Score: teal
17
+ * Critical: #ef4444, High: #f97316, Medium: #eab308, Low: #3b82f6, Info: #6b7280
18
+ * Font: system monospace (JetBrains Mono fallback)
19
+ *
20
+ * No external dependencies. No emojis.
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.generateExecutiveSummary = generateExecutiveSummary;
24
+ exports.generateShieldHtmlReport = generateShieldHtmlReport;
25
+ /**
26
+ * Generate the executive summary text from report data.
27
+ * No LLM needed -- deterministic sentence generation.
28
+ */
29
+ function generateExecutiveSummary(report, findings, trend) {
30
+ const sentences = [];
31
+ // Sentence 1: Score + grade + trend
32
+ if (trend) {
33
+ const dir = trend.direction === 'improving' ? 'improved'
34
+ : trend.direction === 'declining' ? 'declined' : 'remained stable';
35
+ sentences.push(`Security posture score ${dir} from ${trend.previousScore}/${trend.previousGrade} to ${report.posture.score}/${report.posture.grade} over ${trend.periodDays} day${trend.periodDays !== 1 ? 's' : ''} (delta: ${trend.delta > 0 ? '+' : ''}${trend.delta}).`);
36
+ }
37
+ else {
38
+ sentences.push(`Security posture score: ${report.posture.score}/100 (Grade ${report.posture.grade}). No previous snapshot available for trend comparison.`);
39
+ }
40
+ // Sentence 2: Top finding category + count
41
+ const criticalFindings = findings.filter(f => f.finding.severity === 'critical');
42
+ const highFindings = findings.filter(f => f.finding.severity === 'high');
43
+ if (criticalFindings.length > 0) {
44
+ const totalCrit = criticalFindings.reduce((sum, f) => sum + f.count, 0);
45
+ sentences.push(`${totalCrit} critical finding${totalCrit !== 1 ? 's' : ''} across ${criticalFindings.length} categor${criticalFindings.length !== 1 ? 'ies' : 'y'} require immediate attention.`);
46
+ }
47
+ else if (highFindings.length > 0) {
48
+ const totalHigh = highFindings.reduce((sum, f) => sum + f.count, 0);
49
+ sentences.push(`${totalHigh} high-severity finding${totalHigh !== 1 ? 's' : ''} detected. No critical findings.`);
50
+ }
51
+ else if (findings.length > 0) {
52
+ sentences.push(`${findings.length} finding${findings.length !== 1 ? 's' : ''} detected, none above medium severity.`);
53
+ }
54
+ else {
55
+ sentences.push('No security findings detected in this reporting period.');
56
+ }
57
+ // Sentence 3: Policy posture
58
+ const pe = report.policyEvaluation;
59
+ if (pe.blocked > 0) {
60
+ sentences.push(`Policy enforcement is active: ${pe.blocked} action${pe.blocked !== 1 ? 's' : ''} blocked, ${pe.monitored} monitored.`);
61
+ }
62
+ else if (pe.monitored > 0) {
63
+ sentences.push(`Policy is in monitor-only mode: ${pe.monitored} action${pe.monitored !== 1 ? 's' : ''} logged but not blocked.`);
64
+ }
65
+ else {
66
+ sentences.push('No policy enforcement activity recorded.');
67
+ }
68
+ // Sentence 4: Config integrity
69
+ const ci = report.configIntegrity;
70
+ if (ci.filesMonitored > 0) {
71
+ if (ci.tamperedFiles.length > 0) {
72
+ sentences.push(`WARNING: ${ci.tamperedFiles.length} of ${ci.filesMonitored} monitored config file${ci.filesMonitored !== 1 ? 's' : ''} show tampering.`);
73
+ }
74
+ else {
75
+ sentences.push(`All ${ci.filesMonitored} monitored config file${ci.filesMonitored !== 1 ? 's' : ''} have valid signatures.`);
76
+ }
77
+ }
78
+ return sentences.join(' ');
79
+ }
80
+ function generateShieldHtmlReport(report, narrative, findings, trend) {
81
+ const findingsData = findings ?? [];
82
+ const trendData = trend ?? report.posture.trend ?? null;
83
+ const executiveSummary = generateExecutiveSummary(report, findingsData, trendData);
84
+ const jsonData = JSON.stringify({
85
+ report,
86
+ narrative: narrative ?? null,
87
+ findings: findingsData,
88
+ trend: trendData,
89
+ executiveSummary,
90
+ });
91
+ const findingsCount = findingsData.length;
92
+ const violationsCount = (report.policyEvaluation.topViolations || []).length;
93
+ const agentKeys = Object.keys(report.agentActivity.byAgent || {});
94
+ return `<!DOCTYPE html>
95
+ <html lang="en">
96
+ <head>
97
+ <meta charset="UTF-8">
98
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
99
+ <title>Shield Security Report - ${escapeHtml(report.hostname)}</title>
100
+ <style>
101
+ ${CSS}
102
+ </style>
103
+ </head>
104
+ <body>
105
+ <script id="report-data" type="application/json">${jsonData.replace(/<\//g, '<\\/')}</script>
106
+ <div id="app">
107
+ <header class="header">
108
+ <div class="header-top">
109
+ <div class="header-left">
110
+ <h1 class="logo">Shield</h1>
111
+ <span class="header-sep">|</span>
112
+ <span class="header-label">Security Posture Report</span>
113
+ </div>
114
+ <div class="header-right">
115
+ <span class="header-meta">${escapeHtml(report.hostname)} &middot; ${escapeHtml(formatDate(report.periodStart))} to ${escapeHtml(formatDate(report.periodEnd))}</span>
116
+ </div>
117
+ </div>
118
+ <nav class="nav-tabs" id="main-nav">
119
+ <button class="nav-tab active" data-page="overview">Overview</button>
120
+ <button class="nav-tab" data-page="findings">Findings${findingsCount > 0 ? ` <span class="nav-badge">${findingsCount}</span>` : ''}</button>
121
+ <button class="nav-tab" data-page="agents">Agents${agentKeys.length > 0 ? ` <span class="nav-badge">${agentKeys.length}</span>` : ''}</button>
122
+ <button class="nav-tab" data-page="violations">Violations${violationsCount > 0 ? ` <span class="nav-badge nav-badge-warn">${violationsCount}</span>` : ''}</button>
123
+ <button class="nav-tab" data-page="protection">Protection</button>
124
+ <button class="nav-tab" data-page="timeline">Timeline</button>
125
+ </nav>
126
+ </header>
127
+
128
+ <main class="main">
129
+ <div class="page active" id="page-overview"></div>
130
+ <div class="page" id="page-findings"></div>
131
+ <div class="page" id="page-agents"></div>
132
+ <div class="page" id="page-violations"></div>
133
+ <div class="page" id="page-protection"></div>
134
+ <div class="page" id="page-timeline"></div>
135
+ </main>
136
+
137
+ <footer class="footer">
138
+ <span>Generated ${escapeHtml(formatDate(report.generatedAt))} by OpenA2A Shield</span>
139
+ <span class="footer-sep"> | </span>
140
+ <a href="https://opena2a.org" target="_blank" rel="noopener noreferrer">opena2a.org</a>
141
+ </footer>
142
+ </div>
143
+ <script>
144
+ ${JS}
145
+ </script>
146
+ </body>
147
+ </html>`;
148
+ }
149
+ function escapeHtml(str) {
150
+ return str
151
+ .replace(/&/g, '&amp;')
152
+ .replace(/</g, '&lt;')
153
+ .replace(/>/g, '&gt;')
154
+ .replace(/"/g, '&quot;')
155
+ .replace(/'/g, '&#39;');
156
+ }
157
+ function formatDate(iso) {
158
+ try {
159
+ const d = new Date(iso);
160
+ if (isNaN(d.getTime()))
161
+ return iso;
162
+ return d.toISOString().replace('T', ' ').replace(/\.\d+Z$/, ' UTC');
163
+ }
164
+ catch {
165
+ return iso;
166
+ }
167
+ }
168
+ // --- Embedded CSS ---
169
+ const CSS = `
170
+ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
171
+ :root{
172
+ --bg:#0f172a;--card:#1e293b;--card-border:#334155;--card-hover:#334155;
173
+ --primary:#06b6d4;--primary-dim:#0891b2;
174
+ --text:#e2e8f0;--muted:#94a3b8;--dim:#64748b;
175
+ --critical:#ef4444;--high:#f97316;--medium:#eab308;--low:#3b82f6;--info:#6b7280;
176
+ --green:#22c55e;--red:#ef4444;--amber:#f59e0b;
177
+ --radius:8px;--gap:16px;
178
+ --font:'JetBrains Mono','Fira Code','SF Mono',Menlo,Consolas,monospace;
179
+ }
180
+ body{font-family:var(--font);background:var(--bg);color:var(--text);line-height:1.6;font-size:14px}
181
+ a{color:var(--primary);text-decoration:none}
182
+ a:hover{text-decoration:underline}
183
+
184
+ .header{position:sticky;top:0;background:var(--bg);z-index:100;border-bottom:1px solid var(--card-border)}
185
+ .header-top{display:flex;justify-content:space-between;align-items:center;padding:12px 24px 0}
186
+ .header-left{display:flex;align-items:center;gap:12px}
187
+ .logo{font-size:20px;font-weight:700;color:var(--primary)}
188
+ .header-sep{color:var(--card-border)}
189
+ .header-label{color:var(--muted);font-size:14px}
190
+ .header-right{display:flex;align-items:center;gap:12px}
191
+ .header-meta{color:var(--dim);font-size:12px}
192
+
193
+ .nav-tabs{display:flex;gap:2px;padding:12px 24px 0;overflow-x:auto}
194
+ .nav-tab{background:transparent;border:none;border-bottom:2px solid transparent;padding:10px 16px;color:var(--muted);cursor:pointer;font-family:var(--font);font-size:13px;font-weight:600;transition:all .15s;white-space:nowrap;display:flex;align-items:center;gap:6px}
195
+ .nav-tab:hover{color:var(--text);border-bottom-color:var(--card-border)}
196
+ .nav-tab.active{color:var(--primary);border-bottom-color:var(--primary)}
197
+ .nav-badge{background:var(--card-border);color:var(--text);font-size:10px;padding:1px 6px;border-radius:10px;font-weight:700}
198
+ .nav-badge-warn{background:rgba(249,115,22,0.25);color:var(--high)}
199
+
200
+ .page{display:none}
201
+ .page.active{display:block}
202
+
203
+ .main{max-width:1280px;margin:0 auto;padding:24px}
204
+
205
+ .overview-top{display:grid;grid-template-columns:260px 1fr 1fr;gap:var(--gap);margin-bottom:24px}
206
+ @media(max-width:900px){.overview-top{grid-template-columns:1fr}}
207
+
208
+ .donut-legend{display:flex;flex-wrap:wrap;gap:12px;margin-top:12px}
209
+ .donut-legend-item{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--muted)}
210
+ .donut-legend-dot{width:10px;height:10px;border-radius:2px;flex-shrink:0}
211
+
212
+ .search-box{display:flex;margin-bottom:14px}
213
+ .search-input{flex:1;background:var(--bg);border:1px solid var(--card-border);border-radius:var(--radius);padding:8px 12px;color:var(--text);font-family:var(--font);font-size:12px;outline:none;transition:border-color .15s}
214
+ .search-input:focus{border-color:var(--primary)}
215
+ .search-input::placeholder{color:var(--dim)}
216
+
217
+ .finding-row{cursor:pointer;transition:background .15s}
218
+ .finding-row:hover td{background:rgba(6,182,212,0.04)}
219
+ .finding-expand{display:none;background:rgba(0,0,0,0.15)}
220
+ .finding-expand.open{display:table-row}
221
+ .finding-detail{padding:12px 20px}
222
+ .finding-detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
223
+ .finding-meta{font-size:11px;color:var(--muted);margin-bottom:4px;text-transform:uppercase;letter-spacing:0.03em}
224
+ .finding-val{font-size:12px;color:var(--text);margin-bottom:10px}
225
+ .finding-examples{margin-top:8px}
226
+ .finding-example{background:var(--bg);border:1px solid var(--card-border);border-radius:4px;padding:8px 12px;margin-bottom:6px;font-size:11px;overflow-x:auto}
227
+
228
+ .footer{text-align:center;padding:24px;color:var(--dim);font-size:12px;border-top:1px solid var(--card-border);margin-top:48px}
229
+ .footer-sep{color:var(--card-border);margin:0 4px}
230
+
231
+ /* Section headings */
232
+ .section-title{font-size:16px;font-weight:700;color:var(--text);margin:32px 0 16px;padding-bottom:8px;border-bottom:1px solid var(--card-border)}
233
+ .section-title:first-child{margin-top:0}
234
+
235
+ /* Cards */
236
+ .card{background:var(--card);border:1px solid var(--card-border);border-radius:var(--radius);padding:20px;margin-bottom:var(--gap)}
237
+ .card-title{font-size:13px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:12px}
238
+
239
+ /* Stats grid */
240
+ .stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:var(--gap);margin-bottom:24px}
241
+ .stat-card{background:var(--card);border:1px solid var(--card-border);border-radius:var(--radius);padding:20px;text-align:center}
242
+ .stat-value{font-size:28px;font-weight:700}
243
+ .stat-label{color:var(--muted);font-size:12px;margin-top:4px}
244
+
245
+ /* Posture gauge section */
246
+ .posture-section{display:grid;grid-template-columns:280px 1fr;gap:24px;margin-bottom:24px}
247
+ .gauge-card{background:var(--card);border:1px solid var(--card-border);border-radius:var(--radius);padding:24px;display:flex;flex-direction:column;align-items:center;justify-content:center}
248
+ .gauge-card .gauge-label{font-size:13px;color:var(--muted);margin-top:8px}
249
+ .gauge-card .gauge-trend{font-size:12px;color:var(--dim);margin-top:4px}
250
+
251
+ /* Severity bar chart */
252
+ .bar-chart{display:flex;flex-direction:column;gap:10px}
253
+ .bar-row{display:flex;align-items:center;gap:12px}
254
+ .bar-label{width:70px;text-align:right;font-size:12px;font-weight:600;text-transform:uppercase;flex-shrink:0}
255
+ .bar-track{flex:1;height:24px;background:rgba(255,255,255,0.05);border-radius:4px;overflow:hidden;position:relative}
256
+ .bar-fill{height:100%;border-radius:4px;transition:width .3s ease;display:flex;align-items:center;justify-content:flex-end;padding-right:8px;min-width:0}
257
+ .bar-count{font-size:11px;font-weight:700;color:white}
258
+
259
+ /* Tables */
260
+ .data-table{width:100%;border-collapse:collapse;font-size:13px}
261
+ .data-table th{text-align:left;padding:10px 12px;color:var(--muted);font-weight:600;font-size:11px;text-transform:uppercase;letter-spacing:0.05em;border-bottom:2px solid var(--card-border)}
262
+ .data-table td{padding:10px 12px;border-bottom:1px solid var(--card-border)}
263
+ .data-table tr:last-child td{border-bottom:none}
264
+ .data-table tr:hover td{background:rgba(255,255,255,0.02)}
265
+
266
+ /* Severity badges */
267
+ .sev-badge{display:inline-block;font-size:10px;font-weight:700;text-transform:uppercase;padding:2px 8px;border-radius:4px}
268
+ .sev-critical{background:rgba(239,68,68,0.15);color:var(--critical)}
269
+ .sev-high{background:rgba(249,115,22,0.15);color:var(--high)}
270
+ .sev-medium{background:rgba(234,179,8,0.15);color:var(--medium)}
271
+ .sev-low{background:rgba(59,130,246,0.15);color:var(--low)}
272
+ .sev-info{background:rgba(107,114,128,0.15);color:var(--info)}
273
+
274
+ /* Filter controls */
275
+ .filter-bar{display:flex;gap:8px;margin-bottom:16px;flex-wrap:wrap;align-items:center}
276
+ .filter-btn{background:transparent;border:1px solid var(--card-border);border-radius:var(--radius);padding:6px 12px;color:var(--muted);cursor:pointer;font-family:var(--font);font-size:12px;transition:all .2s}
277
+ .filter-btn:hover{border-color:var(--text);color:var(--text)}
278
+ .filter-btn.active{border-color:var(--primary);color:var(--primary)}
279
+ .filter-btn[data-sev="critical"].active{border-color:var(--critical);color:var(--critical)}
280
+ .filter-btn[data-sev="high"].active{border-color:var(--high);color:var(--high)}
281
+ .filter-btn[data-sev="medium"].active{border-color:var(--medium);color:var(--medium)}
282
+ .filter-btn[data-sev="low"].active{border-color:var(--low);color:var(--low)}
283
+ .filter-btn[data-sev="info"].active{border-color:var(--info);color:var(--info)}
284
+
285
+ .violation-count{color:var(--muted);font-size:12px;margin-left:auto}
286
+
287
+ /* Detail cards (runtime, creds, supply chain) */
288
+ .detail-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:var(--gap);margin-bottom:24px}
289
+ .detail-row{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid rgba(255,255,255,0.05)}
290
+ .detail-row:last-child{border-bottom:none}
291
+ .detail-key{color:var(--muted);font-size:13px}
292
+ .detail-val{font-weight:600;font-size:13px}
293
+
294
+ /* Narrative */
295
+ .narrative-section{margin-top:24px}
296
+ .narrative-block{margin-bottom:16px}
297
+ .narrative-block h4{font-size:13px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:8px}
298
+ .narrative-text{color:var(--text);font-size:13px;line-height:1.7}
299
+ .narrative-list{list-style:none;padding:0}
300
+ .narrative-list li{padding:4px 0;color:var(--text);font-size:13px}
301
+ .narrative-list li::before{content:"--";color:var(--dim);margin-right:8px}
302
+ .narrative-concern li::before{color:var(--amber)}
303
+ .narrative-highlight li::before{color:var(--green)}
304
+ .narrative-rec li::before{color:var(--primary)}
305
+
306
+ /* Provider list */
307
+ .provider-list{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}
308
+ .provider-tag{background:rgba(6,182,212,0.1);border:1px solid rgba(6,182,212,0.3);border-radius:4px;padding:2px 10px;font-size:12px;color:var(--primary)}
309
+
310
+ /* Empty state */
311
+ .empty-state{color:var(--dim);font-size:13px;padding:16px 0;text-align:center}
312
+
313
+ /* Posture factors */
314
+ .factor-row{display:flex;align-items:center;gap:12px;padding:6px 0}
315
+ .factor-name{width:100px;font-size:12px;color:var(--muted)}
316
+ .factor-bar{flex:1;height:8px;background:rgba(255,255,255,0.05);border-radius:4px;overflow:hidden}
317
+ .factor-fill{height:100%;border-radius:4px;background:var(--primary)}
318
+ .factor-score{width:40px;text-align:right;font-size:12px;font-weight:600}
319
+ .factor-detail{font-size:11px;color:var(--dim);width:160px}
320
+
321
+ /* Active/inactive indicator */
322
+ .status-active{color:var(--green);font-weight:600}
323
+ .status-inactive{color:var(--red);font-weight:600}
324
+
325
+ /* Compliance badges */
326
+ .badge-owasp{background:#f59e0b;color:#000;padding:2px 6px;border-radius:3px;font-size:10px;font-weight:700;display:inline-block;margin:1px 2px}
327
+ .badge-mitre{background:#8b5cf6;color:#fff;padding:2px 6px;border-radius:3px;font-size:10px;font-weight:700;display:inline-block;margin:1px 2px}
328
+ .finding-id{font-family:var(--font);font-size:11px;color:var(--primary);font-weight:600;cursor:help}
329
+
330
+ /* Remediation command */
331
+ .remediation-cmd{display:flex;align-items:center;gap:6px}
332
+ .remediation-code{background:rgba(255,255,255,0.05);border:1px solid var(--card-border);border-radius:4px;padding:3px 8px;font-size:11px;font-family:var(--font);color:var(--text);max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
333
+ .copy-btn{background:transparent;border:1px solid var(--card-border);border-radius:4px;padding:3px 6px;color:var(--muted);cursor:pointer;font-family:var(--font);font-size:10px;transition:all .2s}
334
+ .copy-btn:hover{border-color:var(--primary);color:var(--primary)}
335
+ .copy-btn.copied{border-color:var(--green);color:var(--green)}
336
+
337
+ /* Executive summary */
338
+ .exec-summary{background:var(--card);border:1px solid var(--card-border);border-radius:var(--radius);padding:20px;margin-bottom:24px}
339
+ .exec-summary-title{font-size:13px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:12px}
340
+ .exec-summary-text{color:var(--text);font-size:13px;line-height:1.8}
341
+
342
+ /* Trend indicator */
343
+ .trend-indicator{display:inline-flex;align-items:center;gap:6px;font-size:13px;margin-top:8px}
344
+ .trend-improving{color:var(--green)}
345
+ .trend-declining{color:var(--red)}
346
+ .trend-stable{color:var(--muted)}
347
+ .trend-delta{font-weight:700}
348
+
349
+ @media(max-width:768px){
350
+ .posture-section{grid-template-columns:1fr}
351
+ .detail-grid{grid-template-columns:1fr}
352
+ .header-top{flex-direction:column;gap:8px}
353
+ .header-right{width:100%}
354
+ .stats-grid{grid-template-columns:repeat(2,1fr)}
355
+ .finding-detail-grid{grid-template-columns:1fr}
356
+ .nav-tabs{gap:0}
357
+ }
358
+ `;
359
+ // --- Embedded JavaScript ---
360
+ const JS = `
361
+ (function() {
362
+ 'use strict';
363
+ var raw = JSON.parse(document.getElementById('report-data').textContent);
364
+ var report = raw.report;
365
+ var narrative = raw.narrative;
366
+ var findings = raw.findings || [];
367
+ var trend = raw.trend || null;
368
+ var executiveSummary = raw.executiveSummary || '';
369
+ var currentPage = 'overview';
370
+ var activeViolationFilters = new Set(['critical','high','medium','low','info']);
371
+ var pagesRendered = {};
372
+
373
+ function init() { renderPage('overview'); bindNav(); }
374
+
375
+ function esc(s) { if (!s) return ''; var d = document.createElement('div'); d.textContent = String(s); return d.innerHTML; }
376
+ function formatTs(iso) { if (!iso) return '--'; try { var d = new Date(iso); if (isNaN(d.getTime())) return iso; return d.toISOString().replace('T',' ').replace(/\\.\\d+Z$/,' UTC'); } catch(e) { return iso; } }
377
+ function scoreColor(s) { return s >= 90 ? 'var(--green)' : s >= 70 ? 'var(--primary)' : s >= 50 ? 'var(--medium)' : 'var(--red)'; }
378
+ function statCard(v, l, c) { return '<div class="stat-card"><div class="stat-value" style="color:'+c+'">'+v+'</div><div class="stat-label">'+l+'</div></div>'; }
379
+
380
+ function bindNav() {
381
+ document.getElementById('main-nav').addEventListener('click', function(e) {
382
+ var btn = e.target.closest('.nav-tab'); if (!btn) return;
383
+ var pg = btn.dataset.page; if (!pg || pg === currentPage) return;
384
+ var tabs = document.querySelectorAll('.nav-tab');
385
+ for (var i=0;i<tabs.length;i++) tabs[i].classList.toggle('active', tabs[i].dataset.page===pg);
386
+ var pages = document.querySelectorAll('.page');
387
+ for (var i=0;i<pages.length;i++) pages[i].classList.toggle('active', pages[i].id==='page-'+pg);
388
+ currentPage = pg; renderPage(pg);
389
+ });
390
+ }
391
+
392
+ function renderPage(pg) {
393
+ if (pagesRendered[pg]) return; pagesRendered[pg] = true;
394
+ var el = document.getElementById('page-'+pg);
395
+ switch(pg) {
396
+ case 'overview': el.innerHTML = renderOverview(); break;
397
+ case 'findings': el.innerHTML = renderFindings(); bindFindingExpand(); break;
398
+ case 'agents': el.innerHTML = renderAgents(); break;
399
+ case 'violations': el.innerHTML = renderViolations(); bindViolationFilters(); break;
400
+ case 'protection': el.innerHTML = renderProtection(); break;
401
+ case 'timeline': el.innerHTML = renderTimeline(); break;
402
+ }
403
+ }
404
+
405
+ // --- Gauge SVG ---
406
+ function gaugeCircle(score) {
407
+ var sz=170,cx=sz/2,cy=sz/2,r=65,sw=10,circ=2*Math.PI*r;
408
+ var pct=Math.max(0,Math.min(100,score))/100,dash=pct*circ,gap=circ-dash;
409
+ var clr = score>=90?'#22c55e':score>=70?'#06b6d4':score>=50?'#eab308':'#ef4444';
410
+ var g = report.posture.grade||'';
411
+ var s='<svg width="'+sz+'" height="'+sz+'" viewBox="0 0 '+sz+' '+sz+'">';
412
+ s+='<circle cx="'+cx+'" cy="'+cy+'" r="'+r+'" fill="none" stroke="rgba(255,255,255,0.05)" stroke-width="'+sw+'"/>';
413
+ s+='<circle cx="'+cx+'" cy="'+cy+'" r="'+r+'" fill="none" stroke="'+clr+'" stroke-width="'+sw+'" stroke-dasharray="'+dash+' '+gap+'" stroke-dashoffset="'+(circ*0.25)+'" stroke-linecap="round" transform="rotate(-90 '+cx+' '+cy+')"/>';
414
+ s+='<text x="'+cx+'" y="'+(cy-6)+'" text-anchor="middle" dominant-baseline="middle" font-size="32" font-weight="700" fill="'+clr+'" font-family="var(--font)">'+score+'</text>';
415
+ s+='<text x="'+cx+'" y="'+(cy+20)+'" text-anchor="middle" dominant-baseline="middle" font-size="14" font-weight="600" fill="'+clr+'" font-family="var(--font)">Grade '+esc(g)+'</text>';
416
+ s+='</svg>'; return s;
417
+ }
418
+
419
+ function trendHtml() {
420
+ if (!trend) return '';
421
+ var cls='trend-'+trend.direction;
422
+ var arrow=trend.direction==='improving'?'&#9650;':trend.direction==='declining'?'&#9660;':'&#9654;';
423
+ var sign=trend.delta>0?'+':'';
424
+ return '<div class="trend-indicator '+cls+'">'+arrow+' <span class="trend-delta">'+sign+trend.delta+'</span> from '+trend.previousScore+'/'+esc(trend.previousGrade)+' ('+trend.periodDays+'d)</div>';
425
+ }
426
+
427
+ // --- Donut chart ---
428
+ function donutChart() {
429
+ var counts={critical:0,high:0,medium:0,low:0,info:0};
430
+ var violations=report.policyEvaluation.topViolations||[];
431
+ for(var i=0;i<violations.length;i++) counts[violations[i].severity]=(counts[violations[i].severity]||0)+violations[i].count;
432
+ for(var i=0;i<findings.length;i++){var s=findings[i].finding.severity;counts[s]=(counts[s]||0)+findings[i].count;}
433
+ var total=counts.critical+counts.high+counts.medium+counts.low+counts.info;
434
+ if(total===0) return '<div class="empty-state">No findings in this period.</div>';
435
+ var sz=150,cx=sz/2,cy=sz/2,r=50,sw=18,circ=2*Math.PI*r;
436
+ var colors={critical:'#ef4444',high:'#f97316',medium:'#eab308',low:'#3b82f6',info:'#6b7280'};
437
+ var order=['critical','high','medium','low','info'];
438
+ var off=0,svg='<svg width="'+sz+'" height="'+sz+'" viewBox="0 0 '+sz+' '+sz+'">';
439
+ for(var j=0;j<order.length;j++){var sv=order[j],c=counts[sv]||0;if(!c)continue;var p=c/total,d=p*circ,g=circ-d;
440
+ svg+='<circle cx="'+cx+'" cy="'+cy+'" r="'+r+'" fill="none" stroke="'+colors[sv]+'" stroke-width="'+sw+'" stroke-dasharray="'+d+' '+g+'" stroke-dashoffset="'+(-off+circ*0.25)+'" transform="rotate(-90 '+cx+' '+cy+')"/>';off+=d;}
441
+ svg+='<text x="'+cx+'" y="'+cy+'" text-anchor="middle" dominant-baseline="middle" font-size="18" font-weight="700" fill="var(--text)" font-family="var(--font)">'+total+'</text></svg>';
442
+ var leg='<div class="donut-legend" style="flex-direction:column">';
443
+ for(var k=0;k<order.length;k++){var sv=order[k];if(!counts[sv])continue;leg+='<div class="donut-legend-item"><div class="donut-legend-dot" style="background:'+colors[sv]+'"></div>'+sv.charAt(0).toUpperCase()+sv.slice(1)+': '+counts[sv]+'</div>';}
444
+ leg+='</div>';
445
+ return '<div style="display:flex;align-items:center;gap:16px;flex-wrap:wrap">'+svg+leg+'</div>';
446
+ }
447
+
448
+ function factorsHtml() {
449
+ var factors=report.posture.factors||[];if(!factors.length) return '';
450
+ var h='<div class="card"><div class="card-title">Score Factors</div>';
451
+ for(var i=0;i<factors.length;i++){var f=factors[i];var bc=f.score>=70?'var(--green)':f.score>=40?'var(--medium)':'var(--red)';
452
+ h+='<div class="factor-row"><span class="factor-name">'+esc(f.name)+(f.weight?' ('+Math.round(f.weight*100)+'%)':'')+'</span><div class="factor-bar"><div class="factor-fill" style="width:'+f.score+'%;background:'+bc+'"></div></div><span class="factor-score">'+f.score+'</span><span class="factor-detail">'+esc(f.detail)+'</span></div>';}
453
+ h+='</div>'; return h;
454
+ }
455
+
456
+ // ======================== OVERVIEW ========================
457
+ function renderOverview() {
458
+ var h='';
459
+ h+='<div class="stats-grid">';
460
+ h+=statCard(report.posture.score+'/100','Score',scoreColor(report.posture.score));
461
+ h+=statCard('Grade '+(report.posture.grade||'--'),'Posture',scoreColor(report.posture.score));
462
+ h+=statCard(report.agentActivity.totalSessions,'Sessions','var(--primary)');
463
+ h+=statCard(report.agentActivity.totalActions,'Events','var(--text)');
464
+ h+=statCard(report.policyEvaluation.monitored,'Monitored','var(--muted)');
465
+ h+=statCard(report.policyEvaluation.blocked,'Blocked',report.policyEvaluation.blocked>0?'var(--red)':'var(--text)');
466
+ h+='</div>';
467
+
468
+ h+='<h2 class="section-title">Posture Score</h2>';
469
+ h+='<div class="overview-top">';
470
+ h+='<div class="gauge-card">'+gaugeCircle(report.posture.score)+trendHtml()+'</div>';
471
+ h+='<div class="card"><div class="card-title">Severity Breakdown</div>'+donutChart()+'</div>';
472
+ h+=factorsHtml();
473
+ h+='</div>';
474
+
475
+ if(executiveSummary){h+='<div class="exec-summary"><div class="exec-summary-title">Executive Summary</div><div class="exec-summary-text">'+esc(executiveSummary)+'</div></div>';}
476
+
477
+ if(findings.length>0){
478
+ h+='<h2 class="section-title">Top Findings</h2><div class="card"><table class="data-table"><thead><tr><th>ID</th><th>Title</th><th>Severity</th><th>Count</th><th>OWASP</th><th>MITRE</th></tr></thead><tbody>';
479
+ var top=findings.slice(0,5);
480
+ for(var i=0;i<top.length;i++){var f=top[i];h+='<tr><td><span class="finding-id">'+esc(f.finding.id)+'</span></td><td>'+esc(f.finding.title)+'</td><td><span class="sev-badge sev-'+esc(f.finding.severity)+'">'+esc(f.finding.severity)+'</span></td><td>'+f.count+'</td><td><span class="badge-owasp">'+esc(f.finding.owaspAgentic)+'</span></td><td><span class="badge-mitre">'+esc(f.finding.mitreAtlas)+'</span></td></tr>';}
481
+ h+='</tbody></table>';
482
+ if(findings.length>5) h+='<div style="text-align:center;padding:8px;color:var(--dim);font-size:11px;cursor:pointer" onclick="document.querySelector(\\'.nav-tab[data-page=findings]\\').click()">View all '+findings.length+' findings --></div>';
483
+ h+='</div>';
484
+ }
485
+ return h;
486
+ }
487
+
488
+ // ======================== FINDINGS ========================
489
+ function renderFindings() {
490
+ if(!findings.length) return '<h2 class="section-title">Classified Findings</h2><div class="card"><div class="empty-state">No security findings classified in this period.</div></div>';
491
+ var h='<h2 class="section-title">Classified Findings ('+findings.length+')</h2>';
492
+ h+='<div class="search-box"><input type="text" class="search-input" id="findings-search" placeholder="Search findings by ID, title, OWASP, MITRE..." oninput="window._filterFindings(this.value)"></div>';
493
+ h+='<div class="card"><table class="data-table" id="findings-table"><thead><tr><th>ID</th><th>Title</th><th>Severity</th><th>Count</th><th>OWASP</th><th>MITRE</th><th>Remediation</th></tr></thead><tbody>';
494
+ for(var i=0;i<findings.length;i++){var f=findings[i];
495
+ h+='<tr class="finding-row" data-idx="'+i+'" data-search="'+esc((f.finding.id+' '+f.finding.title+' '+f.finding.owaspAgentic+' '+f.finding.mitreAtlas+' '+f.finding.category).toLowerCase())+'">';
496
+ h+='<td><span class="finding-id">'+esc(f.finding.id)+'</span></td><td>'+esc(f.finding.title)+'</td>';
497
+ h+='<td><span class="sev-badge sev-'+esc(f.finding.severity)+'">'+esc(f.finding.severity)+'</span></td><td>'+f.count+'</td>';
498
+ h+='<td><span class="badge-owasp">'+esc(f.finding.owaspAgentic)+'</span></td><td><span class="badge-mitre">'+esc(f.finding.mitreAtlas)+'</span></td>';
499
+ h+='<td><div class="remediation-cmd"><code class="remediation-code" title="'+esc(f.finding.remediation)+'">'+esc(f.finding.remediation)+'</code><button class="copy-btn" data-cmd="'+esc(f.finding.remediation)+'" onclick="event.stopPropagation();copyCmd(this)">Copy</button></div></td></tr>';
500
+ h+='<tr class="finding-expand" id="finding-detail-'+i+'"><td colspan="7"><div class="finding-detail"><div class="finding-detail-grid">';
501
+ h+='<div><div class="finding-meta">Description</div><div class="finding-val">'+esc(f.finding.description)+'</div></div>';
502
+ h+='<div><div class="finding-meta">Category</div><div class="finding-val">'+esc(f.finding.category)+'</div><div class="finding-meta" style="margin-top:8px">Time Range</div><div class="finding-val">'+esc(formatTs(f.firstSeen))+' to '+esc(formatTs(f.lastSeen))+'</div></div></div>';
503
+ if(f.examples&&f.examples.length>0){h+='<div class="finding-examples"><div class="finding-meta">Event Examples ('+f.examples.length+')</div>';
504
+ for(var ei=0;ei<f.examples.length;ei++){var ex=f.examples[ei];h+='<div class="finding-example"><span style="color:var(--dim)">'+esc(formatTs(ex.timestamp))+'</span> <span style="color:var(--primary)">'+esc(ex.source)+'</span>: '+esc(ex.action)+' -> '+esc(ex.target)+' [<span class="sev-badge sev-'+esc(ex.severity)+'" style="font-size:9px;padding:1px 4px">'+esc(ex.severity)+'</span>]</div>';}
505
+ h+='</div>';}
506
+ h+='</div></td></tr>';}
507
+ h+='</tbody></table></div>';return h;
508
+ }
509
+
510
+ function bindFindingExpand(){document.addEventListener('click',function(e){var row=e.target.closest('.finding-row');if(!row)return;var idx=row.dataset.idx;if(idx===undefined)return;var det=document.getElementById('finding-detail-'+idx);if(det)det.classList.toggle('open');});}
511
+ window._filterFindings=function(q){q=q.toLowerCase().trim();var rows=document.querySelectorAll('#findings-table .finding-row');var dets=document.querySelectorAll('#findings-table .finding-expand');for(var i=0;i<rows.length;i++){var m=!q||(rows[i].dataset.search||'').indexOf(q)>=0;rows[i].style.display=m?'':'none';if(dets[i]){dets[i].style.display='none';dets[i].classList.remove('open');}}};
512
+
513
+ // ======================== AGENTS ========================
514
+ function renderAgents() {
515
+ var agents=report.agentActivity.byAgent;var keys=Object.keys(agents);
516
+ if(!keys.length) return '<h2 class="section-title">Agent Activity</h2><div class="card"><div class="empty-state">No agent activity recorded.</div></div>';
517
+ var h='<h2 class="section-title">Agent Activity ('+keys.length+' agents)</h2>';
518
+ h+='<div class="stats-grid">'+statCard(keys.length,'Agents','var(--primary)')+statCard(report.agentActivity.totalSessions,'Sessions','var(--text)')+statCard(report.agentActivity.totalActions,'Total Actions','var(--text)')+'</div>';
519
+ h+='<div class="card"><table class="data-table"><thead><tr><th>Agent</th><th>Sessions</th><th>Actions</th><th>First Seen</th><th>Last Seen</th><th>Top Actions</th></tr></thead><tbody>';
520
+ for(var i=0;i<keys.length;i++){var name=keys[i],a=agents[name];
521
+ var topActs=(a.topActions||[]).slice(0,4).map(function(ta){return '<span style="color:var(--primary)">'+esc(ta.action)+'</span> <span style="color:var(--dim)">('+ta.count+')</span>';}).join(', ');
522
+ h+='<tr><td style="font-weight:600;color:var(--primary)">'+esc(name)+'</td><td>'+a.sessions+'</td><td>'+a.actions+'</td><td style="font-size:11px;color:var(--dim)">'+esc(formatTs(a.firstSeen))+'</td><td style="font-size:11px;color:var(--dim)">'+esc(formatTs(a.lastSeen))+'</td><td style="font-size:11px">'+(topActs||'--')+'</td></tr>';}
523
+ h+='</tbody></table></div>';return h;
524
+ }
525
+
526
+ // ======================== VIOLATIONS ========================
527
+ function renderViolations() {
528
+ var violations=report.policyEvaluation.topViolations||[];
529
+ if(!violations.length) return '<h2 class="section-title">Policy Violations</h2><div class="card"><div class="empty-state">No policy violations recorded.</div></div>';
530
+ var h='<h2 class="section-title">Policy Violations</h2>';
531
+ h+='<div class="stats-grid">'+statCard(violations.length,'Violations','var(--red)')+statCard(report.policyEvaluation.monitored,'Monitored','var(--muted)')+statCard(report.policyEvaluation.blocked,'Blocked',report.policyEvaluation.blocked>0?'var(--red)':'var(--text)')+'</div>';
532
+ h+='<div class="card">';
533
+ h+='<div class="filter-bar" id="violation-filters"><button class="filter-btn active" data-sev="all">All</button><button class="filter-btn active" data-sev="critical">Critical</button><button class="filter-btn active" data-sev="high">High</button><button class="filter-btn active" data-sev="medium">Medium</button><button class="filter-btn active" data-sev="low">Low</button><button class="filter-btn active" data-sev="info">Info</button><span class="violation-count" id="violation-count">'+violations.length+' violations</span></div>';
534
+ h+='<div class="search-box"><input type="text" class="search-input" id="violations-search" placeholder="Search violations..." oninput="window._applyVF()"></div>';
535
+ h+='<table class="data-table" id="violations-table"><thead><tr><th>Finding</th><th>Action</th><th>Target</th><th>Agent</th><th>Count</th><th>Severity</th><th>Compliance</th><th>Remediation</th></tr></thead><tbody>';
536
+ for(var i=0;i<violations.length;i++){var v=violations[i];
537
+ h+='<tr class="violation-row" data-severity="'+esc(v.severity)+'" data-search="'+esc(((v.findingId||'')+' '+v.action+' '+v.target+' '+v.agent).toLowerCase())+'">';
538
+ h+='<td>'+(v.findingId?'<span class="finding-id" title="'+esc(v.recommendation)+'">'+esc(v.findingId)+'</span>':'<span style="color:var(--dim)">--</span>')+'</td>';
539
+ h+='<td>'+esc(v.action)+'</td><td style="max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="'+esc(v.target)+'">'+esc(v.target)+'</td><td>'+esc(v.agent)+'</td><td>'+v.count+'</td>';
540
+ h+='<td><span class="sev-badge sev-'+esc(v.severity)+'">'+esc(v.severity)+'</span></td><td>';
541
+ if(v.compliance&&v.compliance.length>0){for(var ci=0;ci<v.compliance.length;ci++){var tag=v.compliance[ci];if(tag.indexOf('ASI')===0)h+='<span class="badge-owasp">'+esc(tag)+'</span>';else if(tag.indexOf('AML')===0)h+='<span class="badge-mitre">'+esc(tag)+'</span>';}}else h+='--';
542
+ h+='</td><td>';
543
+ if(v.remediationCommand){h+='<div class="remediation-cmd"><code class="remediation-code" title="'+esc(v.remediationCommand)+'">'+esc(v.remediationCommand)+'</code><button class="copy-btn" data-cmd="'+esc(v.remediationCommand)+'" onclick="copyCmd(this)">Copy</button></div>';}else h+=esc(v.recommendation);
544
+ h+='</td></tr>';}
545
+ h+='</tbody></table></div>';return h;
546
+ }
547
+
548
+ function bindViolationFilters(){var bar=document.getElementById('violation-filters');if(!bar)return;bar.addEventListener('click',function(e){if(!e.target.classList.contains('filter-btn'))return;var sev=e.target.dataset.sev;if(sev==='all'){activeViolationFilters=new Set(['critical','high','medium','low','info']);var btns=bar.querySelectorAll('.filter-btn');for(var b=0;b<btns.length;b++)btns[b].classList.add('active');}else{if(activeViolationFilters.has(sev))activeViolationFilters.delete(sev);else activeViolationFilters.add(sev);e.target.classList.toggle('active');var ab=bar.querySelector('.filter-btn[data-sev="all"]');if(ab)ab.classList.toggle('active',activeViolationFilters.size===5);}window._applyVF();});}
549
+ window._applyVF=function(){var rows=document.querySelectorAll('.violation-row');var q=((document.getElementById('violations-search')||{}).value||'').toLowerCase().trim();var vis=0;for(var i=0;i<rows.length;i++){var sv=rows[i].dataset.severity;var sm=activeViolationFilters.has(sv);var qm=!q||(rows[i].dataset.search||'').indexOf(q)>=0;var show=sm&&qm;rows[i].style.display=show?'':'none';if(show)vis++;}var c=document.getElementById('violation-count');if(c)c.textContent=vis+' of '+rows.length+' violations';};
550
+
551
+ // ======================== PROTECTION ========================
552
+ function renderProtection() {
553
+ var h='<h2 class="section-title">Protection Details</h2><div class="detail-grid">';
554
+ var rt=report.runtimeProtection;
555
+ h+='<div class="card"><div class="card-title">Runtime Protection (ARP)</div>';
556
+ h+=dr('ARP Status',rt.arpActive?'<span class="status-active">Active</span>':'<span class="status-inactive">Inactive</span>');
557
+ h+=dr('Processes Spawned',rt.processesSpawned)+dr('Network Connections',rt.networkConnections);
558
+ h+=dr('Anomalies','<span style="color:'+(rt.anomalies>0?'var(--amber)':'var(--green)')+'">'+rt.anomalies+'</span>')+'</div>';
559
+ var cred=report.credentialExposure;
560
+ h+='<div class="card"><div class="card-title">Credential Exposure</div>';
561
+ h+=dr('Access Attempts',cred.accessAttempts)+dr('Unique Credentials',cred.uniqueCredentials);
562
+ var providers=Object.keys(cred.byProvider||{});
563
+ if(providers.length>0){h+='<div class="detail-row"><span class="detail-key">By Provider</span><span class="detail-val">&nbsp;</span></div><div class="provider-list">';for(var p=0;p<providers.length;p++)h+='<span class="provider-tag">'+esc(providers[p])+': '+cred.byProvider[providers[p]]+'</span>';h+='</div>';}
564
+ h+='</div>';
565
+ var sc=report.supplyChain;
566
+ h+='<div class="card"><div class="card-title">Supply Chain</div>';
567
+ h+=dr('Packages Installed',sc.packagesInstalled)+dr('Advisories Found','<span style="color:'+(sc.advisoriesFound>0?'var(--amber)':'var(--green)')+'">'+sc.advisoriesFound+'</span>');
568
+ h+=dr('Blocked Installs','<span style="color:'+(sc.blockedInstalls>0?'var(--red)':'var(--text)')+'">'+sc.blockedInstalls+'</span>')+'</div>';
569
+ var ci=report.configIntegrity;
570
+ h+='<div class="card"><div class="card-title">Config Integrity</div>';
571
+ h+=dr('Files Monitored',ci.filesMonitored);
572
+ h+=dr('Signature Status',ci.signatureStatus==='signed'||ci.signatureStatus==='valid'?'<span class="status-active">Valid</span>':ci.signatureStatus==='unsigned'?'<span style="color:var(--amber)">Unsigned</span>':'<span class="status-inactive">'+esc(ci.signatureStatus)+'</span>');
573
+ if(ci.tamperedFiles&&ci.tamperedFiles.length>0){h+=dr('Tampered Files','<span class="status-inactive">'+ci.tamperedFiles.length+'</span>');for(var t=0;t<ci.tamperedFiles.length;t++)h+='<div style="color:var(--red);font-size:12px;padding:2px 0">'+esc(ci.tamperedFiles[t])+'</div>';}
574
+ else if(ci.filesMonitored>0) h+=dr('Integrity','<span class="status-active">All files valid</span>');
575
+ h+='</div></div>';return h;
576
+ }
577
+ function dr(k,v){return '<div class="detail-row"><span class="detail-key">'+esc(k)+'</span><span class="detail-val">'+v+'</span></div>';}
578
+
579
+ // ======================== TIMELINE ========================
580
+ function renderTimeline() {
581
+ if(!narrative) return '<h2 class="section-title">Event Timeline</h2><div class="card"><div class="empty-state">No narrative analysis available. Use --analyze flag to generate AI-powered event analysis.</div></div>';
582
+ var h='<h2 class="section-title">Event Timeline</h2><div class="card">';
583
+ if(narrative.summary){h+='<div class="narrative-block"><h4>Summary</h4><div class="narrative-text">'+esc(narrative.summary)+'</div></div>';}
584
+ if(narrative.highlights&&narrative.highlights.length>0){h+='<div class="narrative-block"><h4>Highlights</h4><ul class="narrative-list narrative-highlight">';for(var i=0;i<narrative.highlights.length;i++)h+='<li>'+esc(narrative.highlights[i])+'</li>';h+='</ul></div>';}
585
+ if(narrative.concerns&&narrative.concerns.length>0){h+='<div class="narrative-block"><h4>Concerns</h4><ul class="narrative-list narrative-concern">';for(var i=0;i<narrative.concerns.length;i++)h+='<li>'+esc(narrative.concerns[i])+'</li>';h+='</ul></div>';}
586
+ if(narrative.recommendations&&narrative.recommendations.length>0){h+='<div class="narrative-block"><h4>Recommendations</h4><ul class="narrative-list narrative-rec">';for(var i=0;i<narrative.recommendations.length;i++)h+='<li>'+esc(narrative.recommendations[i])+'</li>';h+='</ul></div>';}
587
+ h+='</div>';return h;
588
+ }
589
+
590
+ // --- Copy command ---
591
+ window.copyCmd=function(btn){var cmd=btn.getAttribute('data-cmd');if(!cmd)return;if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(cmd).then(function(){btn.textContent='OK';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},1500);});}else{var ta=document.createElement('textarea');ta.value=cmd;ta.style.position='fixed';ta.style.left='-9999px';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);btn.textContent='OK';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},1500);}};
592
+
593
+ init();
594
+ })();
595
+ `;
596
+ //# sourceMappingURL=report-html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-html.js","sourceRoot":"","sources":["../../src/shield/report-html.ts"],"names":[],"mappings":"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"}