opena2a-cli 0.5.12 → 0.6.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.
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Generates a self-contained HTML file with:
6
6
  * - Dark theme (#0f172a bg, #1e293b cards, teal primary)
7
- * - 6-tab navigation (Overview, Credentials, Hygiene, Integrity, Shield, HMA)
7
+ * - 7-tab navigation (Overview, Credentials, Hygiene, Integrity, Shield, HMA, Shadow AI)
8
8
  * - Composite score gauge with recovery summary
9
9
  * - Phase status cards with timing
10
10
  * - Cross-tab navigation
@@ -29,24 +29,7 @@ function generateReviewHtml(report) {
29
29
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
30
30
  <title>OpenA2A Security Review - ${esc(report.projectName ?? 'Project')}</title>
31
31
  <style>
32
- :root {
33
- --bg: #0f172a;
34
- --card: #1e293b;
35
- --card-border: #334155;
36
- --primary: #06b6d4;
37
- --text: #e2e8f0;
38
- --muted: #94a3b8;
39
- --dim: #64748b;
40
- --critical: #ef4444;
41
- --high: #f97316;
42
- --medium: #eab308;
43
- --low: #3b82f6;
44
- --info: #6b7280;
45
- --green: #22c55e;
46
- --red: #ef4444;
47
- --amber: #f59e0b;
48
- --font: 'JetBrains Mono','Fira Code','SF Mono',Menlo,Consolas,monospace;
49
- }
32
+ :root{--bg:#0f172a;--card:#1e293b;--card-border:#334155;--primary:#06b6d4;--text:#e2e8f0;--muted:#94a3b8;--dim:#64748b;--critical:#ef4444;--high:#f97316;--medium:#eab308;--low:#3b82f6;--info:#6b7280;--green:#22c55e;--red:#ef4444;--amber:#f59e0b;--font:'JetBrains Mono','Fira Code','SF Mono',Menlo,Consolas,monospace;}
50
33
  *{margin:0;padding:0;box-sizing:border-box;}
51
34
  body{background:var(--bg);color:var(--text);font-family:var(--font);font-size:14px;line-height:1.6;}
52
35
  .container{max-width:1200px;margin:0 auto;padding:16px 20px;}
@@ -57,8 +40,7 @@ body{background:var(--bg);color:var(--text);font-family:var(--font);font-size:14
57
40
  .nav-tab{background:none;border:none;color:var(--dim);font-family:var(--font);font-size:13px;padding:8px 14px;cursor:pointer;border-bottom:2px solid transparent;transition:all 0.15s;}
58
41
  .nav-tab:hover{color:var(--text);}
59
42
  .nav-tab.active{color:var(--primary);border-bottom-color:var(--primary);}
60
- .page{display:none;}
61
- .page.active{display:block;}
43
+ .page{display:none;}.page.active{display:block;}
62
44
  .card{background:var(--card);border:1px solid var(--card-border);border-radius:6px;padding:16px;margin-bottom:12px;}
63
45
  .card-title{font-size:14px;font-weight:700;color:var(--text);margin-bottom:10px;}
64
46
  .stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px;margin-bottom:16px;}
@@ -72,7 +54,7 @@ body{background:var(--bg);color:var(--text);font-family:var(--font);font-size:14
72
54
  .score-banner-track{height:6px;background:rgba(255,255,255,0.06);border-radius:3px;overflow:hidden;}
73
55
  .score-banner-fill{height:100%;border-radius:3px;transition:width 0.3s;}
74
56
  .score-banner-label{font-size:12px;color:var(--dim);display:flex;justify-content:space-between;}
75
- .phase-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:8px;margin-bottom:16px;}
57
+ .phase-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:8px;margin-bottom:16px;}
76
58
  .phase-card{background:var(--card);border:1px solid var(--card-border);border-radius:6px;padding:12px 14px;}
77
59
  .phase-name{font-size:13px;font-weight:600;margin-bottom:4px;}
78
60
  .phase-detail{font-size:12px;color:var(--muted);}
@@ -148,7 +130,7 @@ body{background:var(--bg);color:var(--text);font-family:var(--font);font-size:14
148
130
  <div class="header">
149
131
  <div>
150
132
  <div class="header-title">OpenA2A Security Review</div>
151
- <div class="header-subtitle">Security review of <strong style="color:var(--text)">${esc(report.projectName ?? report.directory)}</strong> -- composite score from 5 automated checks. Higher is better.</div>
133
+ <div class="header-subtitle">Security review of <strong style="color:var(--text)">${esc(report.projectName ?? report.directory)}</strong> -- composite score from 6 automated checks. Higher is better.</div>
152
134
  </div>
153
135
  <div class="header-meta">${esc(report.timestamp)}</div>
154
136
  </div>
@@ -159,6 +141,7 @@ body{background:var(--bg);color:var(--text);font-family:var(--font);font-size:14
159
141
  <button class="nav-tab" data-page="integrity">Integrity</button>
160
142
  <button class="nav-tab" data-page="shield">Shield</button>
161
143
  <button class="nav-tab" data-page="hma">HMA</button>
144
+ <button class="nav-tab" data-page="shadowai">Shadow AI</button>
162
145
  </nav>
163
146
  <div class="page active" id="page-overview"></div>
164
147
  <div class="page" id="page-credentials"></div>
@@ -166,427 +149,12 @@ body{background:var(--bg);color:var(--text);font-family:var(--font);font-size:14
166
149
  <div class="page" id="page-integrity"></div>
167
150
  <div class="page" id="page-shield"></div>
168
151
  <div class="page" id="page-hma"></div>
152
+ <div class="page" id="page-shadowai"></div>
169
153
  <div class="footer">Generated by <a href="https://opena2a.org" target="_blank" rel="noopener">OpenA2A</a> -- Open Agent-to-Agent Security Platform</div>
170
154
  </div>
171
-
172
155
  <script id="report-data" type="application/json">${jsonData}</script>
173
156
  <script>
174
- (function(){
175
- var report = JSON.parse(document.getElementById('report-data').textContent);
176
- var pagesRendered = {};
177
-
178
- function esc(s){return s==null?'':String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}
179
- function scoreColor(s){return s>=90?'var(--green)':s>=70?'var(--primary)':s>=50?'var(--medium)':'var(--red)';}
180
-
181
- // Tab navigation
182
- document.getElementById('main-nav').addEventListener('click',function(e){
183
- var btn=e.target.closest('.nav-tab');if(!btn)return;
184
- var pg=btn.getAttribute('data-page');
185
- document.querySelectorAll('.nav-tab').forEach(function(t){t.classList.toggle('active',t===btn);});
186
- document.querySelectorAll('.page').forEach(function(p){p.classList.toggle('active',p.id==='page-'+pg);});
187
- renderPage(pg);
188
- });
189
-
190
- function renderPage(pg){
191
- if(pagesRendered[pg])return;pagesRendered[pg]=true;
192
- var el=document.getElementById('page-'+pg);
193
- switch(pg){
194
- case 'overview':el.innerHTML=renderOverview();break;
195
- case 'credentials':el.innerHTML=renderCredentials();break;
196
- case 'hygiene':el.innerHTML=renderHygiene();break;
197
- case 'integrity':el.innerHTML=renderIntegrity();break;
198
- case 'shield':el.innerHTML=renderShield();break;
199
- case 'hma':el.innerHTML=renderHma();break;
200
- }
201
- }
202
-
203
- // Copy command
204
- window.copyCmd=function(btn){
205
- var cmd=btn.getAttribute('data-cmd');
206
- if(navigator.clipboard&&navigator.clipboard.writeText){
207
- navigator.clipboard.writeText(cmd).then(function(){
208
- btn.textContent='OK';btn.classList.add('copied');
209
- setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},1500);
210
- });
211
- }else{
212
- var ta=document.createElement('textarea');ta.value=cmd;ta.style.position='fixed';ta.style.left='-9999px';
213
- document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);
214
- btn.textContent='OK';btn.classList.add('copied');
215
- setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},1500);
216
- }
217
- };
218
-
219
- // Cross-tab navigation
220
- window.goToTab=function(tab){
221
- var btn=document.querySelector('.nav-tab[data-page="'+tab+'"]');
222
- if(btn)btn.click();
223
- };
224
-
225
- // Gauge SVG
226
- function gaugeCircle(score,label){
227
- var sz=170,cx=sz/2,cy=sz/2,r=65,sw=10,circ=2*Math.PI*r;
228
- var pct=Math.max(0,Math.min(100,score))/100,dash=pct*circ,gap=circ-dash;
229
- var clr=score>=90?'#22c55e':score>=70?'#06b6d4':score>=50?'#eab308':'#ef4444';
230
- var s='<svg width="'+sz+'" height="'+sz+'" viewBox="0 0 '+sz+' '+sz+'">';
231
- s+='<circle cx="'+cx+'" cy="'+cy+'" r="'+r+'" fill="none" stroke="rgba(255,255,255,0.05)" stroke-width="'+sw+'"/>';
232
- 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+')"/>';
233
- 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>';
234
- 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)">'+esc(label||('out of 100'))+'</text>';
235
- s+='</svg>';return s;
236
- }
237
-
238
- function cmdBlock(cmd){
239
- return '<div class="cmd-block"><span class="cmd-text">'+esc(cmd)+'</span><button class="copy-btn" data-cmd="'+esc(cmd)+'" onclick="copyCmd(this)">Copy</button></div>';
240
- }
241
-
242
- function statCard(value,label,color){
243
- return '<div class="stat-card"><div class="stat-value" style="color:'+color+'">'+esc(String(value))+'</div><div class="stat-label">'+esc(label)+'</div></div>';
244
- }
245
-
246
- function scoreBanner(score,recoverySummary){
247
- var clr=scoreColor(score);
248
- var h='<div class="score-banner">';
249
- h+='<div class="score-banner-num" style="color:'+clr+'">'+score+'</div>';
250
- h+='<div class="score-banner-bar">';
251
- h+='<div class="score-banner-label"><span>Composite Score</span><span>'+score+'/100</span></div>';
252
- h+='<div class="score-banner-track"><div class="score-banner-fill" style="width:'+score+'%;background:'+clr+'"></div></div>';
253
- h+='</div>';
254
- // Recovery-framed: show path forward instead of letter grade
255
- if(recoverySummary && recoverySummary.totalRecoverable>0){
256
- var recovBg=score>=70?'rgba(6,182,212,0.15)':score>=50?'rgba(234,179,8,0.15)':'rgba(239,68,68,0.15)';
257
- h+='<div class="score-banner-grade" style="color:'+clr+';background:'+recovBg+'">+'+recoverySummary.totalRecoverable+' recoverable</div>';
258
- }
259
- h+='</div>';
260
- return h;
261
- }
262
-
263
- var phaseDescriptions={
264
- 'Project Scan':'Checks .gitignore, lock files, security config, and dependency advisories',
265
- 'Credentials':'Scans source files for hardcoded API keys, tokens, and secrets',
266
- 'Config Integrity':'Verifies cryptographic signatures on monitored config files',
267
- 'Shield Analysis':'Analyzes 7 days of security events, policy violations, and ARP detections',
268
- 'HMA Scan':'Runs HackMyAgent security checks against your AI agent endpoints'
269
- };
270
- function phaseCard(phase){
271
- var statusCls='status-'+phase.status;
272
- var time=phase.status==='skip'?'--':(phase.durationMs/1000).toFixed(1)+'s';
273
- var desc=phaseDescriptions[phase.name]||'';
274
- return '<div class="phase-card"><div style="display:flex;justify-content:space-between;align-items:center"><div class="phase-name">'+esc(phase.name)+'</div><span class="status-badge '+statusCls+'">'+esc(phase.status)+'</span></div><div class="phase-detail">'+esc(phase.detail)+'</div>'+(desc?'<div class="phase-desc">'+esc(desc)+'</div>':'')+'<div class="phase-time">'+esc(time)+'</div></div>';
275
- }
276
-
277
- // ======================== OVERVIEW ========================
278
- function renderOverview(){
279
- var h='';
280
- // Score banner
281
- var sevCounts={critical:0,high:0,medium:0,low:0};
282
- var findings=report.findings||[];
283
- for(var i=0;i<findings.length;i++){var s=findings[i].severity;if(s in sevCounts)sevCounts[s]++;}
284
- h+=scoreBanner(report.compositeScore,report.recoverySummary);
285
- h+='<div class="stats-grid">';
286
- h+=statCard(findings.length,'Findings',findings.length>0?'var(--amber)':'var(--green)');
287
- h+=statCard(sevCounts.critical,'Critical',sevCounts.critical>0?'var(--critical)':'var(--text)');
288
- h+=statCard(sevCounts.high,'High',sevCounts.high>0?'var(--high)':'var(--text)');
289
- h+=statCard(sevCounts.medium,'Medium',sevCounts.medium>0?'var(--medium)':'var(--text)');
290
- h+='</div>';
291
-
292
- // Phase cards (full width, 5 columns)
293
- h+='<h2 class="section-title">Phase Results</h2><div class="phase-grid">';
294
- var phases=report.phases||[];
295
- for(var i=0;i<phases.length;i++) h+=phaseCard(phases[i]);
296
- h+='</div>';
297
-
298
- // Score breakdown -- show actual score, weight, weighted contribution per dimension
299
- var dims=[
300
- {name:'Hygiene',weight:35,score:report.initData.trustScore,tab:'hygiene'},
301
- {name:'Shield',weight:25,score:report.shieldData.postureScore,tab:'shield'},
302
- {name:'Credentials',weight:22,score:phases.length>1?phases[1].score:0,tab:'credentials'},
303
- {name:'Integrity',weight:18,score:phases.length>2?phases[2].score:0,tab:'integrity'}
304
- ];
305
- h+='<div class="score-explainer">';
306
- h+='<div style="font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:12px">Score Breakdown</div>';
307
- h+='<div style="display:flex;flex-direction:column;gap:10px">';
308
- for(var i=0;i<dims.length;i++){
309
- var d=dims[i];
310
- var weighted=Math.round(d.score*d.weight/100);
311
- var clr=scoreColor(d.score);
312
- h+='<div style="display:grid;grid-template-columns:100px 1fr 60px 50px;align-items:center;gap:10px;cursor:pointer" onclick="goToTab(&quot;'+d.tab+'&quot;)">';
313
- h+='<div style="display:flex;align-items:baseline;gap:6px"><span style="font-size:13px;color:var(--muted)">'+esc(d.name)+'</span></div>';
314
- h+='<div style="position:relative;height:8px;background:rgba(255,255,255,0.06);border-radius:4px;overflow:hidden"><div style="position:absolute;left:0;top:0;height:100%;width:'+d.score+'%;background:'+clr+';border-radius:4px;transition:width 0.3s"></div></div>';
315
- h+='<div style="text-align:right;font-size:14px;font-weight:700;color:'+clr+'">'+d.score+'<span style="font-size:11px;color:var(--dim);font-weight:400">/100</span></div>';
316
- h+='<div style="text-align:right;font-size:11px;color:var(--dim)">x '+d.weight+'%</div>';
317
- h+='</div>';
318
- }
319
- h+='</div>';
320
- h+='<div style="display:flex;justify-content:space-between;align-items:center;border-top:1px solid rgba(51,65,85,0.4);margin-top:12px;padding-top:8px">';
321
- h+='<div style="display:flex;gap:12px;font-size:12px;color:var(--dim)">';
322
- h+='<span><strong style="color:var(--green)">A</strong> 90+</span>';
323
- h+='<span><strong style="color:var(--primary)">B</strong> 80+</span>';
324
- h+='<span><strong style="color:var(--medium)">C</strong> 70+</span>';
325
- h+='<span><strong style="color:var(--high)">D</strong> 60+</span>';
326
- h+='<span><strong style="color:var(--red)">F</strong> &lt;60</span>';
327
- h+='</div>';
328
- h+='<div style="font-size:12px;color:var(--dim)">Click a row to view details</div>';
329
- h+='</div>';
330
- h+='</div>';
331
-
332
- // Action items
333
- var actions=report.actionItems||[];
334
- var actionImpact={
335
- 'critical':'Immediate risk of credential compromise or data breach',
336
- 'high':'Significant security gap that attackers can exploit',
337
- 'medium':'Moderate risk that weakens your security posture',
338
- 'low':'Minor improvement to harden your defenses',
339
- 'info':'Recommended best practice'
340
- };
341
- if(actions.length>0){
342
- h+='<h2 class="section-title">Action Items</h2><div class="card">';
343
- for(var i=0;i<actions.length;i++){
344
- var a=actions[i];
345
- var impact=actionImpact[a.severity]||'';
346
- h+='<div class="action-item"><div class="action-priority">#'+a.priority+'</div><div class="action-content"><div class="action-desc"><span class="sev-badge sev-'+esc(a.severity)+'">'+esc(a.severity)+'</span> '+esc(a.description)+'</div>'+(impact?'<div class="check-desc">'+esc(impact)+'</div>':'')+cmdBlock(a.command)+'<span class="action-link" onclick="goToTab(&quot;'+esc(a.tab)+'&quot;)">View details</span></div></div>';
347
- }
348
- h+='</div>';
349
- }
350
-
351
- // Top findings
352
- if(findings.length>0){
353
- h+='<h2 class="section-title">Findings</h2><div class="card"><table class="data-table"><thead><tr><th>ID</th><th>Title</th><th>Severity</th><th>Source</th><th>Detail</th></tr></thead><tbody>';
354
- var top=findings.slice(0,10);
355
- for(var i=0;i<top.length;i++){var f=top[i];
356
- h+='<tr><td>'+esc(f.id)+'</td><td>'+esc(f.title)+'</td><td><span class="sev-badge sev-'+esc(f.severity)+'">'+esc(f.severity)+'</span></td><td>'+esc(f.source)+'</td><td style="font-size:11px;color:var(--muted)">'+esc(f.detail)+'</td></tr>';
357
- }
358
- h+='</tbody></table></div>';
359
- }
360
- return h;
361
- }
362
-
363
- // ======================== CREDENTIALS ========================
364
- function renderCredentials(){
365
- var data=report.credentialData;
366
- if(!data||data.totalFindings===0){
367
- return '<div class="card"><div class="empty-state">No hardcoded credentials found. Your project is clean.</div></div>';
368
- }
369
- var h='<div class="section-intro">Hardcoded credentials in source code are the #1 cause of security breaches in AI projects. Keys pushed to git are scraped by bots within minutes. Each finding below is a credential that should be moved to environment variables.</div>';
370
- h+='<div class="stats-grid">';
371
- h+=statCard(data.totalFindings,'Total Findings',data.totalFindings>0?'var(--red)':'var(--green)');
372
- h+=statCard(data.bySeverity.critical||0,'Critical',(data.bySeverity.critical||0)>0?'var(--critical)':'var(--text)');
373
- h+=statCard(data.bySeverity.high||0,'High',(data.bySeverity.high||0)>0?'var(--high)':'var(--text)');
374
- h+=statCard(data.bySeverity.medium||0,'Medium',(data.bySeverity.medium||0)>0?'var(--medium)':'var(--text)');
375
- h+='</div>';
376
-
377
- h+='<h2 class="section-title">Credential Findings</h2>';
378
- var matches=data.matches||[];
379
- for(var i=0;i<matches.length;i++){
380
- var m=matches[i];
381
- h+='<div class="cred-card">';
382
- h+='<div class="cred-card-header"><span class="sev-badge sev-'+esc(m.severity)+'">'+esc(m.severity)+'</span><span class="cred-card-title">'+esc(m.title)+'</span><span style="color:var(--dim);font-size:12px">'+esc(m.findingId)+'</span></div>';
383
- h+='<div class="cred-card-meta">';
384
- h+='<div><div class="cred-card-meta-label">File</div><div class="cred-card-meta-value">'+esc(m.filePath)+':'+m.line+'</div></div>';
385
- h+='<div><div class="cred-card-meta-label">Migrate to</div><div class="cred-card-meta-value env">'+esc(m.envVar)+'</div></div>';
386
- h+='</div>';
387
- if(m.explanation||m.businessImpact){
388
- h+='<div class="cred-card-detail">';
389
- if(m.explanation){h+='<div class="cred-card-detail-label">Why this matters</div><div class="cred-card-detail-text">'+esc(m.explanation)+'</div>';}
390
- if(m.businessImpact){h+='<div class="cred-card-detail-label">Business impact</div><div class="cred-card-detail-text">'+esc(m.businessImpact)+'</div>';}
391
- h+='</div>';
392
- }
393
- h+='</div>';
394
- }
395
-
396
- // Drift
397
- if(data.driftFindings&&data.driftFindings.length>0){
398
- h+='<h2 class="section-title">Scope Drift</h2>';
399
- h+='<div class="card"><p style="color:var(--muted);font-size:12px;margin-bottom:8px;line-height:1.5">Scope drift occurs when a key provisioned for one service (e.g., Google Maps) silently grants access to AI services (e.g., Gemini). The key\\\'s permissions are wider than intended, expanding your attack surface without any code change.</p>';
400
- h+='<table class="data-table"><thead><tr><th>ID</th><th>File</th><th>Line</th></tr></thead><tbody>';
401
- for(var i=0;i<data.driftFindings.length;i++){
402
- var d=data.driftFindings[i];
403
- h+='<tr><td>'+esc(d.findingId)+'</td><td style="font-size:11px;color:var(--muted)">'+esc(d.filePath)+'</td><td>'+d.line+'</td></tr>';
404
- }
405
- h+='</tbody></table></div>';
406
- }
407
-
408
- h+='<h2 class="section-title">Remediation</h2>';
409
- h+='<div class="card">'+cmdBlock('opena2a protect')+'<p style="color:var(--muted);font-size:12px;margin-top:8px">Migrate hardcoded credentials to environment variables or encrypted vault.</p></div>';
410
- return h;
411
- }
412
-
413
- // ======================== HYGIENE ========================
414
- var hygieneDescriptions={
415
- 'Credential scan':'Detects API keys and secrets hardcoded in source files',
416
- 'credentials':'Detects API keys and secrets hardcoded in source files',
417
- '.gitignore':'Prevents sensitive files from being committed to version control',
418
- 'gitignore':'Prevents sensitive files from being committed to version control',
419
- '.env protection':'Ensures .env files (which store secrets) are excluded from git',
420
- 'env protection':'Ensures .env files (which store secrets) are excluded from git',
421
- 'Lock file':'Pins exact dependency versions to prevent supply chain attacks',
422
- 'lock file':'Pins exact dependency versions to prevent supply chain attacks',
423
- 'Security config':'OpenA2A configuration enables automated security monitoring',
424
- 'security config':'OpenA2A configuration enables automated security monitoring'
425
- };
426
- function findHygieneDesc(label){
427
- if(!label)return '';
428
- var lc=label.toLowerCase();
429
- for(var key in hygieneDescriptions){if(lc.indexOf(key.toLowerCase())>=0)return hygieneDescriptions[key];}
430
- return '';
431
- }
432
- function renderHygiene(){
433
- var init=report.initData;
434
- var h='<div class="section-intro">Project hygiene measures foundational security practices. These checks do not require any OpenA2A tools -- they are standard development practices that prevent accidental exposure.</div>';
435
- h+='<div class="stats-grid">';
436
- h+=statCard(init.trustScore+'/100','Trust Score',scoreColor(init.trustScore));
437
- h+=statCard(init.postureScore+'/100','Posture Score',scoreColor(init.postureScore));
438
- h+=statCard(init.riskLevel,'Risk Level',init.riskLevel==='SECURE'||init.riskLevel==='LOW'?'var(--green)':init.riskLevel==='MEDIUM'?'var(--medium)':'var(--red)');
439
- h+='</div>';
440
-
441
- h+='<div class="overview-top">';
442
- h+='<div class="gauge-card">'+gaugeCircle(init.trustScore,'out of 100')+'</div>';
443
- h+='<div>';
444
- h+='<h2 class="section-title">Hygiene Checks</h2>';
445
- h+='<div class="card">';
446
- var checks=init.hygieneChecks||[];
447
- for(var i=0;i<checks.length;i++){
448
- var c=checks[i];
449
- var statusClr=c.status==='pass'?'var(--green)':c.status==='fail'?'var(--red)':c.status==='warn'?'var(--medium)':'var(--dim)';
450
- var desc=findHygieneDesc(c.label);
451
- h+='<div class="hygiene-row"><div><span class="hygiene-label">'+esc(c.label)+'</span>'+(desc?'<div class="check-desc">'+esc(desc)+'</div>':'')+'</div><span style="color:'+statusClr+'">'+esc(c.detail)+'</span></div>';
452
- }
453
- h+='</div>';
454
- h+='</div></div>';
455
-
456
- h+='<div class="score-explainer">';
457
- h+='<div style="font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:8px">Trust Score Formula</div>';
458
- h+='<div style="display:grid;grid-template-columns:1fr 1fr;gap:4px 16px;font-size:12px">';
459
- h+='<div style="color:var(--muted)">Start</div><div style="color:var(--text);font-weight:600">100</div>';
460
- h+='<div style="color:var(--muted)">Missing .gitignore</div><div style="color:var(--red)">-15</div>';
461
- h+='<div style="color:var(--muted)">Unprotected .env</div><div style="color:var(--red)">-10</div>';
462
- h+='<div style="color:var(--muted)">No lock file</div><div style="color:var(--amber)">-5</div>';
463
- h+='<div style="color:var(--muted)">Security config</div><div style="color:var(--green)">+5 bonus</div>';
464
- h+='</div>';
465
- h+='</div>';
466
-
467
- h+='<div class="stats-grid">';
468
- h+=statCard(init.activeTools+'/'+init.totalTools,'Active Tools','var(--primary)');
469
- h+=statCard(init.advisoryCount,'Advisories',init.advisoryCount>0?'var(--amber)':'var(--green)');
470
- h+='</div>';
471
-
472
- h+='<h2 class="section-title">Project Info</h2>';
473
- h+='<div class="card">';
474
- h+='<div class="hygiene-row"><span class="hygiene-label">Project</span><span>'+esc(init.projectName||'unnamed')+'</span></div>';
475
- h+='<div class="hygiene-row"><span class="hygiene-label">Type</span><span>'+esc(init.projectType)+'</span></div>';
476
- h+='<div class="hygiene-row"><span class="hygiene-label">Version</span><span>'+esc(init.projectVersion||'--')+'</span></div>';
477
- h+='</div>';
478
- return h;
479
- }
480
-
481
- // ======================== INTEGRITY ========================
482
- function renderIntegrity(){
483
- var guard=report.guardData;
484
- var h='<div class="section-intro">ConfigGuard signs your configuration files with SHA-256 hashes. If anyone (or any agent) modifies a signed file, the tampering is immediately detectable. This is your first line of defense against configuration drift.</div>';
485
- var statusClr=guard.signatureStatus==='valid'?'var(--green)':guard.signatureStatus==='tampered'?'var(--red)':'var(--dim)';
486
- var statusLabel=guard.signatureStatus==='valid'?'Active':guard.signatureStatus==='tampered'?'Tampered':'Unsigned';
487
-
488
- h+='<div class="stats-grid">';
489
- h+=statCard(statusLabel,'Signature Status',statusClr);
490
- h+=statCard(guard.filesMonitored,'Files Monitored','var(--primary)');
491
- h+=statCard(guard.tamperedFiles?guard.tamperedFiles.length:0,'Tampered',(guard.tamperedFiles&&guard.tamperedFiles.length>0)?'var(--red)':'var(--green)');
492
- h+='</div>';
493
-
494
- if(guard.signatureStatus==='unsigned'){
495
- h+='<div class="cta-card"><div class="cta-title">ConfigGuard Not Active</div><div class="cta-desc">Sign your config files to detect unauthorized modifications.</div>'+cmdBlock('opena2a guard sign')+'</div>';
496
- }else if(guard.signatureStatus==='tampered'){
497
- h+='<h2 class="section-title">Tampered Files</h2>';
498
- h+='<div class="card"><table class="data-table"><thead><tr><th>File</th><th>Status</th></tr></thead><tbody>';
499
- for(var i=0;i<guard.tamperedFiles.length;i++){
500
- h+='<tr><td>'+esc(guard.tamperedFiles[i])+'</td><td><span class="sev-badge sev-critical">tampered</span></td></tr>';
501
- }
502
- h+='</tbody></table></div>';
503
- h+='<h2 class="section-title">Remediation</h2>';
504
- h+='<div class="card">'+cmdBlock('opena2a guard diff')+'<p style="color:var(--muted);font-size:12px;margin:4px 0">Review changes, then resign:</p>'+cmdBlock('opena2a guard resign')+'</div>';
505
- }else{
506
- h+='<div class="card"><div class="empty-state">All '+guard.filesMonitored+' monitored files have valid signatures.</div></div>';
507
- }
508
- return h;
509
- }
510
-
511
- // ======================== SHIELD ========================
512
- function renderShield(){
513
- var shield=report.shieldData;
514
- var h='<div class="section-intro">Shield is the unified security orchestration layer. It collects events from all OpenA2A tools (ARP, ConfigGuard, Secretless, HMA) into a tamper-evident log and classifies them into actionable findings.</div>';
515
- h+='<div class="stats-grid">';
516
- h+=statCard(shield.postureScore+'/100','Posture Score',scoreColor(shield.postureScore));
517
- h+=statCard(shield.eventCount,'Events (7d)','var(--primary)');
518
- h+=statCard(shield.classifiedFindings?shield.classifiedFindings.length:0,'Findings',shield.classifiedFindings&&shield.classifiedFindings.length>0?'var(--amber)':'var(--green)');
519
- h+=statCard(shield.policyLoaded?'Loaded':'None','Policy',shield.policyLoaded?'var(--green)':'var(--dim)');
520
- h+=statCard(shield.policyMode||'--','Mode','var(--muted)');
521
- h+=statCard(shield.integrityStatus||'healthy','Integrity',shield.integrityStatus==='healthy'?'var(--green)':'var(--red)');
522
- h+='</div>';
523
-
524
- // Classified findings
525
- var cf=shield.classifiedFindings||[];
526
- if(cf.length>0){
527
- h+='<h2 class="section-title">Classified Findings</h2>';
528
- h+='<div class="card"><table class="data-table"><thead><tr><th>ID</th><th>Title</th><th>Severity</th><th>Count</th><th>Remediation</th></tr></thead><tbody>';
529
- for(var i=0;i<cf.length;i++){
530
- var f=cf[i];
531
- var badges='';
532
- if(f.finding.owaspAgentic)badges+='<span class="badge-owasp">'+esc(f.finding.owaspAgentic)+'</span>';
533
- if(f.finding.mitreAtlas)badges+='<span class="badge-mitre">'+esc(f.finding.mitreAtlas)+'</span>';
534
- h+='<tr><td>'+esc(f.finding.id)+'</td><td>'+esc(f.finding.title)+(badges?' '+badges:'')+'</td><td><span class="sev-badge sev-'+esc(f.finding.severity)+'">'+esc(f.finding.severity)+'</span></td><td>'+f.count+'</td><td>'+cmdBlock(f.finding.remediation)+'</td></tr>';
535
- if(f.finding.description){
536
- h+='<tr><td colspan="5" class="finding-desc">'+esc(f.finding.description)+'</td></tr>';
537
- }
538
- }
539
- h+='</tbody></table></div>';
540
- }
541
-
542
- // ARP stats
543
- var arp=shield.arpStats;
544
- if(arp&&arp.totalEvents>0){
545
- h+='<h2 class="section-title">Runtime Protection (ARP)</h2>';
546
- h+='<div class="card"><div class="arp-grid">';
547
- h+='<div class="arp-stat"><div class="arp-stat-value">'+arp.totalEvents+'</div><div class="arp-stat-label">Total Events</div></div>';
548
- h+='<div class="arp-stat"><div class="arp-stat-value" style="color:var(--amber)">'+arp.anomalies+'</div><div class="arp-stat-label">Anomalies</div></div>';
549
- h+='<div class="arp-stat"><div class="arp-stat-value" style="color:var(--red)">'+arp.violations+'</div><div class="arp-stat-label">Violations</div></div>';
550
- h+='<div class="arp-stat"><div class="arp-stat-value">'+arp.processEvents+'</div><div class="arp-stat-label">Process</div></div>';
551
- h+='<div class="arp-stat"><div class="arp-stat-value">'+arp.networkEvents+'</div><div class="arp-stat-label">Network</div></div>';
552
- h+='<div class="arp-stat"><div class="arp-stat-value">'+arp.enforcements+'</div><div class="arp-stat-label">Enforcements</div></div>';
553
- h+='</div></div>';
554
- }else{
555
- h+='<h2 class="section-title">Runtime Protection (ARP)</h2>';
556
- h+='<div class="section-intro">ARP (Agent Runtime Protection) monitors process spawns, network connections, and file access in real time. It detects anomalous agent behavior before damage occurs.</div>';
557
- h+='<div class="card"><div class="empty-state">No ARP events in the last 7 days. Start runtime monitoring:</div>'+cmdBlock('opena2a runtime start')+'</div>';
558
- }
559
-
560
- if(!shield.policyLoaded){
561
- h+='<h2 class="section-title">Policy</h2>';
562
- h+='<div class="cta-card"><div class="cta-title">No Security Policy</div><div class="cta-desc">Initialize Shield to enable adaptive security policy.</div>'+cmdBlock('opena2a shield init')+'</div>';
563
- }
564
- return h;
565
- }
566
-
567
- // ======================== HMA ========================
568
- function renderHma(){
569
- var hma=report.hmaData;
570
- if(!hma||!hma.available){
571
- var h='<div class="section-intro">HackMyAgent runs 147 security checks against AI agent endpoints, testing for prompt injection, tool misuse, excessive agency, and OWASP Top 10 for LLM vulnerabilities.</div>';
572
- h+='<div class="cta-card"><div class="cta-title">HackMyAgent Not Installed</div>';
573
- h+='<div class="cta-desc">Install HMA to run comprehensive security scans against your AI agent.</div>';
574
- h+=cmdBlock('npm install -g hackmyagent');
575
- h+='<p style="color:var(--muted);font-size:13px;margin-top:12px;text-align:center">Then re-run: <code style="color:var(--primary)">opena2a review</code></p>';
576
- h+='</div>';
577
- return h;
578
- }
579
- var h='<div class="stats-grid">';
580
- h+=statCard(hma.score+'/100','HMA Score',scoreColor(hma.score));
581
- h+=statCard('Available','Status','var(--green)');
582
- h+='</div>';
583
- h+='<div class="card"><div class="empty-state">HMA is available. Run a full scan for detailed results:</div>'+cmdBlock('opena2a scan --deep')+'</div>';
584
- return h;
585
- }
586
-
587
- // Initial render
588
- renderPage('overview');
589
- })();
157
+ (function(){var report=JSON.parse(document.getElementById('report-data').textContent);var pagesRendered={};function esc(s){return s==null?'':String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}function scoreColor(s){return s>=90?'var(--green)':s>=70?'var(--primary)':s>=50?'var(--medium)':'var(--red)';}document.getElementById('main-nav').addEventListener('click',function(e){var btn=e.target.closest('.nav-tab');if(!btn)return;var pg=btn.getAttribute('data-page');document.querySelectorAll('.nav-tab').forEach(function(t){t.classList.toggle('active',t===btn);});document.querySelectorAll('.page').forEach(function(p){p.classList.toggle('active',p.id==='page-'+pg);});renderPage(pg);});function renderPage(pg){if(pagesRendered[pg])return;pagesRendered[pg]=true;var el=document.getElementById('page-'+pg);switch(pg){case 'overview':el.innerHTML=renderOverview();break;case 'credentials':el.innerHTML=renderCredentials();break;case 'hygiene':el.innerHTML=renderHygiene();break;case 'integrity':el.innerHTML=renderIntegrity();break;case 'shield':el.innerHTML=renderShield();break;case 'hma':el.innerHTML=renderHma();break;case 'shadowai':el.innerHTML=renderShadowAi();break;}}window.copyCmd=function(btn){var cmd=btn.getAttribute('data-cmd');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);}};window.goToTab=function(tab){var btn=document.querySelector('.nav-tab[data-page="'+tab+'"]');if(btn)btn.click();};function gaugeCircle(score,label){var sz=170,cx=sz/2,cy=sz/2,r=65,sw=10,circ=2*Math.PI*r;var pct=Math.max(0,Math.min(100,score))/100,dash=pct*circ,gap=circ-dash;var clr=score>=90?'#22c55e':score>=70?'#06b6d4':score>=50?'#eab308':'#ef4444';var s='<svg width="'+sz+'" height="'+sz+'" viewBox="0 0 '+sz+' '+sz+'">';s+='<circle cx="'+cx+'" cy="'+cy+'" r="'+r+'" fill="none" stroke="rgba(255,255,255,0.05)" stroke-width="'+sw+'"/>';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+')"/>';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>';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)">'+esc(label||('out of 100'))+'</text>';s+='</svg>';return s;}function cmdBlock(cmd){return '<div class="cmd-block"><span class="cmd-text">'+esc(cmd)+'</span><button class="copy-btn" data-cmd="'+esc(cmd)+'" onclick="copyCmd(this)">Copy</button></div>';}function statCard(value,label,color){return '<div class="stat-card"><div class="stat-value" style="color:'+color+'">'+esc(String(value))+'</div><div class="stat-label">'+esc(label)+'</div></div>';}function scoreBanner(score,recoverySummary){var clr=scoreColor(score);var h='<div class="score-banner"><div class="score-banner-num" style="color:'+clr+'">'+score+'</div><div class="score-banner-bar"><div class="score-banner-label"><span>Composite Score</span><span>'+score+'/100</span></div><div class="score-banner-track"><div class="score-banner-fill" style="width:'+score+'%;background:'+clr+'"></div></div></div>';if(recoverySummary&&recoverySummary.totalRecoverable>0){var recovBg=score>=70?'rgba(6,182,212,0.15)':score>=50?'rgba(234,179,8,0.15)':'rgba(239,68,68,0.15)';h+='<div class="score-banner-grade" style="color:'+clr+';background:'+recovBg+'">+'+recoverySummary.totalRecoverable+' recoverable</div>';}h+='</div>';return h;}function governanceBanner(score,recoverablePoints){var clr=scoreColor(score);var h='<div class="score-banner"><div class="score-banner-num" style="color:'+clr+'">'+score+'</div><div class="score-banner-bar"><div class="score-banner-label"><span>Governance Score</span><span>'+score+'/100</span></div><div class="score-banner-track"><div class="score-banner-fill" style="width:'+score+'%;background:'+clr+'"></div></div></div>';if(recoverablePoints>0){var projected=Math.min(100,score+recoverablePoints);var recovBg=score>=70?'rgba(6,182,212,0.15)':score>=50?'rgba(234,179,8,0.15)':'rgba(239,68,68,0.15)';h+='<div class="score-banner-grade" style="color:'+clr+';background:'+recovBg+'">path to '+projected+'</div>';}h+='</div>';return h;}var phaseDescriptions={'Project Scan':'Checks .gitignore, lock files, security config, and dependency advisories','Credentials':'Scans source files for hardcoded API keys, tokens, and secrets','Config Integrity':'Verifies cryptographic signatures on monitored config files','Shield Analysis':'Analyzes 7 days of security events, policy violations, and ARP detections','HMA Scan':'Runs HackMyAgent security checks against your AI agent endpoints','Shadow AI':'Detects AI agents, MCP servers, and AI configs; checks governance posture'};function phaseCard(phase){var statusCls='status-'+phase.status;var time=phase.status==='skip'?'--':(phase.durationMs/1000).toFixed(1)+'s';var desc=phaseDescriptions[phase.name]||'';return '<div class="phase-card"><div style="display:flex;justify-content:space-between;align-items:center"><div class="phase-name">'+esc(phase.name)+'</div><span class="status-badge '+statusCls+'">'+esc(phase.status)+'</span></div><div class="phase-detail">'+esc(phase.detail)+'</div>'+(desc?'<div class="phase-desc">'+esc(desc)+'</div>':'')+'<div class="phase-time">'+esc(time)+'</div></div>';}function renderOverview(){var h='';var sevCounts={critical:0,high:0,medium:0,low:0};var findings=report.findings||[];for(var i=0;i<findings.length;i++){var s=findings[i].severity;if(s in sevCounts)sevCounts[s]++;}h+=scoreBanner(report.compositeScore,report.recoverySummary);h+='<div class="stats-grid">';h+=statCard(findings.length,'Findings',findings.length>0?'var(--amber)':'var(--green)');h+=statCard(sevCounts.critical,'Critical',sevCounts.critical>0?'var(--critical)':'var(--text)');h+=statCard(sevCounts.high,'High',sevCounts.high>0?'var(--high)':'var(--text)');h+=statCard(sevCounts.medium,'Medium',sevCounts.medium>0?'var(--medium)':'var(--text)');h+='</div>';h+='<h2 class="section-title">Phase Results</h2><div class="phase-grid">';var phases=report.phases||[];for(var i=0;i<phases.length;i++)h+=phaseCard(phases[i]);h+='</div>';var detectScore=report.detectData?report.detectData.governanceScore:100;var dims=[{name:'Hygiene',weight:30,score:report.initData.trustScore,tab:'hygiene'},{name:'Shield',weight:20,score:report.shieldData.postureScore,tab:'shield'},{name:'Credentials',weight:20,score:phases.length>1?phases[1].score:0,tab:'credentials'},{name:'Integrity',weight:15,score:phases.length>2?phases[2].score:0,tab:'integrity'},{name:'Shadow AI',weight:15,score:detectScore,tab:'shadowai'}];h+='<div class="score-explainer"><div style="font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:12px">Score Breakdown</div><div style="display:flex;flex-direction:column;gap:10px">';for(var i=0;i<dims.length;i++){var d=dims[i];var clr=scoreColor(d.score);h+='<div style="display:grid;grid-template-columns:100px 1fr 60px 50px;align-items:center;gap:10px;cursor:pointer" onclick="goToTab(&quot;'+d.tab+'&quot;)"><div style="display:flex;align-items:baseline;gap:6px"><span style="font-size:13px;color:var(--muted)">'+esc(d.name)+'</span></div><div style="position:relative;height:8px;background:rgba(255,255,255,0.06);border-radius:4px;overflow:hidden"><div style="position:absolute;left:0;top:0;height:100%;width:'+d.score+'%;background:'+clr+';border-radius:4px;transition:width 0.3s"></div></div><div style="text-align:right;font-size:14px;font-weight:700;color:'+clr+'">'+d.score+'<span style="font-size:11px;color:var(--dim);font-weight:400">/100</span></div><div style="text-align:right;font-size:11px;color:var(--dim)">x '+d.weight+'%</div></div>';}h+='</div><div style="display:flex;justify-content:space-between;align-items:center;border-top:1px solid rgba(51,65,85,0.4);margin-top:12px;padding-top:8px"><div style="display:flex;gap:12px;font-size:12px;color:var(--dim)"><span><strong style="color:var(--green)">A</strong> 90+</span><span><strong style="color:var(--primary)">B</strong> 80+</span><span><strong style="color:var(--medium)">C</strong> 70+</span><span><strong style="color:var(--high)">D</strong> 60+</span><span><strong style="color:var(--red)">F</strong> &lt;60</span></div><div style="font-size:12px;color:var(--dim)">Click a row to view details</div></div></div>';var actions=report.actionItems||[];var actionImpact={'critical':'Immediate risk of credential compromise or data breach','high':'Significant security gap that attackers can exploit','medium':'Moderate risk that weakens your security posture','low':'Minor improvement to harden your defenses','info':'Recommended best practice'};if(actions.length>0){h+='<h2 class="section-title">Action Items</h2><div class="card">';for(var i=0;i<actions.length;i++){var a=actions[i];var impact=actionImpact[a.severity]||'';h+='<div class="action-item"><div class="action-priority">#'+a.priority+'</div><div class="action-content"><div class="action-desc"><span class="sev-badge sev-'+esc(a.severity)+'">'+esc(a.severity)+'</span> '+esc(a.description)+'</div>'+(impact?'<div class="check-desc">'+esc(impact)+'</div>':'')+cmdBlock(a.command)+'<span class="action-link" onclick="goToTab(&quot;'+esc(a.tab)+'&quot;)">View details</span></div></div>';}h+='</div>';}if(findings.length>0){h+='<h2 class="section-title">Findings</h2><div class="card"><table class="data-table"><thead><tr><th>ID</th><th>Title</th><th>Severity</th><th>Source</th><th>Detail</th></tr></thead><tbody>';var top=findings.slice(0,10);for(var i=0;i<top.length;i++){var f=top[i];h+='<tr><td>'+esc(f.id)+'</td><td>'+esc(f.title)+'</td><td><span class="sev-badge sev-'+esc(f.severity)+'">'+esc(f.severity)+'</span></td><td>'+esc(f.source)+'</td><td style="font-size:11px;color:var(--muted)">'+esc(f.detail)+'</td></tr>';}h+='</tbody></table></div>';}return h;}function renderCredentials(){var data=report.credentialData;if(!data||data.totalFindings===0)return '<div class="card"><div class="empty-state">No hardcoded credentials found. Your project is clean.</div></div>';var h='<div class="section-intro">Hardcoded credentials in source code are the #1 cause of security breaches in AI projects. Keys pushed to git are scraped by bots within minutes. Each finding below is a credential that should be moved to environment variables.</div>';h+='<div class="stats-grid">';h+=statCard(data.totalFindings,'Total Findings',data.totalFindings>0?'var(--red)':'var(--green)');h+=statCard(data.bySeverity.critical||0,'Critical',(data.bySeverity.critical||0)>0?'var(--critical)':'var(--text)');h+=statCard(data.bySeverity.high||0,'High',(data.bySeverity.high||0)>0?'var(--high)':'var(--text)');h+=statCard(data.bySeverity.medium||0,'Medium',(data.bySeverity.medium||0)>0?'var(--medium)':'var(--text)');h+='</div>';h+='<h2 class="section-title">Credential Findings</h2>';var matches=data.matches||[];for(var i=0;i<matches.length;i++){var m=matches[i];h+='<div class="cred-card"><div class="cred-card-header"><span class="sev-badge sev-'+esc(m.severity)+'">'+esc(m.severity)+'</span><span class="cred-card-title">'+esc(m.title)+'</span><span style="color:var(--dim);font-size:12px">'+esc(m.findingId)+'</span></div><div class="cred-card-meta"><div><div class="cred-card-meta-label">File</div><div class="cred-card-meta-value">'+esc(m.filePath)+':'+m.line+'</div></div><div><div class="cred-card-meta-label">Migrate to</div><div class="cred-card-meta-value env">'+esc(m.envVar)+'</div></div></div>';if(m.explanation||m.businessImpact){h+='<div class="cred-card-detail">';if(m.explanation)h+='<div class="cred-card-detail-label">Why this matters</div><div class="cred-card-detail-text">'+esc(m.explanation)+'</div>';if(m.businessImpact)h+='<div class="cred-card-detail-label">Business impact</div><div class="cred-card-detail-text">'+esc(m.businessImpact)+'</div>';h+='</div>';}h+='</div>';}if(data.driftFindings&&data.driftFindings.length>0){h+='<h2 class="section-title">Scope Drift</h2><div class="card"><p style="color:var(--muted);font-size:12px;margin-bottom:8px;line-height:1.5">Scope drift occurs when a key provisioned for one service silently grants access to AI services. The key\\\'s permissions are wider than intended.</p><table class="data-table"><thead><tr><th>ID</th><th>File</th><th>Line</th></tr></thead><tbody>';for(var i=0;i<data.driftFindings.length;i++){var d=data.driftFindings[i];h+='<tr><td>'+esc(d.findingId)+'</td><td style="font-size:11px;color:var(--muted)">'+esc(d.filePath)+'</td><td>'+d.line+'</td></tr>';}h+='</tbody></table></div>';}h+='<h2 class="section-title">Remediation</h2><div class="card">'+cmdBlock('opena2a protect')+'<p style="color:var(--muted);font-size:12px;margin-top:8px">Migrate hardcoded credentials to environment variables or encrypted vault.</p></div>';return h;}var hygieneDescriptions={'Credential scan':'Detects API keys and secrets hardcoded in source files','.gitignore':'Prevents sensitive files from being committed to version control','.env protection':'Ensures .env files (which store secrets) are excluded from git','Lock file':'Pins exact dependency versions to prevent supply chain attacks','Security config':'OpenA2A configuration enables automated security monitoring'};function findHygieneDesc(label){if(!label)return '';var lc=label.toLowerCase();for(var key in hygieneDescriptions){if(lc.indexOf(key.toLowerCase())>=0)return hygieneDescriptions[key];}return '';}function renderHygiene(){var init=report.initData;var h='<div class="section-intro">Project hygiene measures foundational security practices. These checks do not require any OpenA2A tools -- they are standard development practices that prevent accidental exposure.</div>';h+='<div class="stats-grid">';h+=statCard(init.trustScore+'/100','Trust Score',scoreColor(init.trustScore));h+=statCard(init.postureScore+'/100','Posture Score',scoreColor(init.postureScore));h+=statCard(init.riskLevel,'Risk Level',init.riskLevel==='SECURE'||init.riskLevel==='LOW'?'var(--green)':init.riskLevel==='MEDIUM'?'var(--medium)':'var(--red)');h+='</div>';h+='<div class="overview-top"><div class="gauge-card">'+gaugeCircle(init.trustScore,'out of 100')+'</div><div><h2 class="section-title">Hygiene Checks</h2><div class="card">';var checks=init.hygieneChecks||[];for(var i=0;i<checks.length;i++){var c=checks[i];var statusClr=c.status==='pass'?'var(--green)':c.status==='fail'?'var(--red)':c.status==='warn'?'var(--medium)':'var(--dim)';var desc=findHygieneDesc(c.label);h+='<div class="hygiene-row"><div><span class="hygiene-label">'+esc(c.label)+'</span>'+(desc?'<div class="check-desc">'+esc(desc)+'</div>':'')+'</div><span style="color:'+statusClr+'">'+esc(c.detail)+'</span></div>';}h+='</div></div></div>';h+='<div class="score-explainer"><div style="font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:8px">Trust Score Formula</div><div style="display:grid;grid-template-columns:1fr 1fr;gap:4px 16px;font-size:12px"><div style="color:var(--muted)">Start</div><div style="color:var(--text);font-weight:600">100</div><div style="color:var(--muted)">Missing .gitignore</div><div style="color:var(--red)">-15</div><div style="color:var(--muted)">Unprotected .env</div><div style="color:var(--red)">-10</div><div style="color:var(--muted)">No lock file</div><div style="color:var(--amber)">-5</div><div style="color:var(--muted)">Security config</div><div style="color:var(--green)">+5 bonus</div></div></div>';h+='<div class="stats-grid">';h+=statCard(init.activeTools+'/'+init.totalTools,'Active Tools','var(--primary)');h+=statCard(init.advisoryCount,'Advisories',init.advisoryCount>0?'var(--amber)':'var(--green)');h+='</div>';h+='<h2 class="section-title">Project Info</h2><div class="card"><div class="hygiene-row"><span class="hygiene-label">Project</span><span>'+esc(init.projectName||'unnamed')+'</span></div><div class="hygiene-row"><span class="hygiene-label">Type</span><span>'+esc(init.projectType)+'</span></div><div class="hygiene-row"><span class="hygiene-label">Version</span><span>'+esc(init.projectVersion||'--')+'</span></div></div>';return h;}function renderIntegrity(){var guard=report.guardData;var h='<div class="section-intro">ConfigGuard signs your configuration files with SHA-256 hashes. If anyone (or any agent) modifies a signed file, the tampering is immediately detectable.</div>';var statusClr=guard.signatureStatus==='valid'?'var(--green)':guard.signatureStatus==='tampered'?'var(--red)':'var(--dim)';var statusLabel=guard.signatureStatus==='valid'?'Active':guard.signatureStatus==='tampered'?'Tampered':'Unsigned';h+='<div class="stats-grid">';h+=statCard(statusLabel,'Signature Status',statusClr);h+=statCard(guard.filesMonitored,'Files Monitored','var(--primary)');h+=statCard(guard.tamperedFiles?guard.tamperedFiles.length:0,'Tampered',(guard.tamperedFiles&&guard.tamperedFiles.length>0)?'var(--red)':'var(--green)');h+='</div>';if(guard.signatureStatus==='unsigned'){h+='<div class="cta-card"><div class="cta-title">ConfigGuard Not Active</div><div class="cta-desc">Sign your config files to detect unauthorized modifications.</div>'+cmdBlock('opena2a guard sign')+'</div>';}else if(guard.signatureStatus==='tampered'){h+='<h2 class="section-title">Tampered Files</h2><div class="card"><table class="data-table"><thead><tr><th>File</th><th>Status</th></tr></thead><tbody>';for(var i=0;i<guard.tamperedFiles.length;i++){h+='<tr><td>'+esc(guard.tamperedFiles[i])+'</td><td><span class="sev-badge sev-critical">tampered</span></td></tr>';}h+='</tbody></table></div><h2 class="section-title">Remediation</h2><div class="card">'+cmdBlock('opena2a guard diff')+'<p style="color:var(--muted);font-size:12px;margin:4px 0">Review changes, then resign:</p>'+cmdBlock('opena2a guard resign')+'</div>';}else{h+='<div class="card"><div class="empty-state">All '+guard.filesMonitored+' monitored files have valid signatures.</div></div>';}return h;}function renderShield(){var shield=report.shieldData;var h='<div class="section-intro">Shield is the unified security orchestration layer. It collects events from all OpenA2A tools into a tamper-evident log and classifies them into actionable findings.</div>';h+='<div class="stats-grid">';h+=statCard(shield.postureScore+'/100','Posture Score',scoreColor(shield.postureScore));h+=statCard(shield.eventCount,'Events (7d)','var(--primary)');h+=statCard(shield.classifiedFindings?shield.classifiedFindings.length:0,'Findings',shield.classifiedFindings&&shield.classifiedFindings.length>0?'var(--amber)':'var(--green)');h+=statCard(shield.policyLoaded?'Loaded':'None','Policy',shield.policyLoaded?'var(--green)':'var(--dim)');h+=statCard(shield.policyMode||'--','Mode','var(--muted)');h+=statCard(shield.integrityStatus||'healthy','Integrity',shield.integrityStatus==='healthy'?'var(--green)':'var(--red)');h+='</div>';var cf=shield.classifiedFindings||[];if(cf.length>0){h+='<h2 class="section-title">Classified Findings</h2><div class="card"><table class="data-table"><thead><tr><th>ID</th><th>Title</th><th>Severity</th><th>Count</th><th>Remediation</th></tr></thead><tbody>';for(var i=0;i<cf.length;i++){var f=cf[i];var badges='';if(f.finding.owaspAgentic)badges+='<span class="badge-owasp">'+esc(f.finding.owaspAgentic)+'</span>';if(f.finding.mitreAtlas)badges+='<span class="badge-mitre">'+esc(f.finding.mitreAtlas)+'</span>';h+='<tr><td>'+esc(f.finding.id)+'</td><td>'+esc(f.finding.title)+(badges?' '+badges:'')+'</td><td><span class="sev-badge sev-'+esc(f.finding.severity)+'">'+esc(f.finding.severity)+'</span></td><td>'+f.count+'</td><td>'+cmdBlock(f.finding.remediation)+'</td></tr>';if(f.finding.description)h+='<tr><td colspan="5" class="finding-desc">'+esc(f.finding.description)+'</td></tr>';}h+='</tbody></table></div>';}var arp=shield.arpStats;if(arp&&arp.totalEvents>0){h+='<h2 class="section-title">Runtime Protection (ARP)</h2><div class="card"><div class="arp-grid"><div class="arp-stat"><div class="arp-stat-value">'+arp.totalEvents+'</div><div class="arp-stat-label">Total Events</div></div><div class="arp-stat"><div class="arp-stat-value" style="color:var(--amber)">'+arp.anomalies+'</div><div class="arp-stat-label">Anomalies</div></div><div class="arp-stat"><div class="arp-stat-value" style="color:var(--red)">'+arp.violations+'</div><div class="arp-stat-label">Violations</div></div><div class="arp-stat"><div class="arp-stat-value">'+arp.processEvents+'</div><div class="arp-stat-label">Process</div></div><div class="arp-stat"><div class="arp-stat-value">'+arp.networkEvents+'</div><div class="arp-stat-label">Network</div></div><div class="arp-stat"><div class="arp-stat-value">'+arp.enforcements+'</div><div class="arp-stat-label">Enforcements</div></div></div></div>';}else{h+='<h2 class="section-title">Runtime Protection (ARP)</h2><div class="section-intro">ARP monitors process spawns, network connections, and file access in real time.</div><div class="card"><div class="empty-state">No ARP events in the last 7 days. Start runtime monitoring:</div>'+cmdBlock('opena2a runtime start')+'</div>';}if(!shield.policyLoaded){h+='<h2 class="section-title">Policy</h2><div class="cta-card"><div class="cta-title">No Security Policy</div><div class="cta-desc">Initialize Shield to enable adaptive security policy.</div>'+cmdBlock('opena2a shield init')+'</div>';}return h;}function renderHma(){var hma=report.hmaData;if(!hma||!hma.available){var h='<div class="section-intro">HackMyAgent runs 147 security checks against AI agent endpoints, testing for prompt injection, tool misuse, excessive agency, and OWASP Top 10 for LLM vulnerabilities.</div><div class="cta-card"><div class="cta-title">HackMyAgent Not Installed</div><div class="cta-desc">Install HMA to run comprehensive security scans against your AI agent.</div>'+cmdBlock('npm install -g hackmyagent')+'<p style="color:var(--muted);font-size:13px;margin-top:12px;text-align:center">Then re-run: <code style="color:var(--primary)">opena2a review</code></p></div>';return h;}var h='<div class="stats-grid">';h+=statCard(hma.score+'/100','HMA Score',scoreColor(hma.score));h+=statCard('Available','Status','var(--green)');h+='</div><div class="card"><div class="empty-state">HMA is available. Run a full scan for detailed results:</div>'+cmdBlock('opena2a scan --deep')+'</div>';return h;}function renderShadowAi(){var data=report.detectData;if(!data)return '<div class="card"><div class="empty-state">Shadow AI detection data not available.</div></div>';var capDescs={'filesystem':'Can read and write files','shell-access':'Can run commands on this computer','database':'Can read and modify databases','network':'Can make requests to external services','browser':'Can control a web browser','source-control':'Can access code repositories','messaging':'Can send messages','payments':'Can access payment systems','cloud-services':'Can access cloud infrastructure'};var h='<div class="section-intro">Shadow AI detection discovers AI agents running on this machine, MCP servers configured across all platforms, and AI configuration files in the project. It assesses governance posture and identifies gaps in identity, behavioral rules, and capability policies.</div>';var govScore=data.governanceScore;h+='<div class="stats-grid">';h+=statCard(govScore+'/100','Governance Score',scoreColor(govScore));h+=statCard(data.agents?data.agents.length:0,'AI Agents','var(--primary)');h+=statCard(data.mcpServers?data.mcpServers.length:0,'MCP Servers','var(--primary)');h+=statCard(data.aiConfigs?data.aiConfigs.length:0,'AI Configs','var(--primary)');h+='</div>';h+=governanceBanner(govScore,data.recoverablePoints);var agents=data.agents||[];var mcpServers=data.mcpServers||[];if(agents.length>0||mcpServers.length>0){h+='<h2 class="section-title">What This Means</h2><div class="card">';if(agents.length>0){var governed=0;for(var i=0;i<agents.length;i++){if(agents[i].governanceStatus==='governed')governed++;}var ungoverned=agents.length-governed;if(ungoverned===0){h+='<p style="color:var(--muted);font-size:13px;line-height:1.6;margin-bottom:8px">Your '+(agents.length===1?'AI agent has':'AI agents have')+' governance in place. Actions are bounded by the rules you defined.</p>';}else if(governed>0){h+='<p style="color:var(--muted);font-size:13px;line-height:1.6;margin-bottom:8px">'+agents.length+' AI tool'+(agents.length!==1?'s are':' is')+' running on this machine. '+governed+' '+(governed===1?'has':'have')+' governance rules, '+ungoverned+' '+(ungoverned===1?'does':'do')+' not.</p>';}else{h+='<p style="color:var(--muted);font-size:13px;line-height:1.6;margin-bottom:8px">'+agents.length+' AI tool'+(agents.length!==1?'s are':' is')+' running without governance. There are no documented rules limiting what '+(agents.length===1?'it':'they')+' can do in this project.</p>';}}if(mcpServers.length>0){var verified=0;for(var i=0;i<mcpServers.length;i++){if(mcpServers[i].verified)verified++;}var unverified=mcpServers.length-verified;h+='<p style="color:var(--muted);font-size:13px;line-height:1.6;margin-bottom:8px">'+mcpServers.length+' MCP server'+(mcpServers.length!==1?'s give':' gives')+' your AI agents additional capabilities (file access, database queries, API calls, etc.).</p>';if(unverified>0&&verified>0){h+='<p style="color:var(--muted);font-size:13px;line-height:1.6;margin-bottom:8px">'+verified+' '+(verified===1?'has':'have')+' verified identities, '+unverified+' '+(unverified===1?'does':'do')+' not.</p>';}else if(unverified===mcpServers.length){h+='<p style="color:var(--muted);font-size:13px;line-height:1.6;margin-bottom:8px">None have verified identities, so there is no tamper-evident record of which server version is installed.</p>';}}h+='</div>';}h+='<h2 class="section-title">Running AI Agents</h2>';if(agents.length===0){h+='<div class="card"><div class="empty-state">No AI agents detected on this machine.</div></div>';}else{h+='<div class="card"><table class="data-table"><thead><tr><th>Name</th><th>Identity</th><th>Governance</th></tr></thead><tbody>';for(var i=0;i<agents.length;i++){var a=agents[i];var idClr=a.identityStatus==='identified'?'var(--green)':'var(--medium)';var govClr=a.governanceStatus==='governed'?'var(--green)':'var(--medium)';h+='<tr><td>'+esc(a.name)+'</td><td style="color:'+idClr+'">'+esc(a.identityStatus)+'</td><td style="color:'+govClr+'">'+esc(a.governanceStatus)+'</td></tr>';}h+='</tbody></table></div>';}h+='<h2 class="section-title">MCP Servers</h2>';if(mcpServers.length===0){h+='<div class="card"><div class="empty-state">No MCP server configurations found.</div></div>';}else{var projectMcp=[];var globalMcp=[];for(var i=0;i<mcpServers.length;i++){if(mcpServers[i].source.indexOf('(project)')>=0)projectMcp.push(mcpServers[i]);else globalMcp.push(mcpServers[i]);}if(projectMcp.length>0){h+='<div class="card"><div class="card-title">Project-local ('+projectMcp.length+')</div><table class="data-table"><thead><tr><th>Name</th><th>Transport</th><th>Verified</th><th>Capabilities</th><th>Risk</th></tr></thead><tbody>';for(var i=0;i<projectMcp.length;i++){var s=projectMcp[i];var verStr=s.verified?'<span style="color:var(--green)">verified</span>':'<span style="color:var(--dim)">no</span>';var realCaps=(s.capabilities||[]).filter(function(c){return c!=='unknown';});var capsHtml='';for(var j=0;j<realCaps.length;j++){var desc=capDescs[realCaps[j]]||realCaps[j];capsHtml+='<div style="font-size:11px;color:var(--muted);margin:1px 0">'+esc(desc)+'</div>';}if(realCaps.length===0)capsHtml='<span style="color:var(--dim)">--</span>';h+='<tr><td>'+esc(s.name)+'</td><td>'+esc(s.transport)+'</td><td>'+verStr+'</td><td>'+capsHtml+'</td><td><span class="sev-badge sev-'+esc(s.risk)+'">'+esc(s.risk)+'</span></td></tr>';}h+='</tbody></table></div>';}if(globalMcp.length>0){h+='<div class="card"><div class="card-title">Machine-wide ('+globalMcp.length+')</div><table class="data-table"><thead><tr><th>Name</th><th>Source</th><th>Transport</th><th>Capabilities</th><th>Risk</th></tr></thead><tbody>';for(var i=0;i<globalMcp.length;i++){var s=globalMcp[i];var realCaps=(s.capabilities||[]).filter(function(c){return c!=='unknown';});var capsHtml='';for(var j=0;j<realCaps.length;j++){var desc=capDescs[realCaps[j]]||realCaps[j];capsHtml+='<div style="font-size:11px;color:var(--muted);margin:1px 0">'+esc(desc)+'</div>';}if(realCaps.length===0)capsHtml='<span style="color:var(--dim)">--</span>';h+='<tr><td>'+esc(s.name)+'</td><td style="font-size:11px;color:var(--dim)">'+esc(s.source)+'</td><td>'+esc(s.transport)+'</td><td>'+capsHtml+'</td><td><span class="sev-badge sev-'+esc(s.risk)+'">'+esc(s.risk)+'</span></td></tr>';}h+='</tbody></table></div>';}}var aiConfigs=data.aiConfigs||[];if(aiConfigs.length>0){h+='<h2 class="section-title">AI Config Files</h2><div class="card"><table class="data-table"><thead><tr><th>File</th><th>Tool</th><th>Risk</th><th>Details</th></tr></thead><tbody>';for(var i=0;i<aiConfigs.length;i++){var c=aiConfigs[i];var highlight=c.risk==='critical'||c.risk==='high';var rowStyle=highlight?'background:rgba(239,68,68,0.04)':'';h+='<tr style="'+rowStyle+'"><td>'+esc(c.file)+'</td><td>'+esc(c.tool)+'</td><td><span class="sev-badge sev-'+esc(c.risk)+'">'+esc(c.risk)+'</span></td><td style="font-size:12px;color:var(--muted)">'+esc(c.details)+'</td></tr>';}h+='</tbody></table></div>';}var findings=data.findings||[];if(findings.length>0){h+='<h2 class="section-title">Findings</h2>';for(var i=0;i<findings.length;i++){var f=findings[i];h+='<div class="card" style="margin-bottom:8px"><div style="display:flex;align-items:center;gap:8px;margin-bottom:8px"><span class="sev-badge sev-'+esc(f.severity)+'">'+esc(f.severity)+'</span><span style="font-size:14px;font-weight:600">'+esc(f.title)+'</span></div><p style="color:var(--muted);font-size:13px;line-height:1.6;margin-bottom:10px">'+esc(f.whyItMatters)+'</p>'+cmdBlock(f.remediation)+'</div>';}}if(agents.length===0&&mcpServers.length===0&&aiConfigs.length===0&&findings.length===0){h+='<div class="card"><div class="empty-state">No shadow AI detected. Your project has full governance coverage.</div></div>';}return h;}renderPage('overview');})();
590
158
  </script>
