aura-security 0.4.9 → 0.5.1

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.
package/dist/cli.js CHANGED
@@ -24,7 +24,7 @@ import { existsSync, writeFileSync, mkdirSync } from 'fs';
24
24
  import { join, resolve, basename } from 'path';
25
25
  import { spawnSync } from 'child_process';
26
26
  const AURA_URL = process.env.AURA_URL ?? 'http://127.0.0.1:3000';
27
- const VERSION = '0.4.8';
27
+ const VERSION = '0.5.1';
28
28
  // ANSI colors for terminal output
29
29
  const colors = {
30
30
  reset: '\x1b[0m',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aura-security",
3
- "version": "0.4.9",
3
+ "version": "0.5.1",
4
4
  "description": "Deterministic security auditing engine with optional AI advisory layer. Run as CLI, CI step, or service. AI does not make enforcement decisions.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -349,6 +349,103 @@
349
349
  margin: 0;
350
350
  }
351
351
 
352
+ /* Screenshot Comparison */
353
+ .screenshot-comparison {
354
+ display: grid;
355
+ grid-template-columns: 1fr 1fr;
356
+ gap: 2rem;
357
+ margin: 2rem 0;
358
+ }
359
+
360
+ .screenshot-card {
361
+ background: var(--bg-card);
362
+ border-radius: 16px;
363
+ overflow: hidden;
364
+ border: 1px solid var(--border);
365
+ }
366
+
367
+ .screenshot-label {
368
+ padding: 1rem 1.25rem;
369
+ display: flex;
370
+ align-items: center;
371
+ gap: 0.75rem;
372
+ font-weight: 600;
373
+ font-size: 0.9375rem;
374
+ }
375
+
376
+ .screenshot-card.bad .screenshot-label {
377
+ background: rgba(239, 68, 68, 0.1);
378
+ border-bottom: 2px solid var(--critical);
379
+ color: var(--critical);
380
+ }
381
+
382
+ .screenshot-card.good .screenshot-label {
383
+ background: rgba(34, 197, 94, 0.1);
384
+ border-bottom: 2px solid var(--success);
385
+ color: var(--success);
386
+ }
387
+
388
+ .screenshot-icon {
389
+ font-size: 1.25rem;
390
+ }
391
+
392
+ .screenshot-container {
393
+ position: relative;
394
+ background: #0d1117;
395
+ min-height: 280px;
396
+ }
397
+
398
+ .screenshot-container img {
399
+ width: 100%;
400
+ height: auto;
401
+ display: block;
402
+ }
403
+
404
+ .screenshot-placeholder {
405
+ display: flex;
406
+ flex-direction: column;
407
+ align-items: center;
408
+ justify-content: center;
409
+ height: 280px;
410
+ text-align: center;
411
+ padding: 2rem;
412
+ }
413
+
414
+ .screenshot-placeholder.bad {
415
+ background: linear-gradient(135deg, rgba(239, 68, 68, 0.1), rgba(249, 115, 22, 0.1));
416
+ }
417
+
418
+ .screenshot-placeholder.good {
419
+ background: linear-gradient(135deg, rgba(34, 197, 94, 0.1), rgba(6, 182, 212, 0.1));
420
+ }
421
+
422
+ .placeholder-icon {
423
+ font-size: 4rem;
424
+ margin-bottom: 1rem;
425
+ opacity: 0.8;
426
+ }
427
+
428
+ .placeholder-text {
429
+ color: var(--text-secondary);
430
+ font-size: 1rem;
431
+ line-height: 1.5;
432
+ }
433
+
434
+ .placeholder-text small {
435
+ opacity: 0.7;
436
+ }
437
+
438
+ .screenshot-caption {
439
+ padding: 1rem 1.25rem;
440
+ font-size: 0.8125rem;
441
+ color: var(--text-secondary);
442
+ border-top: 1px solid var(--border);
443
+ }
444
+
445
+ .screenshot-caption strong {
446
+ color: var(--text);
447
+ }
448
+
352
449
  /* Comparison */
