n8n-nodes-trusera 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 (55) hide show
  1. package/README.md +76 -0
  2. package/dist/credentials/TruseraApi.credentials.d.ts +9 -0
  3. package/dist/credentials/TruseraApi.credentials.d.ts.map +1 -0
  4. package/dist/credentials/TruseraApi.credentials.js +39 -0
  5. package/dist/credentials/TruseraApi.credentials.js.map +1 -0
  6. package/dist/index.d.ts +7 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +23 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/lib/config.d.ts +30 -0
  11. package/dist/lib/config.d.ts.map +1 -0
  12. package/dist/lib/config.js +254 -0
  13. package/dist/lib/config.js.map +1 -0
  14. package/dist/lib/dashboardHtml.d.ts +13 -0
  15. package/dist/lib/dashboardHtml.d.ts.map +1 -0
  16. package/dist/lib/dashboardHtml.js +605 -0
  17. package/dist/lib/dashboardHtml.js.map +1 -0
  18. package/dist/lib/models.d.ts +93 -0
  19. package/dist/lib/models.d.ts.map +1 -0
  20. package/dist/lib/models.js +101 -0
  21. package/dist/lib/models.js.map +1 -0
  22. package/dist/lib/policyEngine.d.ts +22 -0
  23. package/dist/lib/policyEngine.d.ts.map +1 -0
  24. package/dist/lib/policyEngine.js +52 -0
  25. package/dist/lib/policyEngine.js.map +1 -0
  26. package/dist/lib/riskScorer.d.ts +13 -0
  27. package/dist/lib/riskScorer.d.ts.map +1 -0
  28. package/dist/lib/riskScorer.js +72 -0
  29. package/dist/lib/riskScorer.js.map +1 -0
  30. package/dist/lib/scanner.d.ts +33 -0
  31. package/dist/lib/scanner.d.ts.map +1 -0
  32. package/dist/lib/scanner.js +590 -0
  33. package/dist/lib/scanner.js.map +1 -0
  34. package/dist/nodes/TruseraDashboard/TruseraDashboard.node.d.ts +6 -0
  35. package/dist/nodes/TruseraDashboard/TruseraDashboard.node.d.ts.map +1 -0
  36. package/dist/nodes/TruseraDashboard/TruseraDashboard.node.js +66 -0
  37. package/dist/nodes/TruseraDashboard/TruseraDashboard.node.js.map +1 -0
  38. package/dist/nodes/TruseraDashboard/trusera.png +0 -0
  39. package/dist/nodes/TruseraDashboard/trusera.svg +4 -0
  40. package/dist/nodes/TruseraPolicy/TruseraPolicy.node.d.ts +6 -0
  41. package/dist/nodes/TruseraPolicy/TruseraPolicy.node.d.ts.map +1 -0
  42. package/dist/nodes/TruseraPolicy/TruseraPolicy.node.js +114 -0
  43. package/dist/nodes/TruseraPolicy/TruseraPolicy.node.js.map +1 -0
  44. package/dist/nodes/TruseraPolicy/trusera.svg +4 -0
  45. package/dist/nodes/TruseraReport/TruseraReport.node.d.ts +6 -0
  46. package/dist/nodes/TruseraReport/TruseraReport.node.d.ts.map +1 -0
  47. package/dist/nodes/TruseraReport/TruseraReport.node.js +201 -0
  48. package/dist/nodes/TruseraReport/TruseraReport.node.js.map +1 -0
  49. package/dist/nodes/TruseraReport/trusera.svg +4 -0
  50. package/dist/nodes/TruseraScan/TruseraScan.node.d.ts +6 -0
  51. package/dist/nodes/TruseraScan/TruseraScan.node.d.ts.map +1 -0
  52. package/dist/nodes/TruseraScan/TruseraScan.node.js +140 -0
  53. package/dist/nodes/TruseraScan/TruseraScan.node.js.map +1 -0
  54. package/dist/nodes/TruseraScan/trusera.svg +4 -0
  55. package/package.json +62 -0