591
159
  </body>
592
160
  </html>`;
@@ -1 +1 @@
1
- {"version":3,"file":"review-html.js","sourceRoot":"","sources":["../../src/report/review-html.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;AAUH,gDA2jBC;AAjkBD,SAAS,GAAG,CAAC,CAA4B;IACvC,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClB,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACxE,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACpD,CAAC;AAED,SAAgB,kBAAkB,CAAC,MAAoB;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhE,OAAO;;;;;mCAK0B,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0FAyHmB,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,SAAS,CAAC;;+BAEtG,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC;;;;;;;;;;;;;;;;;;;mDAmBD,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAoanD,CAAC;AACT,CAAC"}
1
+ {"version":3,"file":"review-html.js","sourceRoot":"","sources":["../../src/report/review-html.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;AAUH,gDA2IC;AAjJD,SAAS,GAAG,CAAC,CAA4B;IACvC,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClB,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACxE,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACpD,CAAC;AAED,SAAgB,kBAAkB,CAAC,MAAoB;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhE,OAAO;;;;;mCAK0B,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0FAuGmB,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,SAAS,CAAC;;+BAEtG,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;mDAoBD,QAAQ;;;;;QAKnD,CAAC;AACT,CAAC"}
@@ -21,6 +21,7 @@ export interface ScanFinding {
21
21
  filePath?: string;
22
22
  lineNumber?: number;
23
23
  autoFixable?: boolean;
24
+ attackClass?: string;
24
25
  }
25
26
  export interface ScanReport {
26
27
  /** Package name being scanned */