353
450
  .comparison {
354
451
  display: grid;
@@ -555,6 +652,9 @@
555
652
  .comparison {
556
653
  grid-template-columns: 1fr;
557
654
  }
655
+ .screenshot-comparison {
656
+ grid-template-columns: 1fr;
657
+ }
558
658
  .slop-grid {
559
659
  grid-template-columns: 1fr;
560
660
  }
@@ -797,6 +897,47 @@
797
897
  <h2>Good vs Bad: Real Examples</h2>
798
898
  <p>Here's what secure and insecure repositories look like when scanned with aurasecurity:</p>
799
899
 
900
+ <!-- 3D Visualizer Screenshots -->
901
+ <h3>3D Visualization Comparison</h3>
902
+ <p>See the difference at a glance - red means danger, green means safe:</p>
903
+
904
+ <div class="screenshot-comparison">
905
+ <div class="screenshot-card bad">
906
+ <div class="screenshot-label">
907
+ <span class="screenshot-icon bad">&#10060;</span>
908
+ <span>Vulnerable Repository</span>
909
+ </div>
910
+ <div class="screenshot-container">
911
+ <img src="https://app.aurasecurity.io/screenshots/bad-repo.png" alt="3D view of vulnerable repository with red nodes" onerror="this.parentElement.innerHTML='<div class=\'screenshot-placeholder bad\'><div class=\'placeholder-icon\'>&#128308;</div><div class=\'placeholder-text\'>juice-shop scan result<br><small>Red node = 9 secrets found</small></div></div>'">
912
+ </div>
913
+ <div class="screenshot-caption">
914
+ <strong>juice-shop</strong> - Multiple red severity indicators orbiting the node. Each red sphere represents a critical or high finding.
915
+ </div>
916
+ </div>
917
+
918
+ <div class="screenshot-card good">
919
+ <div class="screenshot-label">
920
+ <span class="screenshot-icon good">&#9989;</span>
921
+ <span>Clean Repository</span>
922
+ </div>
923
+ <div class="screenshot-container">
924
+ <img src="https://app.aurasecurity.io/screenshots/good-repo.png" alt="3D view of clean repository with green node" onerror="this.parentElement.innerHTML='<div class=\'screenshot-placeholder good\'><div class=\'placeholder-icon\'>&#128994;</div><div class=\'placeholder-text\'>aurasecurity scan result<br><small>Green node = 0 issues</small></div></div>'">
925
+ </div>
926
+ <div class="screenshot-caption">
927
+ <strong>aurasecurity</strong> - Clean green node with no severity indicators. This is your target state.
928
+ </div>
929
+ </div>
930
+ </div>
931
+
932
+ <div class="info-box">
933
+ <h5>&#127912; Reading the 3D View</h5>
934
+ <p><strong>Node Color:</strong> Red = critical issues, Orange = high, Yellow = medium, Green = clean<br>
935
+ <strong>Orbiting Shapes:</strong> Each shape around a node represents a finding category. More shapes = more issues.<br>
936
+ <strong>Click to Drill Down:</strong> Click any node to see severity breakdown, click severity to see individual findings.</p>
937
+ </div>
938
+
939
+ <!-- Stats Comparison Cards -->
940
+ <h3>Scan Statistics</h3>
800
941
  <div class="comparison">
801
942
  <div class="comparison-card bad">
802
943
  <div class="comparison-header">
@@ -815,7 +956,7 @@
815
956
  </div>
816
957
  <div class="comparison-stat">
817
958
  <span class="stat-label">High Findings</span>
818
- <span class="stat-value high">4 (API Keys)</span>
959
+ <span class="stat-value high">8 (API Keys)</span>
819
960
  </div>
820
961
  <div class="comparison-stat">
821
962
  <span class="stat-label">Medium Findings</span>
@@ -827,7 +968,7 @@
827
968
  </div>
828
969
  <div class="comparison-stat">
829
970
  <span class="stat-label">Total Issues</span>
830
- <span class="stat-value critical">20</span>
971
+ <span class="stat-value critical">24</span>
831
972
  </div>
832
973
  </div>
833
974
  </div>
@@ -835,13 +976,13 @@
835
976
  <div class="comparison-card good">
836
977
  <div class="comparison-header">
837
978
  <span>&#9989;</span>
838
- <span>get-shit-done (Clean)</span>
979
+ <span>aurasecurity (Clean)</span>
839
980
  </div>
840
981
  <div class="comparison-body">
841
- <p style="font-size: 0.875rem; margin-bottom: 1rem;">A simple productivity tool with no security findings.</p>
982
+ <p style="font-size: 0.875rem; margin-bottom: 1rem;">Our own repository - we practice what we preach.</p>
842
983
  <div class="comparison-stat">
843
984
  <span class="stat-label">Scan Time</span>
844
- <span class="stat-value">2.87s</span>
985
+ <span class="stat-value">4.10s</span>
845
986
  </div>
846
987
  <div class="comparison-stat">
847
988
  <span class="stat-label">Critical Findings</span>
@@ -436,13 +436,6 @@
436
436
  overflow-y: auto;
437
437
  }
438
438
 
439
- /* Mobile: hide details panel */
440
- @media (max-width: 768px) {
441
- #details-panel {
442
- display: none;
443
- }
444
- }
445
-
446
439
  .selected-repo-header {
447
440
  display: none;
448
441
  padding: 12px;
@@ -584,6 +577,152 @@
584
577
  color: var(--text-primary);
585
578
  }
586
579
 