@@ -0,0 +1,605 @@
1
+ "use strict";
2
+ /**
3
+ * Self-contained HTML dashboard generator for Trusera AI-BOM scan results.
4
+ * Ported from Python src/ai_bom/dashboard/frontend.py, adapted for
5
+ * static embedded data (no API calls) with optional AES-256-GCM encryption.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.generateDashboardHtml = generateDashboardHtml;
9
+ const crypto_1 = require("crypto");
10
+ const config_1 = require("./config");
11
+ /**
12
+ * Generate a self-contained HTML dashboard for the given scan results.
13
+ * If `password` is provided, the scan data is AES-256-GCM encrypted and
14
+ * the page shows a password prompt that decrypts client-side.
15
+ */
16
+ function generateDashboardHtml(scanResult, password) {
17
+ const jsonPayload = JSON.stringify(scanResult);
18
+ let dataScript;
19
+ let decryptionScript = '';
20
+ let passwordFormHtml = '';
21
+ const remediationScript = `<script>var REMEDIATION_MAP = ${JSON.stringify(config_1.REMEDIATION_MAP)};</script>`;
22
+ if (password) {
23
+ const salt = (0, crypto_1.randomBytes)(16);
24
+ const iv = (0, crypto_1.randomBytes)(12);
25
+ const key = (0, crypto_1.pbkdf2Sync)(password, salt, 100_000, 32, 'sha256');
26
+ const cipher = (0, crypto_1.createCipheriv)('aes-256-gcm', key, iv);
27
+ const encrypted = Buffer.concat([
28
+ cipher.update(jsonPayload, 'utf8'),
29
+ cipher.final(),
30
+ ]);
31
+ const authTag = cipher.getAuthTag();
32
+ const blob = Buffer.concat([encrypted, authTag]);
33
+ dataScript = `<script>
34
+ var ENCRYPTED_DATA = "${blob.toString('base64')}";
35
+ var SALT = "${salt.toString('base64')}";
36
+ var IV = "${iv.toString('base64')}";
37
+ var SCAN_DATA = null;
38
+ </script>`;
39
+ decryptionScript = `
40
+ async function decryptData(pwd) {
41
+ try {
42
+ var enc = new TextEncoder();
43
+ var saltBuf = Uint8Array.from(atob(SALT), function(c) { return c.charCodeAt(0); });
44
+ var ivBuf = Uint8Array.from(atob(IV), function(c) { return c.charCodeAt(0); });
45
+ var blobBuf = Uint8Array.from(atob(ENCRYPTED_DATA), function(c) { return c.charCodeAt(0); });
46
+ var ciphertext = blobBuf.slice(0, blobBuf.length - 16);
47
+ var tag = blobBuf.slice(blobBuf.length - 16);
48
+ var combined = new Uint8Array(ciphertext.length + tag.length);
49
+ combined.set(ciphertext);
50
+ combined.set(tag, ciphertext.length);
51
+ var keyMaterial = await crypto.subtle.importKey('raw', enc.encode(pwd), 'PBKDF2', false, ['deriveKey']);
52
+ var aesKey = await crypto.subtle.deriveKey(
53
+ { name: 'PBKDF2', salt: saltBuf, iterations: 100000, hash: 'SHA-256' },
54
+ keyMaterial, { name: 'AES-GCM', length: 256 }, false, ['decrypt']
55
+ );
56
+ var decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: ivBuf }, aesKey, combined);
57
+ return JSON.parse(new TextDecoder().decode(decrypted));
58
+ } catch (e) {
59
+ return null;
60
+ }
61
+ }
62
+
63
+ async function handleLogin(e) {
64
+ e.preventDefault();
65
+ var pwd = document.getElementById('pwd-input').value;
66
+ var errEl = document.getElementById('pwd-error');
67
+ errEl.classList.add('hidden');
68
+ var data = await decryptData(pwd);
69
+ if (!data) {
70
+ errEl.classList.remove('hidden');
71
+ return;
72
+ }
73
+ SCAN_DATA = data;
74
+ try { sessionStorage.setItem('trusera-pwd', pwd); } catch(e) {}
75
+ document.getElementById('login-screen').classList.add('hidden');
76
+ document.getElementById('dashboard').classList.remove('hidden');
77
+ renderDashboard();
78
+ }
79
+
80
+ async function trySessionRestore() {
81
+ var saved; try { saved = sessionStorage.getItem('trusera-pwd'); } catch(e) {}
82
+ if (saved) {
83
+ var data = await decryptData(saved);
84
+ if (data) {
85
+ SCAN_DATA = data;
86
+ document.getElementById('login-screen').classList.add('hidden');
87
+ document.getElementById('dashboard').classList.remove('hidden');
88
+ renderDashboard();
89
+ return;
90
+ }
91
+ }
92
+ document.getElementById('login-screen').classList.remove('hidden');
93
+ }
94
+ `;
95
+ passwordFormHtml = `
96
+ <div id="login-screen" class="login-screen hidden">
97
+ <div class="login-box">
98
+ <svg viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg" width="48" height="48" style="margin-bottom:16px">
99
+ <path d="M32 4L56 18V46L32 60L8 46V18L32 4Z" fill="#0F172A" stroke="#3B82F6" stroke-width="2"/>
100
+ <text x="32" y="40" text-anchor="middle" font-family="Arial,sans-serif" font-size="28" font-weight="bold" fill="#3B82F6">T</text>
101
+ </svg>
102
+ <h2>Trusera Dashboard</h2>
103
+ <p style="color:var(--text-dim);font-size:13px;margin-bottom:16px">Enter password to view scan results</p>
104
+ <form onsubmit="handleLogin(event)">
105
+ <input type="password" id="pwd-input" placeholder="Password" autofocus
106
+ style="width:100%;padding:10px 14px;border-radius:6px;border:1px solid var(--border);background:var(--bg);color:var(--text);font-size:14px;margin-bottom:10px">
107
+ <button type="submit" class="btn btn-primary" style="width:100%;padding:10px">Unlock</button>
108
+ </form>
109
+ <p id="pwd-error" class="hidden" style="color:var(--red);font-size:13px;margin-top:10px">Incorrect password. Please try again.</p>
110
+ </div>
111
+ </div>`;
112
+ }
113
+ else {
114
+ dataScript = `<script>var SCAN_DATA = ${jsonPayload};</script>`;
115
+ }
116
+ const dashboardHiddenClass = password ? ' hidden' : '';
117
+ return `<!DOCTYPE html>
118
+ <html lang="en">
119
+ <head>
120
+ <meta charset="UTF-8">
121
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
122
+ <title>Trusera AI-BOM Dashboard</title>
123
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"><\/script>
124
+ <style>
125
+ :root {
126
+ --bg: #0d1117;
127
+ --bg-card: #161b22;
128
+ --bg-hover: #1c2333;
129
+ --border: #30363d;
130
+ --text: #e6edf3;
131
+ --text-dim: #8b949e;
132
+ --accent: #58a6ff;
133
+ --green: #3fb950;
134
+ --red: #f85149;
135
+ --orange: #d29922;
136
+ --purple: #bc8cff;
137
+ }
138
+ [data-theme="light"] {
139
+ --bg: #ffffff;
140
+ --bg-card: #f6f8fa;
141
+ --bg-hover: #eaeef2;
142
+ --border: #d0d7de;
143
+ --text: #1f2328;
144
+ --text-dim: #656d76;
145
+ --accent: #0969da;
146
+ --green: #1a7f37;
147
+ --red: #cf222e;
148
+ --orange: #9a6700;
149
+ --purple: #8250df;
150
+ }
151
+ * { margin:0; padding:0; box-sizing:border-box; }
152
+ body {
153
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
154
+ background: var(--bg);
155
+ color: var(--text);
156
+ line-height: 1.5;
157
+ }
158
+ .header {
159
+ background: var(--bg-card);
160
+ border-bottom: 1px solid var(--border);
161
+ padding: 16px 24px;
162
+ display: flex;
163
+ align-items: center;
164
+ justify-content: space-between;
165
+ }
166
+ .header h1 { font-size: 20px; font-weight: 600; }
167
+ .header h1 span { color: var(--accent); }
168
+ .header-right { display:flex; align-items:center; gap:12px; }
169
+ .header .version { color: var(--text-dim); font-size: 13px; }
170
+ .container { max-width: 1280px; margin: 0 auto; padding: 24px; }
171
+ .card {
172
+ background: var(--bg-card);
173
+ border: 1px solid var(--border);
174
+ border-radius: 8px;
175
+ padding: 20px;
176
+ margin-bottom: 20px;
177
+ }
178
+ .card h2 { font-size: 16px; margin-bottom: 12px; color: var(--text-dim); font-weight: 500; }
179
+ .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; margin-bottom: 20px; }
180
+ .stat-card {
181
+ background: var(--bg-card);
182
+ border: 1px solid var(--border);
183
+ border-radius: 8px;
184
+ padding: 16px;
185
+ text-align: center;
186
+ }
187
+ .stat-card .value { font-size: 32px; font-weight: 700; }
188
+ .stat-card .label { color: var(--text-dim); font-size: 13px; margin-top: 4px; }
189
+ table { width: 100%; border-collapse: collapse; }
190
+ th, td { padding: 10px 14px; text-align: left; border-bottom: 1px solid var(--border); }
191
+ th { color: var(--text-dim); font-weight: 500; font-size: 13px; text-transform: uppercase; letter-spacing: 0.5px; }
192
+ tbody tr:hover { background: var(--bg-hover); }
193
+ tbody tr { cursor: pointer; }
194
+ .badge {
195
+ display: inline-block;
196
+ padding: 2px 8px;
197
+ border-radius: 12px;
198
+ font-size: 12px;
199
+ font-weight: 600;
200
+ }
201
+ .badge-critical { background: #f8514922; color: var(--red); border: 1px solid #f8514944; }
202
+ .badge-high { background: #d2992222; color: var(--orange); border: 1px solid #d2992244; }
203
+ .badge-medium { background: #58a6ff22; color: var(--accent); border: 1px solid #58a6ff44; }
204
+ .badge-low { background: #3fb95022; color: var(--green); border: 1px solid #3fb95044; }
205
+ .btn {
206
+ display: inline-block;
207
+ padding: 6px 14px;
208
+ border-radius: 6px;
209
+ border: 1px solid var(--border);
210
+ background: var(--bg-card);
211
+ color: var(--text);
212
+ cursor: pointer;
213
+ font-size: 13px;
214
+ transition: background 0.15s;
215
+ }
216
+ .btn:hover { background: var(--bg-hover); }
217
+ .btn-primary { background: var(--accent); color: #000; border-color: var(--accent); font-weight: 600; }
218
+ .btn-primary:hover { opacity: 0.9; }
219
+ .charts { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px; }
220
+ .chart-wrap { max-height: 300px; display: flex; justify-content: center; }
221
+ .filter-bar { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
222
+ .filter-bar input, .filter-bar select {
223
+ padding: 6px 10px;
224
+ border-radius: 6px;
225
+ border: 1px solid var(--border);
226
+ background: var(--bg);
227
+ color: var(--text);
228
+ font-size: 13px;
229
+ }
230
+ .modal-overlay {
231
+ position: fixed; top: 0; left: 0; width: 100%; height: 100%;
232
+ background: rgba(0,0,0,0.6); z-index: 1000; display: flex;
233
+ align-items: center; justify-content: center;
234
+ }
235
+ .modal {
236
+ background: var(--bg-card); border: 1px solid var(--border);
237
+ border-radius: 12px; padding: 24px; max-width: 720px; width: 90%;
238
+ max-height: 80vh; overflow-y: auto; position: relative;
239
+ }
240
+ .modal h3 { font-size: 18px; margin-bottom: 16px; }
241
+ .modal-close {
242
+ position: absolute; top: 12px; right: 16px; background: none;
243
+ border: none; color: var(--text-dim); font-size: 22px; cursor: pointer;
244
+ }
245
+ .modal-close:hover { color: var(--text); }
246
+ .modal-row { display: flex; padding: 8px 0; border-bottom: 1px solid var(--border); }
247
+ .modal-label { width: 140px; color: var(--text-dim); font-size: 13px; flex-shrink: 0; }
248
+ .modal-value { flex: 1; font-size: 14px; word-break: break-all; }
249
+ .theme-toggle {
250
+ background: none; border: 1px solid var(--border); border-radius: 6px;
251
+ color: var(--text); cursor: pointer; font-size: 18px; padding: 4px 10px;
252
+ transition: background 0.15s;
253
+ }
254
+ .theme-toggle:hover { background: var(--bg-hover); }
255
+ .login-screen {
256
+ position: fixed; top: 0; left: 0; width: 100%; height: 100%;
257
+ background: var(--bg); z-index: 2000; display: flex;
258
+ align-items: center; justify-content: center;
259
+ }
260
+ .login-box {
261
+ background: var(--bg-card); border: 1px solid var(--border);
262
+ border-radius: 12px; padding: 32px; max-width: 360px; width: 90%;
263
+ text-align: center;
264
+ }
265
+ .login-box h2 { margin-bottom: 4px; }
266
+ .export-bar { display: flex; gap: 8px; }
267
+ .flag-card {
268
+ border-left: 4px solid var(--border);
269
+ background: var(--bg);
270
+ border-radius: 0 8px 8px 0;
271
+ padding: 14px 16px;
272
+ margin-bottom: 10px;
273
+ }
274
+ .flag-card.severity-critical { border-left-color: var(--red); }
275
+ .flag-card.severity-high { border-left-color: var(--orange); }
276
+ .flag-card.severity-medium { border-left-color: var(--accent); }
277
+ .flag-card.severity-low { border-left-color: var(--green); }
278
+ .flag-card-header { display:flex; align-items:center; gap:8px; margin-bottom:8px; }
279
+ .flag-card-header code { font-size:13px; font-weight:600; }
280
+ .flag-card p { font-size:13px; color:var(--text-dim); margin:4px 0; line-height:1.5; }
281
+ .flag-card strong { color:var(--text); font-size:12px; text-transform:uppercase; letter-spacing:0.5px; }
282
+ .owasp-tag {
283
+ display:inline-block; padding:2px 8px; border-radius:4px;
284
+ font-size:11px; font-weight:600; background:var(--purple); color:#fff;
285
+ margin-top:6px;
286
+ }
287
+ .flag-section { margin-top:16px; }
288
+ .flag-section h4 { font-size:14px; margin-bottom:10px; color:var(--text-dim); }
289
+ .hidden { display: none !important; }
290
+ @media (max-width: 768px) {
291
+ .charts { grid-template-columns: 1fr; }
292
+ .stats { grid-template-columns: 1fr 1fr; }
293
+ }
294
+ </style>
295
+ </head>
296
+ <body>
297
+
298
+ ${dataScript}
299
+ ${remediationScript}
300
+ ${passwordFormHtml}
301
+
302
+ <div id="dashboard" class="${dashboardHiddenClass}">
303
+ <div class="header">
304
+ <h1><span>Trusera</span> AI-BOM Dashboard</h1>
305
+ <div class="header-right">
306
+ <div class="version" id="app-version"></div>
307
+ <button class="theme-toggle" id="theme-toggle" title="Toggle theme">&#9790;</button>
308
+ </div>
309
+ </div>
310
+ <div id="modal-container"></div>
311
+
312
+ <div class="container">
313
+ <div class="stats" id="stat-cards"></div>
314
+ <div class="charts">
315
+ <div class="card"><h2>Severity Distribution</h2><div class="chart-wrap"><canvas id="chart-severity"></canvas></div></div>
316
+ <div class="card"><h2>Component Types</h2><div class="chart-wrap"><canvas id="chart-types"></canvas></div></div>
317
+ </div>
318
+ <div class="card">
319
+ <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;flex-wrap:wrap;gap:8px">
320
+ <h2 style="margin-bottom:0">Findings</h2>
321
+ <div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
322
+ <div class="filter-bar">
323
+ <input type="text" id="filter-search" placeholder="Search components...">
324
+ <select id="filter-severity"><option value="">All Severities</option><option value="critical">Critical</option><option value="high">High</option><option value="medium">Medium</option><option value="low">Low</option></select>
325
+ <select id="filter-type"><option value="">All Types</option></select>
326
+ </div>
327
+ <div class="export-bar">
328
+ <button class="btn" onclick="exportCSV()">CSV</button>
329
+ <button class="btn" onclick="exportJSON()">JSON</button>
330
+ </div>
331
+ </div>
332
+ </div>
333
+ <div id="component-table"></div>
334
+ </div>
335
+ </div>
336
+ </div>
337
+
338
+ <script>
339
+ function esc(s) {
340
+ if (s == null) return '';
341
+ var d = document.createElement('div');
342
+ d.appendChild(document.createTextNode(String(s)));
343
+ return d.innerHTML;
344
+ }
345
+
346
+ function severityBadge(sev) {
347
+ return '<span class="badge badge-' + esc(sev) + '">' + esc(sev).toUpperCase() + '</span>';
348
+ }
349
+
350
+ function statCard(value, label, color) {
351
+ return '<div class="stat-card"><div class="value" style="color:' + color + '">' + esc(value) + '</div><div class="label">' + esc(label) + '</div></div>';
352
+ }
353
+
354
+ var chartSeverity = null;
355
+ var chartTypes = null;
356
+ var filteredComponents = [];
357
+
358
+ ${decryptionScript}
359
+
360
+ function renderDashboard() {
361
+ if (!SCAN_DATA) return;
362
+ var data = SCAN_DATA;
363
+ document.getElementById('app-version').textContent = 'v' + (data.aiBomVersion || '0.1.0');
364
+
365
+ var s = data.summary || {};
366
+ var hr = s.highestRiskScore || 0;
367
+ document.getElementById('stat-cards').innerHTML = [
368
+ statCard(s.totalComponents || 0, 'Components', 'var(--accent)'),
369
+ statCard(s.totalFilesScanned || 0, 'Workflows Scanned', 'var(--purple)'),
370
+ statCard(hr, 'Highest Risk Score', hr >= 70 ? 'var(--red)' : hr >= 40 ? 'var(--orange)' : 'var(--green)'),
371
+ statCard((s.scanDurationSeconds || 0).toFixed(2) + 's', 'Scan Duration', 'var(--text-dim)'),
372
+ ].join('');
373
+
374
+ renderCharts(data);
375
+ populateTypeFilter(data);
376
+ renderComponents(data);
377
+
378
+ document.getElementById('filter-search').addEventListener('input', function() { renderComponents(data); });
379
+ document.getElementById('filter-severity').addEventListener('change', function() { renderComponents(data); });
380
+ document.getElementById('filter-type').addEventListener('change', function() { renderComponents(data); });
381
+ }
382
+
383
+ function renderCharts(data) {
384
+ var s = data.summary || {};
385
+ if (chartSeverity) chartSeverity.destroy();
386
+ var sev = s.bySeverity || {};
387
+ chartSeverity = new Chart(document.getElementById('chart-severity').getContext('2d'), {
388
+ type: 'doughnut',
389
+ data: {
390
+ labels: ['Critical','High','Medium','Low'],
391
+ datasets: [{
392
+ data: [sev.critical||0, sev.high||0, sev.medium||0, sev.low||0],
393
+ backgroundColor: ['#f85149','#d29922','#58a6ff','#3fb950'],
394
+ borderColor: '#161b22',
395
+ borderWidth: 2
396
+ }]
397
+ },
398
+ options: {
399
+ responsive: true,
400
+ maintainAspectRatio: true,
401
+ plugins: { legend: { position: 'bottom', labels: { color: '#8b949e' } } }
402
+ }
403
+ });
404
+
405
+ if (chartTypes) chartTypes.destroy();
406
+ var byType = s.byType || {};
407
+ var typeLabels = Object.keys(byType);
408
+ var typeValues = Object.values(byType);
409
+ chartTypes = new Chart(document.getElementById('chart-types').getContext('2d'), {
410
+ type: 'bar',
411
+ data: {
412
+ labels: typeLabels.map(function(l) { return l.replace(/_/g,' '); }),
413
+ datasets: [{
414
+ label: 'Count',
415
+ data: typeValues,
416
+ backgroundColor: '#58a6ff',
417
+ borderRadius: 4
418
+ }]
419
+ },
420
+ options: {
421
+ responsive: true,
422
+ maintainAspectRatio: true,
423
+ scales: {
424
+ x: { ticks: { color: '#8b949e' }, grid: { color: '#30363d' } },
425
+ y: { ticks: { color: '#8b949e', stepSize: 1 }, grid: { color: '#30363d' }, beginAtZero: true }
426
+ },
427
+ plugins: { legend: { display: false } }
428
+ }
429
+ });
430
+ }
431
+
432
+ function populateTypeFilter(data) {
433
+ var sel = document.getElementById('filter-type');
434
+ sel.innerHTML = '<option value="">All Types</option>';
435
+ var types = new Set((data.components || []).map(function(c) { return c.type; }));
436
+ types.forEach(function(t) {
437
+ var opt = document.createElement('option');
438
+ opt.value = t;
439
+ opt.textContent = t.replace(/_/g, ' ');
440
+ sel.appendChild(opt);
441
+ });
442
+ }
443
+
444
+ function renderComponents(data) {
445
+ var search = (document.getElementById('filter-search').value || '').toLowerCase();
446
+ var sevFilter = document.getElementById('filter-severity').value;
447
+ var typeFilter = document.getElementById('filter-type').value;
448
+ var comps = (data.components || []).slice();
449
+ if (search) comps = comps.filter(function(c) {
450
+ return c.name.toLowerCase().indexOf(search) !== -1 || (c.provider||'').toLowerCase().indexOf(search) !== -1;
451
+ });
452
+ if (sevFilter) comps = comps.filter(function(c) { return c.risk && c.risk.severity === sevFilter; });
453
+ if (typeFilter) comps = comps.filter(function(c) { return c.type === typeFilter; });
454
+ comps.sort(function(a, b) { return (b.risk ? b.risk.score : 0) - (a.risk ? a.risk.score : 0); });
455
+ filteredComponents = comps;
456
+
457
+ var el = document.getElementById('component-table');
458
+ if (comps.length === 0) {
459
+ el.innerHTML = '<p style="color:var(--text-dim);padding:10px">No components match the current filters.</p>';
460
+ return;
461
+ }
462
+ var html = '<table><thead><tr><th>Name</th><th>Type</th><th>Provider</th><th>Severity</th><th>Risk Score</th><th>Workflow</th></tr></thead><tbody>';
463
+ comps.forEach(function(c, idx) {
464
+ var sev = (c.risk && c.risk.severity) || 'low';
465
+ var score = (c.risk && c.risk.score) || 0;
466
+ var fp = (c.location && c.location.filePath) || '';
467
+ html += '<tr onclick="showComponentModal(' + idx + ')">';
468
+ html += '<td><strong>' + esc(c.name) + '</strong></td>';
469
+ html += '<td>' + esc((c.type||'').replace(/_/g,' ')) + '</td>';
470
+ html += '<td>' + esc(c.provider||'-') + '</td>';
471
+ html += '<td>' + severityBadge(sev) + '</td>';
472
+ html += '<td>' + esc(score) + '</td>';
473
+ html += '<td title="' + esc(fp) + '">' + esc(fp) + '</td>';
474
+ html += '</tr>';
475
+ });
476
+ html += '</tbody></table>';
477
+ el.innerHTML = html;
478
+ }
479
+
480
+ function showComponentModal(idx) {
481
+ var c = filteredComponents[idx];
482
+ if (!c) return;
483
+ var sev = (c.risk && c.risk.severity) || 'low';
484
+ var score = (c.risk && c.risk.score) || 0;
485
+ var fp = (c.location && c.location.filePath) || '-';
486
+ var owaspCats = (c.risk && c.risk.owaspCategories && c.risk.owaspCategories.length > 0)
487
+ ? c.risk.owaspCategories.map(function(cat) { return '<span class="owasp-tag">' + esc(cat) + '</span>'; }).join(' ')
488
+ : '<span style="color:var(--text-dim)">None</span>';
489
+
490
+ var flagsHtml = '';
491
+ var flags = c.flags || [];
492
+ if (flags.length > 0) {
493
+ flagsHtml = '<div class="flag-section"><h4>Risk Findings & Remediation</h4>';
494
+ flags.forEach(function(flag) {
495
+ var entry = REMEDIATION_MAP[flag];
496
+ var flagSev = entry ? entry.severity : 'low';
497
+ flagsHtml += '<div class="flag-card severity-' + esc(flagSev) + '">';
498
+ flagsHtml += '<div class="flag-card-header"><code>' + esc(flag) + '</code>' + severityBadge(flagSev) + '</div>';
499
+ if (entry) {
500
+ flagsHtml += '<p>' + esc(entry.description) + '</p>';
501
+ flagsHtml += '<strong>Remediation</strong><p>' + esc(entry.remediation) + '</p>';
502
+ flagsHtml += '<strong>Guardrail</strong><p>' + esc(entry.guardrail) + '</p>';
503
+ flagsHtml += '<span class="owasp-tag">' + esc(entry.owaspCategory + ': ' + entry.owaspCategoryName) + '</span>';
504
+ } else {
505
+ flagsHtml += '<p>' + esc(flag.replace(/_/g, ' ')) + '</p>';
506
+ }
507
+ flagsHtml += '</div>';
508
+ });
509
+ flagsHtml += '</div>';
510
+ } else {
511
+ flagsHtml = '<div class="flag-section"><h4>Risk Findings & Remediation</h4><p style="color:var(--text-dim)">No risk flags detected for this component.</p></div>';
512
+ }
513
+
514
+ var el = document.getElementById('modal-container');
515
+ el.innerHTML = '<div class="modal-overlay" onclick="closeModal(event)">' +
516
+ '<div class="modal" onclick="event.stopPropagation()">' +
517
+ '<button class="modal-close" onclick="closeModal()">&times;</button>' +
518
+ '<h3>' + esc(c.name) + '</h3>' +
519
+ modalRow('Type', (c.type || '').replace(/_/g, ' ')) +
520
+ modalRow('Provider', c.provider || '-') +
521
+ modalRow('Model', c.modelName || '-') +
522
+ modalRow('Version', c.version || '-') +
523
+ modalRow('Severity', severityBadge(sev)) +
524
+ modalRow('Risk Score', String(score)) +
525
+ modalRow('Workflow', fp) +
526
+ modalRow('OWASP Categories', owaspCats) +
527
+ modalRow('Source', c.source || '-') +
528
+ flagsHtml +
529
+ '</div></div>';
530
+ }
531
+
532
+ function modalRow(label, value) {
533
+ return '<div class="modal-row"><div class="modal-label">' + esc(label) + '</div><div class="modal-value">' + (typeof value === 'string' && value.indexOf('<') !== -1 ? value : esc(value)) + '</div></div>';
534
+ }
535
+
536
+ function closeModal(event) {
537
+ if (event && event.target && !event.target.classList.contains('modal-overlay')) return;
538
+ document.getElementById('modal-container').innerHTML = '';
539
+ }
540
+ document.addEventListener('keydown', function(e) {
541
+ if (e.key === 'Escape') document.getElementById('modal-container').innerHTML = '';
542
+ });
543
+
544
+ function downloadBlob(content, filename, mimeType) {
545
+ var blob = new Blob([content], { type: mimeType });
546
+ var url = URL.createObjectURL(blob);
547
+ var a = document.createElement('a');
548
+ a.href = url; a.download = filename;
549
+ document.body.appendChild(a); a.click();
550
+ document.body.removeChild(a);
551
+ URL.revokeObjectURL(url);
552
+ }
553
+
554
+ function exportCSV() {
555
+ if (!SCAN_DATA || !SCAN_DATA.components) return;
556
+ var headers = ['Name','Type','Provider','Version','Severity','Risk Score','Workflow','Flags','OWASP Categories','Source'];
557
+ var rows = SCAN_DATA.components.map(function(c) {
558
+ return [
559
+ c.name || '', (c.type || '').replace(/_/g,' '), c.provider || '',
560
+ c.version || '', (c.risk && c.risk.severity) || '', (c.risk && c.risk.score) || 0,
561
+ (c.location && c.location.filePath) || '', (c.flags || []).join('; '),
562
+ (c.risk && c.risk.owaspCategories) ? c.risk.owaspCategories.join('; ') : '', c.source || ''
563
+ ];
564
+ });
565
+ var csv = [headers].concat(rows).map(function(r) {
566
+ return r.map(function(v) { return '"' + String(v).replace(/"/g, '""') + '"'; }).join(',');
567
+ }).join('\\n');
568
+ downloadBlob(csv, 'trusera-scan.csv', 'text/csv');
569
+ }
570
+
571
+ function exportJSON() {
572
+ if (!SCAN_DATA) return;
573
+ downloadBlob(JSON.stringify(SCAN_DATA, null, 2), 'trusera-scan.json', 'application/json');
574
+ }
575
+
576
+ function safeStorage(method, key, val) {
577
+ try { return method === 'get' ? localStorage.getItem(key) : localStorage.setItem(key, val); } catch(e) { return null; }
578
+ }
579
+ function initTheme() {
580
+ var saved = safeStorage('get', 'trusera-theme');
581
+ if (saved === 'light') {
582
+ document.documentElement.setAttribute('data-theme', 'light');
583
+ document.getElementById('theme-toggle').innerHTML = '&#9728;';
584
+ }
585
+ }
586
+ document.getElementById('theme-toggle').addEventListener('click', function() {
587
+ var isLight = document.documentElement.getAttribute('data-theme') === 'light';
588
+ if (isLight) {
589
+ document.documentElement.removeAttribute('data-theme');
590
+ this.innerHTML = '&#9790;';
591
+ safeStorage('set', 'trusera-theme', 'dark');
592
+ } else {
593
+ document.documentElement.setAttribute('data-theme', 'light');
594
+ this.innerHTML = '&#9728;';
595
+ safeStorage('set', 'trusera-theme', 'light');
596
+ }
597
+ });
598
+
599
+ initTheme();
600
+ ${password ? 'trySessionRestore();' : 'renderDashboard();'}
601
+ <\/script>
602
+ </body>
603
+ </html>`;
604
+ }
605
+ //# sourceMappingURL=dashboardHtml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboardHtml.js","sourceRoot":"","sources":["../../lib/dashboardHtml.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAWH,sDAslBC;AA/lBD,mCAAiE;AAEjE,qCAA2C;AAE3C;;;;GAIG;AACH,SAAgB,qBAAqB,CACnC,UAAsB,EACtB,QAAiB;IAEjB,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IAE/C,IAAI,UAAkB,CAAC;IACvB,IAAI,gBAAgB,GAAG,EAAE,CAAC;IAC1B,IAAI,gBAAgB,GAAG,EAAE,CAAC;IAE1B,MAAM,iBAAiB,GAAG,iCAAiC,IAAI,CAAC,SAAS,CAAC,wBAAe,CAAC,YAAY,CAAC;IAEvG,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,IAAI,GAAG,IAAA,oBAAW,EAAC,EAAE,CAAC,CAAC;QAC7B,MAAM,EAAE,GAAG,IAAA,oBAAW,EAAC,EAAE,CAAC,CAAC;QAC3B,MAAM,GAAG,GAAG,IAAA,mBAAU,EAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;QAC9D,MAAM,MAAM,GAAG,IAAA,uBAAc,EAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QACtD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC;YAC9B,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC;YAClC,MAAM,CAAC,KAAK,EAAE;SACf,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;QAEjD,UAAU,GAAG;wBACO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;cACjC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACzB,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC;;UAEvB,CAAC;QAEP,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuDtB,CAAC;QAEE,gBAAgB,GAAG;;;;;;;;;;;;;;;;OAgBhB,CAAC;IACN,CAAC;SAAM,CAAC;QACN,UAAU,GAAG,2BAA2B,WAAW,YAAY,CAAC;IAClE,CAAC;IAED,MAAM,oBAAoB,GAAG,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IAEvD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqLP,UAAU;EACV,iBAAiB;EACjB,gBAAgB;;6BAEW,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwD/C,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkPhB,QAAQ,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,oBAAoB;;;QAGlD,CAAC;AACT,CAAC"}