580
+ /* Filter toggles */
581
+ .filter-section {
582
+ margin-bottom: 16px;
583
+ padding-bottom: 12px;
584
+ border-bottom: 1px solid var(--border);
585
+ }
586
+
587
+ .filter-label {
588
+ font-size: 11px;
589
+ color: var(--text-muted);
590
+ margin-bottom: 8px;
591
+ text-transform: uppercase;
592
+ letter-spacing: 0.5px;
593
+ }
594
+
595
+ .filter-toggles {
596
+ display: flex;
597
+ flex-wrap: wrap;
598
+ gap: 6px;
599
+ }
600
+
601
+ .filter-toggle {
602
+ display: flex;
603
+ align-items: center;
604
+ gap: 4px;
605
+ padding: 4px 10px;
606
+ background: var(--bg-primary);
607
+ border: 1px solid var(--border);
608
+ border-radius: 4px;
609
+ font-size: 11px;
610
+ font-family: inherit;
611
+ color: var(--text-secondary);
612
+ cursor: pointer;
613
+ transition: all 0.15s;
614
+ }
615
+
616
+ .filter-toggle:hover {
617
+ border-color: var(--border-light);
618
+ color: var(--text-primary);
619
+ }
620
+
621
+ .filter-toggle.active {
622
+ background: var(--accent-subtle);
623
+ border-color: var(--accent);
624
+ color: var(--accent);
625
+ }
626
+
627
+ .filter-toggle.active.critical {
628
+ background: var(--critical-subtle);
629
+ border-color: var(--critical);
630
+ color: var(--critical);
631
+ }
632
+
633
+ .filter-toggle.active.high {
634
+ background: var(--high-subtle);
635
+ border-color: var(--high);
636
+ color: var(--high);
637
+ }
638
+
639
+ .filter-toggle.active.medium {
640
+ background: var(--warning-subtle);
641
+ border-color: var(--warning);
642
+ color: var(--warning);
643
+ }
644
+
645
+ .filter-toggle.active.low {
646
+ background: var(--success-subtle);
647
+ border-color: var(--success);
648
+ color: var(--success);
649
+ }
650
+
651
+ .filter-toggle .toggle-dot {
652
+ width: 6px;
653
+ height: 6px;
654
+ border-radius: 50%;
655
+ background: currentColor;
656
+ opacity: 0.6;
657
+ }
658
+
659
+ .filter-toggle.active .toggle-dot {
660
+ opacity: 1;
661
+ }
662
+
663
+ .filter-toggle .toggle-count {
664
+ font-weight: 600;
665
+ margin-left: 2px;
666
+ }
667
+
668
+ /* Badge generator */
669
+ .badge-section {
670
+ margin-top: 16px;
671
+ padding-top: 12px;
672
+ border-top: 1px solid var(--border);
673
+ }
674
+
675
+ .badge-preview {
676
+ display: flex;
677
+ align-items: center;
678
+ gap: 8px;
679
+ margin-bottom: 12px;
680
+ padding: 12px;
681
+ background: var(--bg-primary);
682
+ border-radius: 6px;
683
+ justify-content: center;
684
+ }
685
+
686
+ .badge-preview img {
687
+ height: 20px;
688
+ }
689
+
690
+ .badge-code {
691
+ background: var(--bg-primary);
692
+ border: 1px solid var(--border);
693
+ border-radius: 6px;
694
+ padding: 10px;
695
+ font-family: 'SF Mono', Monaco, monospace;
696
+ font-size: 11px;
697
+ color: var(--text-secondary);
698
+ word-break: break-all;
699
+ margin-bottom: 8px;
700
+ }
701
+
702
+ .badge-actions {
703
+ display: flex;
704
+ gap: 6px;
705
+ }
706
+
707
+ .badge-actions .btn {
708
+ width: auto;
709
+ padding: 6px 12px;
710
+ font-size: 11px;
711
+ margin-top: 0;
712
+ }
713
+
714
+ .copy-success {
715
+ color: var(--success);
716
+ font-size: 11px;
717
+ margin-left: 8px;
718
+ opacity: 0;
719
+ transition: opacity 0.2s;
720
+ }
721
+
722
+ .copy-success.visible {
723
+ opacity: 1;
724
+ }
725
+
587
726
  /* Empty state */
588
727
  .empty-state {
589
728
  text-align: center;
@@ -729,12 +868,63 @@
729
868
  <div class="selected-repo-path" id="selectedRepoPath">-</div>
730
869
  </div>
731
870
 
871
+ <!-- Filter Toggles -->
872
+ <div class="filter-section" id="filterSection" style="display: none;">
873
+ <div class="filter-label">Filter by Severity</div>
874
+ <div class="filter-toggles" id="severityFilters">
875
+ <button class="filter-toggle active critical" data-filter="critical" onclick="toggleFilter('severity', 'critical')">
876
+ <span class="toggle-dot"></span>Critical <span class="toggle-count" id="countCritical">0</span>
877
+ </button>
878
+ <button class="filter-toggle active high" data-filter="high" onclick="toggleFilter('severity', 'high')">
879
+ <span class="toggle-dot"></span>High <span class="toggle-count" id="countHigh">0</span>
880
+ </button>
881
+ <button class="filter-toggle active medium" data-filter="medium" onclick="toggleFilter('severity', 'medium')">
882
+ <span class="toggle-dot"></span>Medium <span class="toggle-count" id="countMedium">0</span>
883
+ </button>
884
+ <button class="filter-toggle active low" data-filter="low" onclick="toggleFilter('severity', 'low')">
885
+ <span class="toggle-dot"></span>Low <span class="toggle-count" id="countLow">0</span>
886
+ </button>
887
+ </div>
888
+ <div class="filter-label" style="margin-top: 12px;">Filter by Type</div>
889
+ <div class="filter-toggles" id="typeFilters">
890
+ <button class="filter-toggle active" data-filter="SECRET" onclick="toggleFilter('type', 'SECRET')">
891
+ <span class="toggle-dot"></span>Secrets
892
+ </button>
893
+ <button class="filter-toggle active" data-filter="VULN" onclick="toggleFilter('type', 'VULN')">
894
+ <span class="toggle-dot"></span>Vulns
895
+ </button>
896
+ <button class="filter-toggle active" data-filter="CODE" onclick="toggleFilter('type', 'CODE')">
897
+ <span class="toggle-dot"></span>Code
898
+ </button>
899
+ <button class="filter-toggle active" data-filter="IAC" onclick="toggleFilter('type', 'IAC')">
900
+ <span class="toggle-dot"></span>IaC
901
+ </button>
902
+ <button class="filter-toggle active" data-filter="DOCKER" onclick="toggleFilter('type', 'DOCKER')">
903
+ <span class="toggle-dot"></span>Docker
904
+ </button>
905
+ </div>
906
+ </div>
907
+
732
908
  <div class="finding-list" id="findingList">
733
909
  <div class="empty-state">
734
910
  <div class="empty-state-icon">🔍</div>
735
911
  <p>Select a target to view findings</p>
736
912
  </div>
737
913
  </div>
914
+
915
+ <!-- Badge Generator -->
916
+ <div class="badge-section" id="badgeSection" style="display: none;">
917
+ <div class="filter-label">Security Badge</div>
918
+ <div class="badge-preview" id="badgePreview">
919
+ <img id="badgeImg" src="" alt="Security Badge">
920
+ </div>
921
+ <div class="badge-code" id="badgeCode">Select a repo to generate badge</div>
922
+ <div class="badge-actions">
923
+ <button class="btn btn-secondary" onclick="copyBadge('markdown')">Copy Markdown</button>
924
+ <button class="btn btn-secondary" onclick="copyBadge('html')">Copy HTML</button>
925
+ <span class="copy-success" id="copySuccess">Copied!</span>
926
+ </div>
927
+ </div>
738
928
  </div>
739
929
 
740
930
  <!-- Reports Panel (Center, hidden by default) -->
@@ -1659,6 +1849,113 @@
1659
1849
  }
1660
1850
  window.selectRepo = selectRepo;
1661
1851
 
1852
+ // ========== FILTER TOGGLES ==========
1853
+ const filters = {
1854
+ severity: { critical: true, high: true, medium: true, low: true },
1855
+ type: { SECRET: true, VULN: true, CODE: true, IAC: true, DOCKER: true }
1856
+ };
1857
+
1858
+ function toggleFilter(category, value) {
1859
+ filters[category][value] = !filters[category][value];
1860
+
1861
+ // Update button state
1862
+ const btn = document.querySelector(`.filter-toggle[data-filter="${value}"]`);
1863
+ if (btn) {
1864
+ btn.classList.toggle('active', filters[category][value]);
1865
+ }
1866
+
1867
+ // Re-render findings list with filters
1868
+ if (state.selectedRepo) {
1869
+ const repo = state.repos.find(r => r.id === state.selectedRepo);
1870
+ if (repo) {
1871
+ updateFindingsList(repo);
1872
+ }
1873
+ }
1874
+ }
1875
+ window.toggleFilter = toggleFilter;
1876
+
1877
+ function updateFilterCounts(repo) {
1878
+ const counts = { critical: 0, high: 0, medium: 0, low: 0 };
1879
+ if (repo && repo.findings) {
1880
+ repo.findings.forEach(f => {
1881
+ const sev = f.severity?.toLowerCase() || 'medium';
1882
+ if (counts[sev] !== undefined) counts[sev]++;
1883
+ });
1884
+ }
1885
+
1886
+ document.getElementById('countCritical').textContent = counts.critical;
1887
+ document.getElementById('countHigh').textContent = counts.high;
1888
+ document.getElementById('countMedium').textContent = counts.medium;
1889
+ document.getElementById('countLow').textContent = counts.low;
1890
+ }
1891
+
1892
+ // ========== BADGE GENERATOR ==========
1893
+ function generateBadge(repo) {
1894
+ if (!repo) return;
1895
+
1896
+ const secrets = repo.secrets || 0;
1897
+ const vulns = repo.vulns || 0;
1898
+
1899
+ // Determine status and color
1900
+ let status, color;
1901
+ if (secrets > 0) {
1902
+ status = `${secrets} secrets`;
1903
+ color = 'critical';
1904
+ } else if (vulns > 10) {
1905
+ status = `${vulns} vulns`;
1906
+ color = 'important';
1907
+ } else if (vulns > 0) {
1908
+ status = `${vulns} vulns`;
1909
+ color = 'yellow';
1910
+ } else {
1911
+ status = 'passing';
1912
+ color = 'success';
1913
+ }
1914
+
1915
+ // Generate shields.io URL
1916
+ const label = 'security';
1917
+ const encodedStatus = encodeURIComponent(status);
1918
+ const badgeUrl = `https://img.shields.io/badge/${label}-${encodedStatus}-${color}?style=flat-square&logo=shield&logoColor=white`;
1919
+
1920
+ // Update preview
1921
+ const badgeImg = document.getElementById('badgeImg');
1922
+ badgeImg.src = badgeUrl;
1923
+ badgeImg.alt = `Security: ${status}`;
1924
+
1925
+ // Store for copy functions
1926
+ window.currentBadge = {
1927
+ url: badgeUrl,
1928
+ label: label,
1929
+ status: status,
1930
+ repoName: repo.name
1931
+ };
1932
+
1933
+ // Update code preview with markdown
1934
+ const markdown = `[![Security](${badgeUrl})](https://aurasecurity.io)`;
1935
+ document.getElementById('badgeCode').textContent = markdown;
1936
+
1937
+ // Show badge section
1938
+ document.getElementById('badgeSection').style.display = 'block';
1939
+ }
1940
+
1941
+ function copyBadge(format) {
1942
+ if (!window.currentBadge) return;
1943
+
1944
+ let text;
1945
+ if (format === 'markdown') {
1946
+ text = `[![Security](${window.currentBadge.url})](https://aurasecurity.io)`;
1947
+ } else {
1948
+ text = `<a href="https://aurasecurity.io"><img src="${window.currentBadge.url}" alt="Security"></a>`;
1949
+ }
1950
+
1951
+ navigator.clipboard.writeText(text).then(() => {
1952
+ const successEl = document.getElementById('copySuccess');
1953
+ successEl.classList.add('visible');
1954
+ setTimeout(() => successEl.classList.remove('visible'), 2000);
1955
+ });
1956
+ }
1957
+ window.copyBadge = copyBadge;
1958
+
1662
1959
  function updateUI() {
1663
1960
  let totalCrit = 0, totalWarn = 0;
1664
1961
  state.repos.forEach(r => {
@@ -1702,7 +1999,17 @@
1702
1999
  function updateFindingsList(repo) {
1703
2000
  const container = document.getElementById('findingList');
1704
2001
 
2002
+ // Show/hide filter section
2003
+ document.getElementById('filterSection').style.display = repo && repo.findings?.length > 0 ? 'block' : 'none';
2004
+
2005
+ // Update filter counts
2006
+ updateFilterCounts(repo);
2007
+
2008
+ // Generate badge
2009
+ generateBadge(repo);
2010
+
1705
2011
  if (!repo || repo.findings.length === 0) {
2012
+ document.getElementById('badgeSection').style.display = 'none';
1706
2013
  container.innerHTML = `
1707
2014
  <div class="empty-state">
1708
2015
  <div class="empty-state-icon">✓</div>
@@ -1712,7 +2019,24 @@
1712
2019
  return;
1713
2020
  }
1714
2021
 
1715
- container.innerHTML = repo.findings.map(f => `
2022
+ // Apply filters
2023
+ const filtered = repo.findings.filter(f => {
2024
+ const sev = f.severity?.toLowerCase() || 'medium';
2025
+ const type = f.type || 'CODE';
2026
+ return filters.severity[sev] && filters.type[type];
2027
+ });
2028
+
2029
+ if (filtered.length === 0) {
2030
+ container.innerHTML = `
2031
+ <div class="empty-state">
2032
+ <div class="empty-state-icon">🔍</div>
2033
+ <p>No findings match current filters</p>
2034
+ </div>
2035
+ `;
2036
+ return;
2037
+ }
2038
+
2039
+ container.innerHTML = filtered.map(f => `
1716
2040
  <div class="finding-item ${f.severity}">
1717
2041
  <div class="finding-header">
1718
2042
  <span class="finding-severity">${f.severity}</span>
@@ -421,49 +421,6 @@
421
421
  box-shadow: 0 0 30px #00ff88;
422
422
  }
423
423
 
424
- /* Filter Toggles */
425
- .filter-toggle {
426
- display: flex;
427
- align-items: center;
428
- gap: 4px;
429
- padding: 4px 8px;
430
- background: rgba(0, 20, 10, 0.6);
431
- border: 1px solid #00ff8844;
432
- border-radius: 4px;
433
- font-size: 10px;
434
- cursor: pointer;
435
- transition: all 0.2s;
436
- user-select: none;
437
- }
438
-
439
- .filter-toggle input {
440
- display: none;
441
- }
442
-
443
- .filter-toggle .filter-icon {
444
- width: 14px;
445
- height: 14px;
446
- border-radius: 3px;
447
- display: flex;
448
- align-items: center;
449
- justify-content: center;
450
- font-size: 8px;
451
- }
452
-
453
- .filter-toggle.active {
454
- border-color: #00ff88;
455
- background: rgba(0, 255, 136, 0.1);
456
- }
457
-
458
- .filter-toggle:not(.active) {
459
- opacity: 0.5;
460
- border-color: #333;
461
- }
462
-
463
- .filter-toggle:hover {
464
- border-color: #00ff88;
465
- }
466
-
467
424
  /* Scrollbar */
468
425
  ::-webkit-scrollbar { width: 6px; }
469
426
  ::-webkit-scrollbar-track { background: #0a0a0f; }
@@ -658,32 +615,6 @@
658
615
 
659
616
  <div class="panel">
660
617
  <div class="panel-title">Security Findings</div>
661
- <!-- Filter Toggles -->
662
- <div id="findingsFilter" style="margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid #00ff8833;">
663
- <div style="font-size: 9px; color: #666; margin-bottom: 6px; text-transform: uppercase; letter-spacing: 1px;">Filter by Type:</div>
664
- <div style="display: flex; flex-wrap: wrap; gap: 6px;">
665
- <label class="filter-toggle active" data-filter="secrets">
666
- <input type="checkbox" checked onchange="toggleFilter('secrets', this.checked)">
667
- <span class="filter-icon" style="background: #ff0088;">🔑</span>
668
- <span>Secrets</span>
669
- </label>
670
- <label class="filter-toggle active" data-filter="cves">
671
- <input type="checkbox" checked onchange="toggleFilter('cves', this.checked)">
672
- <span class="filter-icon" style="background: #ff4400;">📦</span>
673
- <span>CVEs</span>
674
- </label>
675
- <label class="filter-toggle active" data-filter="dockerfile">
676
- <input type="checkbox" checked onchange="toggleFilter('dockerfile', this.checked)">
677
- <span class="filter-icon" style="background: #00aaff;">🐳</span>
678
- <span>Docker</span>
679
- </label>
680
- <label class="filter-toggle active" data-filter="sast">
681
- <input type="checkbox" checked onchange="toggleFilter('sast', this.checked)">
682
- <span class="filter-icon" style="background: #ffaa00;">🔬</span>
683
- <span>SAST</span>
684
- </label>
685
- </div>
686
- </div>
687
618
  <div id="findingsList">
688
619
  <p style="color: #666; font-size: 11px;">No findings yet. Run an audit to detect issues.</p>
689
620
  </div>
@@ -694,34 +625,6 @@
694
625
  <div class="panel-title">Settings <span id="settingsStatus" style="color: #666; font-size: 10px;"></span></div>
695
626
  <button class="btn secondary" style="width: 100%;" onclick="openSettingsModal()">Configure Integrations</button>
696
627
  </div>
697
-
698
- <!-- Badge Generator Panel -->
699
- <div class="panel">
700
- <div class="panel-title">Badge Generator</div>
701
- <p style="font-size: 10px; color: #666; margin-bottom: 8px;">Generate shields for your README</p>
702
- <div id="badgePreview" style="margin-bottom: 10px; text-align: center; padding: 10px; background: rgba(0,0,0,0.3); border-radius: 4px;">
703
- <img id="badgeImg" src="https://img.shields.io/badge/security-scanning-00ff88?style=flat-square&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0xMiAxTDMgNXY2YzAgNS41NSAzLjg0IDEwLjc0IDkgMTIgNS4xNi0xLjI2IDktNi40NSA5LTEyVjVsLTktNHoiLz48L3N2Zz4=" alt="security badge" style="height: 20px;">
704
- </div>
705
- <div style="margin-bottom: 8px;">
706
- <select id="badgeStyle" style="width: 100%; padding: 6px; background: rgba(0,20,10,0.8); border: 1px solid #00ff88; color: #00ff88; font-size: 11px;" onchange="updateBadgePreview()">
707
- <option value="flat-square">Flat Square</option>
708
- <option value="flat">Flat</option>
709
- <option value="plastic">Plastic</option>
710
- <option value="for-the-badge">For The Badge</option>
711
- </select>
712
- </div>
713
- <div style="margin-bottom: 8px;">
714
- <select id="badgeType" style="width: 100%; padding: 6px; background: rgba(0,20,10,0.8); border: 1px solid #00ff88; color: #00ff88; font-size: 11px;" onchange="updateBadgePreview()">
715
- <option value="status">Scan Status</option>
716
- <option value="critical">Critical Count</option>
717
- <option value="total">Total Findings</option>
718
- </select>
719
- </div>
720
- <div id="badgeMarkdown" style="background: rgba(0,0,0,0.4); padding: 8px; border-radius: 4px; font-family: monospace; font-size: 9px; color: #00ff88; word-break: break-all; margin-bottom: 8px; max-height: 60px; overflow-y: auto;">
721
- ![aurasecurity](https://img.shields.io/badge/security-scanning-00ff88?style=flat-square)
722
- </div>
723
- <button class="btn secondary" style="width: 100%; font-size: 10px;" onclick="copyBadgeMarkdown()">📋 Copy Markdown</button>
724
- </div>
725
628
  </div>
726
629
 
727
630
  <!-- Settings Modal -->
@@ -1048,71 +951,6 @@
1048
951
  blocked: 0xff0044
1049
952
  };
1050
953
 
1051
- // ========== FINDINGS FILTER STATE ==========
1052
- const findingsFilter = {
1053
- secrets: true,
1054
- cves: true,
1055
- dockerfile: true,
1056
- sast: true
1057
- };
1058
-
1059
- // Toggle filter and re-render findings
1060
- window.toggleFilter = function(filterType, enabled) {
1061
- findingsFilter[filterType] = enabled;
1062
- const toggle = document.querySelector(`.filter-toggle[data-filter="${filterType}"]`);
1063
- if (toggle) {
1064
- toggle.classList.toggle('active', enabled);
1065
- }
1066
- // Re-render with current findings
1067
- if (typeof renderFindingsList === 'function') {
1068
- renderFindingsList(currentFindings);
1069
- }
1070
- // Also re-render scan findings if we have them
1071
- if (typeof renderScanFindings === 'function' && currentScanFindings) {
1072
- renderScanFindings(currentScanFindings);
1073
- }
1074
- };
1075
-
1076
- // Determine finding type from finding data
1077
- function getFindingType(finding) {
1078
- // Check payload for audit events
1079
- if (finding.payload) {
1080
- const claim = (finding.payload.claim || '').toLowerCase();
1081
- const assets = (finding.payload.affected_assets || []).join(' ').toLowerCase();
1082
-
1083
- if (claim.includes('secret') || claim.includes('credential') || claim.includes('key') || claim.includes('password') || assets.includes('secrets')) {
1084
- return 'secrets';
1085
- }
1086
- if (claim.includes('vulnerability') || claim.includes('cve') || claim.includes('package') || claim.includes('dependency')) {
1087
- return 'cves';
1088
- }
1089
- if (claim.includes('docker') || claim.includes('container') || claim.includes('dockerfile')) {
1090
- return 'dockerfile';
1091
- }
1092
- if (claim.includes('injection') || claim.includes('xss') || claim.includes('sast') || claim.includes('code')) {
1093
- return 'sast';
1094
- }
1095
- }
1096
- // Check direct finding type
1097
- if (finding.type) {
1098
- const type = finding.type.toLowerCase();
1099
- if (type.includes('secret') || type.includes('key') || type.includes('credential')) return 'secrets';
1100
- if (type.includes('vuln') || type.includes('cve') || type.includes('package')) return 'cves';
1101
- if (type.includes('docker') || type.includes('hadolint')) return 'dockerfile';
1102
- if (type.includes('sast') || type.includes('semgrep')) return 'sast';
1103
- }
1104
- return 'sast'; // Default to SAST for unknown types
1105
- }
1106
-
1107
- // Check if finding passes current filters
1108
- function findingPassesFilter(finding) {
1109
- const type = getFindingType(finding);
1110
- return findingsFilter[type] === true;
1111
- }
1112
-
1113
- // Store current scan findings for filtering
1114
- let currentScanFindings = null;
1115
-
1116
954
  // Clear the map - remove all modules and connections to start fresh
1117
955
  function clearMap() {
1118
956
  console.log('Clearing map for fresh scan...');
@@ -1537,24 +1375,11 @@
1537
1375
  return;
1538
1376
  }
1539
1377
 
1540
- // Apply filters
1541
- const filteredFindings = findings.filter(f => findingPassesFilter(f));
1542
-
1543
- if (filteredFindings.length === 0) {
1544
- container.innerHTML = '<p style="color: #666; font-size: 11px;">No findings match current filters. Adjust filters above.</p>';
1545
- return;
1546
- }
1547
-
1548
- container.innerHTML = filteredFindings.map(f => {
1549
- const findingType = getFindingType(f);
1550
- const typeIcons = { secrets: '🔑', cves: '📦', dockerfile: '🐳', sast: '🔬' };
1551
- const typeIcon = typeIcons[findingType] || '⚠️';
1552
-
1553
- return `
1554
- <div class="finding ${f.payload.severity}" data-type="${findingType}">
1378
+ container.innerHTML = findings.map(f => `
1379
+ <div class="finding ${f.payload.severity}">
1555
1380
  <div class="finding-header">
1556
1381
  <span class="severity-badge ${f.payload.severity}">${f.payload.severity.toUpperCase()}</span>
1557
- <span class="finding-module">${typeIcon} ${f.payload.affected_assets[0] || 'system'}</span>
1382
+ <span class="finding-module">${f.payload.affected_assets[0] || 'system'}</span>
1558
1383
  </div>
1559
1384
  <div class="finding-claim">${f.payload.claim}</div>
1560
1385
  <div class="finding-details">
@@ -1564,7 +1389,7 @@
1564
1389
  <strong>Confidence:</strong> ${(f.payload.confidence * 100).toFixed(0)}%
1565
1390
  </div>
1566
1391
  </div>
1567
- `}).join('');
1392
+ `).join('');
1568
1393
  }
1569
1394
 
1570
1395
  function updateStats(findings) {
@@ -2136,83 +1961,6 @@
2136
1961
  setTimeout(() => toast.remove(), 4000);
2137
1962
  }
2138
1963
 
2139
- // ========== BADGE GENERATOR ==========
2140
- window.updateBadgePreview = function() {
2141
- const style = document.getElementById('badgeStyle').value;
2142
- const type = document.getElementById('badgeType').value;
2143
-
2144
- // Get current stats
2145
- const critical = parseInt(document.getElementById('statCritical').textContent) || 0;
2146
- const high = parseInt(document.getElementById('statHigh').textContent) || 0;
2147
- const medium = parseInt(document.getElementById('statMedium').textContent) || 0;
2148
- const low = parseInt(document.getElementById('statLow').textContent) || 0;
2149
- const total = critical + high + medium + low;
2150
-
2151
- let label, message, color;
2152
-
2153
- if (type === 'status') {
2154
- label = 'security';
2155
- if (critical > 0) {
2156
- message = 'critical';
2157
- color = 'ff0044';
2158
- } else if (high > 0) {
2159
- message = 'issues found';
2160
- color = 'ff4400';
2161
- } else if (total > 0) {
2162
- message = 'warnings';
2163
- color = 'ffaa00';
2164
- } else {
2165
- message = 'passing';
2166
- color = '00ff88';
2167
- }
2168
- } else if (type === 'critical') {
2169
- label = 'critical';
2170
- message = critical.toString();
2171
- color = critical > 0 ? 'ff0044' : '00ff88';
2172
- } else {
2173
- label = 'findings';
2174
- message = total.toString();
2175
- color = total > 5 ? 'ff4400' : total > 0 ? 'ffaa00' : '00ff88';
2176
- }
2177
-
2178
- // Shield logo as base64
2179
- const logo = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0xMiAxTDMgNXY2YzAgNS41NSAzLjg0IDEwLjc0IDkgMTIgNS4xNi0xLjI2IDktNi40NSA5LTEyVjVsLTktNHoiLz48L3N2Zz4=';
2180
-
2181
- const badgeUrl = `https://img.shields.io/badge/${encodeURIComponent(label)}-${encodeURIComponent(message)}-${color}?style=${style}&logo=${encodeURIComponent(logo)}`;
2182
- const markdown = `[![aurasecurity](${badgeUrl})](https://aurasecurity.io)`;
2183
-
2184
- document.getElementById('badgeImg').src = badgeUrl;
2185
- document.getElementById('badgeMarkdown').textContent = markdown;
2186
- };
2187
-
2188
- window.copyBadgeMarkdown = function() {
2189
- const markdown = document.getElementById('badgeMarkdown').textContent;
2190
- navigator.clipboard.writeText(markdown).then(() => {
2191
- showToast('Badge markdown copied!');
2192
- }).catch(() => {
2193
- showToast('Failed to copy', true);
2194
- });
2195
- };
2196
-
2197
- // Update badge when stats change
2198
- const originalUpdateStats = function() {};
2199
- const statsObserver = new MutationObserver(() => {
2200
- if (typeof updateBadgePreview === 'function') {
2201
- updateBadgePreview();
2202
- }
2203
- });
2204
-
2205
- // Start observing stats after DOM is ready
2206
- setTimeout(() => {
2207
- const statElements = ['statCritical', 'statHigh', 'statMedium', 'statLow'];
2208
- statElements.forEach(id => {
2209
- const el = document.getElementById(id);
2210
- if (el) statsObserver.observe(el, { childList: true, characterData: true, subtree: true });
2211
- });
2212
- // Initial badge update
2213
- if (typeof updateBadgePreview === 'function') updateBadgePreview();
2214
- }, 1000);
2215
-
2216
1964
  function onWindowResize() {
2217
1965
  camera.aspect = window.innerWidth / window.innerHeight;
2218
1966
  camera.updateProjectionMatrix();