@wakastellar/ui 2.1.2 → 2.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/blocks/apm-overview/index.d.ts +58 -0
  2. package/dist/blocks/cicd-builder/index.d.ts +47 -0
  3. package/dist/blocks/cloud-cost-dashboard/index.d.ts +49 -0
  4. package/dist/blocks/container-orchestrator/index.d.ts +63 -0
  5. package/dist/blocks/database-admin/index.d.ts +84 -0
  6. package/dist/blocks/gitops-sync-status/index.d.ts +45 -0
  7. package/dist/blocks/incident-manager/index.d.ts +44 -0
  8. package/dist/blocks/index.d.ts +10 -0
  9. package/dist/blocks/infrastructure-map/index.d.ts +32 -0
  10. package/dist/blocks/on-call-schedule/index.d.ts +43 -0
  11. package/dist/blocks/release-notes/index.d.ts +49 -0
  12. package/dist/components/index.d.ts +34 -0
  13. package/dist/components/waka-ad-banner/index.d.ts +36 -0
  14. package/dist/components/waka-ad-fallback/index.d.ts +33 -0
  15. package/dist/components/waka-ad-inline/index.d.ts +15 -0
  16. package/dist/components/waka-ad-interstitial/index.d.ts +26 -0
  17. package/dist/components/waka-ad-placeholder/index.d.ts +17 -0
  18. package/dist/components/waka-ad-provider/index.d.ts +103 -0
  19. package/dist/components/waka-ad-sidebar/index.d.ts +18 -0
  20. package/dist/components/waka-ad-sticky-footer/index.d.ts +17 -0
  21. package/dist/components/waka-alert-panel/index.d.ts +45 -0
  22. package/dist/components/waka-artifact-list/index.d.ts +32 -0
  23. package/dist/components/waka-build-matrix/index.d.ts +36 -0
  24. package/dist/components/waka-config-comparator/index.d.ts +37 -0
  25. package/dist/components/waka-container-list/index.d.ts +51 -0
  26. package/dist/components/waka-content-recommendation/index.d.ts +23 -0
  27. package/dist/components/waka-database-card/index.d.ts +46 -0
  28. package/dist/components/waka-dependency-tree/index.d.ts +38 -0
  29. package/dist/components/waka-env-var-editor/index.d.ts +30 -0
  30. package/dist/components/waka-feature-flag-row/index.d.ts +45 -0
  31. package/dist/components/waka-kubernetes-overview/index.d.ts +98 -0
  32. package/dist/components/waka-log-viewer/index.d.ts +38 -0
  33. package/dist/components/waka-migration-list/index.d.ts +36 -0
  34. package/dist/components/waka-outstream-video/index.d.ts +24 -0
  35. package/dist/components/waka-pod-card/index.d.ts +73 -0
  36. package/dist/components/waka-query-explain/index.d.ts +48 -0
  37. package/dist/components/waka-secret-card/index.d.ts +43 -0
  38. package/dist/components/waka-security-scan-result/index.d.ts +45 -0
  39. package/dist/components/waka-service-graph/index.d.ts +44 -0
  40. package/dist/components/waka-sponsored-badge/index.d.ts +20 -0
  41. package/dist/components/waka-sponsored-card/index.d.ts +25 -0
  42. package/dist/components/waka-sponsored-feed/index.d.ts +31 -0
  43. package/dist/components/waka-test-report/index.d.ts +60 -0
  44. package/dist/components/waka-trace-viewer/index.d.ts +36 -0
  45. package/dist/components/waka-video-ad/index.d.ts +32 -0
  46. package/dist/components/waka-video-overlay/index.d.ts +26 -0
  47. package/dist/index.cjs.js +251 -200
  48. package/dist/index.d.ts +1 -0
  49. package/dist/index.es.js +47315 -35823
  50. package/dist/utils/security.d.ts +96 -0
  51. package/package.json +4 -4
  52. package/src/blocks/apm-overview/index.tsx +672 -0
  53. package/src/blocks/cicd-builder/index.tsx +738 -0
  54. package/src/blocks/cloud-cost-dashboard/index.tsx +597 -0
  55. package/src/blocks/container-orchestrator/index.tsx +729 -0
  56. package/src/blocks/database-admin/index.tsx +679 -0
  57. package/src/blocks/gitops-sync-status/index.tsx +557 -0
  58. package/src/blocks/incident-manager/index.tsx +586 -0
  59. package/src/blocks/index.ts +119 -0
  60. package/src/blocks/infrastructure-map/index.tsx +638 -0
  61. package/src/blocks/on-call-schedule/index.tsx +615 -0
  62. package/src/blocks/release-notes/index.tsx +643 -0
  63. package/src/blocks/sidebar/index.tsx +6 -6
  64. package/src/components/DataTable/templates/index.tsx +3 -2
  65. package/src/components/index.ts +283 -0
  66. package/src/components/waka-3d-pie-chart/index.tsx +11 -11
  67. package/src/components/waka-achievement-unlock/index.tsx +16 -16
  68. package/src/components/waka-ad-banner/index.tsx +275 -0
  69. package/src/components/waka-ad-fallback/index.tsx +181 -0
  70. package/src/components/waka-ad-inline/index.tsx +103 -0
  71. package/src/components/waka-ad-interstitial/index.tsx +278 -0
  72. package/src/components/waka-ad-placeholder/index.tsx +84 -0
  73. package/src/components/waka-ad-provider/index.tsx +329 -0
  74. package/src/components/waka-ad-sidebar/index.tsx +113 -0
  75. package/src/components/waka-ad-sticky-footer/index.tsx +125 -0
  76. package/src/components/waka-alert-panel/index.tsx +493 -0
  77. package/src/components/waka-artifact-list/index.tsx +416 -0
  78. package/src/components/waka-badge-showcase/index.tsx +12 -11
  79. package/src/components/waka-build-matrix/index.tsx +396 -0
  80. package/src/components/waka-command-bar/index.tsx +2 -1
  81. package/src/components/waka-config-comparator/index.tsx +416 -0
  82. package/src/components/waka-container-list/index.tsx +475 -0
  83. package/src/components/waka-content-recommendation/index.tsx +294 -0
  84. package/src/components/waka-cost-breakdown/index.tsx +10 -10
  85. package/src/components/waka-database-card/index.tsx +473 -0
  86. package/src/components/waka-dependency-tree/index.tsx +542 -0
  87. package/src/components/waka-env-var-editor/index.tsx +417 -0
  88. package/src/components/waka-feature-flag-row/index.tsx +386 -0
  89. package/src/components/waka-funnel-chart/index.tsx +8 -8
  90. package/src/components/waka-health-pulse/index.tsx +6 -6
  91. package/src/components/waka-kubernetes-overview/index.tsx +536 -0
  92. package/src/components/waka-leaderboard/index.tsx +9 -9
  93. package/src/components/waka-log-viewer/index.tsx +386 -0
  94. package/src/components/waka-loot-box/index.tsx +20 -20
  95. package/src/components/waka-migration-list/index.tsx +487 -0
  96. package/src/components/waka-outstream-video/index.tsx +240 -0
  97. package/src/components/waka-player-card/index.tsx +5 -5
  98. package/src/components/waka-pod-card/index.tsx +528 -0
  99. package/src/components/waka-query-explain/index.tsx +657 -0
  100. package/src/components/waka-quota-bar/index.tsx +4 -4
  101. package/src/components/waka-radar-score/index.tsx +10 -10
  102. package/src/components/waka-scratch-card/index.tsx +5 -4
  103. package/src/components/waka-secret-card/index.tsx +371 -0
  104. package/src/components/waka-security-scan-result/index.tsx +473 -0
  105. package/src/components/waka-server-rack/index.tsx +28 -27
  106. package/src/components/waka-service-graph/index.tsx +445 -0
  107. package/src/components/waka-sponsored-badge/index.tsx +97 -0
  108. package/src/components/waka-sponsored-card/index.tsx +275 -0
  109. package/src/components/waka-sponsored-feed/index.tsx +127 -0
  110. package/src/components/waka-spotlight/index.tsx +2 -1
  111. package/src/components/waka-success-explosion/index.tsx +4 -4
  112. package/src/components/waka-test-report/index.tsx +469 -0
  113. package/src/components/waka-trace-viewer/index.tsx +490 -0
  114. package/src/components/waka-video-ad/index.tsx +406 -0
  115. package/src/components/waka-video-overlay/index.tsx +257 -0
  116. package/src/components/waka-xp-bar/index.tsx +13 -13
  117. package/src/styles/base.css +16 -0
  118. package/src/styles/tailwind.preset.js +12 -0
  119. package/src/styles/themes/forest.css +16 -0
  120. package/src/styles/themes/monochrome.css +16 -0
  121. package/src/styles/themes/perpetuity.css +16 -0
  122. package/src/styles/themes/sunset.css +16 -0
  123. package/src/styles/themes/twilight.css +16 -0
@@ -0,0 +1,473 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "../../utils/cn"
5
+ import { Badge } from "../badge"
6
+ import { Button } from "../button"
7
+ import { Progress } from "../progress"
8
+ import { ScrollArea } from "../scroll-area"
9
+ import { Input } from "../input"
10
+ import {
11
+ Shield,
12
+ ShieldAlert,
13
+ ShieldCheck,
14
+ AlertTriangle,
15
+ AlertCircle,
16
+ Info,
17
+ Search,
18
+ ExternalLink,
19
+ ChevronDown,
20
+ ChevronRight,
21
+ Package,
22
+ FileCode,
23
+ Link,
24
+ Clock,
25
+ } from "lucide-react"
26
+
27
+ export type VulnerabilitySeverity = "critical" | "high" | "medium" | "low" | "info"
28
+ export type ScanType = "sast" | "dast" | "sca" | "secret" | "container"
29
+
30
+ export interface Vulnerability {
31
+ id: string
32
+ title: string
33
+ description: string
34
+ severity: VulnerabilitySeverity
35
+ type: ScanType
36
+ cveId?: string
37
+ cweId?: string
38
+ cvssScore?: number
39
+ package?: string
40
+ version?: string
41
+ fixedVersion?: string
42
+ file?: string
43
+ line?: number
44
+ url?: string
45
+ remediation?: string
46
+ }
47
+
48
+ export interface ScanSummary {
49
+ critical: number
50
+ high: number
51
+ medium: number
52
+ low: number
53
+ info: number
54
+ total: number
55
+ }
56
+
57
+ export interface WakaSecurityScanResultProps {
58
+ /** List of vulnerabilities */
59
+ vulnerabilities: Vulnerability[]
60
+ /** Scan type */
61
+ scanType?: ScanType | "all"
62
+ /** Scan timestamp */
63
+ scannedAt?: Date
64
+ /** Callback when clicking on a vulnerability */
65
+ onVulnerabilityClick?: (vuln: Vulnerability) => void
66
+ /** Callback when viewing CVE details */
67
+ onViewCVE?: (cveId: string) => void
68
+ /** Title */
69
+ title?: string
70
+ /** Custom class name */
71
+ className?: string
72
+ }
73
+
74
+ const severityConfig: Record<VulnerabilitySeverity, { icon: React.ElementType; color: string; bgColor: string; label: string; score: string }> = {
75
+ critical: { icon: ShieldAlert, color: "text-red-600", bgColor: "bg-red-500", label: "Critical", score: "9.0-10.0" },
76
+ high: { icon: AlertCircle, color: "text-orange-500", bgColor: "bg-orange-500", label: "High", score: "7.0-8.9" },
77
+ medium: { icon: AlertTriangle, color: "text-yellow-500", bgColor: "bg-yellow-500", label: "Medium", score: "4.0-6.9" },
78
+ low: { icon: Info, color: "text-blue-500", bgColor: "bg-blue-500", label: "Low", score: "0.1-3.9" },
79
+ info: { icon: Info, color: "text-gray-500", bgColor: "bg-gray-500", label: "Info", score: "0" },
80
+ }
81
+
82
+ const scanTypeConfig: Record<ScanType, { icon: React.ElementType; label: string; description: string }> = {
83
+ sast: { icon: FileCode, label: "SAST", description: "Static Application Security Testing" },
84
+ dast: { icon: Link, label: "DAST", description: "Dynamic Application Security Testing" },
85
+ sca: { icon: Package, label: "SCA", description: "Software Composition Analysis" },
86
+ secret: { icon: Shield, label: "Secrets", description: "Secret Detection" },
87
+ container: { icon: Package, label: "Container", description: "Container Image Scanning" },
88
+ }
89
+
90
+ function SeverityBadge({ severity }: { severity: VulnerabilitySeverity }) {
91
+ const config = severityConfig[severity]
92
+ return (
93
+ <Badge className={cn("text-white", config.bgColor)}>
94
+ {config.label}
95
+ </Badge>
96
+ )
97
+ }
98
+
99
+ function VulnerabilityCard({
100
+ vulnerability,
101
+ isExpanded,
102
+ onToggle,
103
+ onViewCVE,
104
+ }: {
105
+ vulnerability: Vulnerability
106
+ isExpanded: boolean
107
+ onToggle: () => void
108
+ onViewCVE?: (cveId: string) => void
109
+ }) {
110
+ const sevConfig = severityConfig[vulnerability.severity]
111
+ const typeConfig = scanTypeConfig[vulnerability.type]
112
+ const SevIcon = sevConfig.icon
113
+ const TypeIcon = typeConfig.icon
114
+
115
+ return (
116
+ <div
117
+ className={cn(
118
+ "border rounded-lg overflow-hidden",
119
+ vulnerability.severity === "critical" && "border-red-500/50",
120
+ vulnerability.severity === "high" && "border-orange-500/50"
121
+ )}
122
+ >
123
+ <button
124
+ className={cn(
125
+ "w-full flex items-start gap-3 p-4 text-left hover:bg-muted/50 transition-colors",
126
+ vulnerability.severity === "critical" && "bg-red-500/5",
127
+ vulnerability.severity === "high" && "bg-orange-500/5"
128
+ )}
129
+ onClick={onToggle}
130
+ >
131
+ {isExpanded ? (
132
+ <ChevronDown className="h-4 w-4 mt-1 text-muted-foreground shrink-0" />
133
+ ) : (
134
+ <ChevronRight className="h-4 w-4 mt-1 text-muted-foreground shrink-0" />
135
+ )}
136
+
137
+ <SevIcon className={cn("h-5 w-5 mt-0.5 shrink-0", sevConfig.color)} />
138
+
139
+ <div className="flex-1 min-w-0">
140
+ <div className="flex items-center gap-2 flex-wrap">
141
+ <span className="font-medium">{vulnerability.title}</span>
142
+ <SeverityBadge severity={vulnerability.severity} />
143
+ <Badge variant="outline" className="text-xs">
144
+ <TypeIcon className="h-3 w-3 mr-1" />
145
+ {typeConfig.label}
146
+ </Badge>
147
+ </div>
148
+
149
+ <p className="text-sm text-muted-foreground mt-1 line-clamp-2">
150
+ {vulnerability.description}
151
+ </p>
152
+
153
+ <div className="flex items-center gap-3 mt-2 text-xs text-muted-foreground">
154
+ {vulnerability.cveId && (
155
+ <button
156
+ className="text-primary hover:underline flex items-center gap-1"
157
+ onClick={(e) => {
158
+ e.stopPropagation()
159
+ onViewCVE?.(vulnerability.cveId!)
160
+ }}
161
+ >
162
+ {vulnerability.cveId}
163
+ <ExternalLink className="h-3 w-3" />
164
+ </button>
165
+ )}
166
+ {vulnerability.cvssScore && (
167
+ <span>CVSS: {vulnerability.cvssScore.toFixed(1)}</span>
168
+ )}
169
+ {vulnerability.package && (
170
+ <span className="flex items-center gap-1">
171
+ <Package className="h-3 w-3" />
172
+ {vulnerability.package}@{vulnerability.version}
173
+ </span>
174
+ )}
175
+ {vulnerability.file && (
176
+ <span className="truncate max-w-[200px]">{vulnerability.file}:{vulnerability.line}</span>
177
+ )}
178
+ </div>
179
+ </div>
180
+ </button>
181
+
182
+ {isExpanded && (
183
+ <div className="border-t bg-muted/20 p-4 space-y-4">
184
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
185
+ {vulnerability.cveId && (
186
+ <div>
187
+ <span className="text-muted-foreground">CVE ID:</span>{" "}
188
+ <span className="font-mono">{vulnerability.cveId}</span>
189
+ </div>
190
+ )}
191
+ {vulnerability.cweId && (
192
+ <div>
193
+ <span className="text-muted-foreground">CWE ID:</span>{" "}
194
+ <span className="font-mono">{vulnerability.cweId}</span>
195
+ </div>
196
+ )}
197
+ {vulnerability.cvssScore && (
198
+ <div>
199
+ <span className="text-muted-foreground">CVSS Score:</span>{" "}
200
+ <span className="font-medium">{vulnerability.cvssScore.toFixed(1)}</span>
201
+ </div>
202
+ )}
203
+ {vulnerability.package && (
204
+ <div>
205
+ <span className="text-muted-foreground">Package:</span>{" "}
206
+ <span className="font-mono">{vulnerability.package}@{vulnerability.version}</span>
207
+ </div>
208
+ )}
209
+ {vulnerability.fixedVersion && (
210
+ <div>
211
+ <span className="text-muted-foreground">Fixed in:</span>{" "}
212
+ <span className="font-mono text-green-500">{vulnerability.fixedVersion}</span>
213
+ </div>
214
+ )}
215
+ {vulnerability.file && (
216
+ <div>
217
+ <span className="text-muted-foreground">Location:</span>{" "}
218
+ <span className="font-mono">{vulnerability.file}:{vulnerability.line}</span>
219
+ </div>
220
+ )}
221
+ </div>
222
+
223
+ {vulnerability.remediation && (
224
+ <div>
225
+ <div className="text-sm font-medium mb-1">Remediation</div>
226
+ <p className="text-sm text-muted-foreground">{vulnerability.remediation}</p>
227
+ </div>
228
+ )}
229
+
230
+ {vulnerability.url && (
231
+ <Button
232
+ variant="outline"
233
+ size="sm"
234
+ onClick={() => window.open(vulnerability.url, "_blank")}
235
+ >
236
+ <ExternalLink className="h-4 w-4 mr-2" />
237
+ Learn More
238
+ </Button>
239
+ )}
240
+ </div>
241
+ )}
242
+ </div>
243
+ )
244
+ }
245
+
246
+ export function WakaSecurityScanResult({
247
+ vulnerabilities,
248
+ scanType = "all",
249
+ scannedAt,
250
+ onVulnerabilityClick,
251
+ onViewCVE,
252
+ title = "Security Scan Results",
253
+ className,
254
+ }: WakaSecurityScanResultProps) {
255
+ const [searchQuery, setSearchQuery] = React.useState("")
256
+ const [severityFilter, setSeverityFilter] = React.useState<VulnerabilitySeverity | "all">("all")
257
+ const [expandedIds, setExpandedIds] = React.useState<Set<string>>(new Set())
258
+
259
+ const toggleExpand = (id: string) => {
260
+ setExpandedIds((prev) => {
261
+ const next = new Set(prev)
262
+ if (next.has(id)) {
263
+ next.delete(id)
264
+ } else {
265
+ next.add(id)
266
+ }
267
+ return next
268
+ })
269
+ }
270
+
271
+ // Calculate summary
272
+ const summary: ScanSummary = React.useMemo(() => {
273
+ return vulnerabilities.reduce(
274
+ (acc, vuln) => {
275
+ acc[vuln.severity]++
276
+ acc.total++
277
+ return acc
278
+ },
279
+ { critical: 0, high: 0, medium: 0, low: 0, info: 0, total: 0 }
280
+ )
281
+ }, [vulnerabilities])
282
+
283
+ // Filter vulnerabilities
284
+ const filteredVulns = React.useMemo(() => {
285
+ return vulnerabilities.filter((vuln) => {
286
+ if (severityFilter !== "all" && vuln.severity !== severityFilter) return false
287
+ if (searchQuery) {
288
+ const query = searchQuery.toLowerCase()
289
+ return (
290
+ vuln.title.toLowerCase().includes(query) ||
291
+ vuln.description.toLowerCase().includes(query) ||
292
+ vuln.cveId?.toLowerCase().includes(query) ||
293
+ vuln.package?.toLowerCase().includes(query)
294
+ )
295
+ }
296
+ return true
297
+ })
298
+ }, [vulnerabilities, severityFilter, searchQuery])
299
+
300
+ // Security score (simple calculation)
301
+ const securityScore = React.useMemo(() => {
302
+ const weights = { critical: 40, high: 20, medium: 5, low: 1, info: 0 }
303
+ const penalty = Object.entries(summary).reduce((acc, [key, count]) => {
304
+ if (key === "total") return acc
305
+ return acc + (weights[key as VulnerabilitySeverity] || 0) * count
306
+ }, 0)
307
+ return Math.max(0, 100 - penalty)
308
+ }, [summary])
309
+
310
+ const scoreColor = securityScore >= 80 ? "text-green-500" : securityScore >= 50 ? "text-yellow-500" : "text-red-500"
311
+
312
+ return (
313
+ <div className={cn("flex flex-col border rounded-lg bg-background", className)}>
314
+ {/* Header */}
315
+ <div className="flex items-center justify-between gap-4 p-3 border-b">
316
+ <div className="flex items-center gap-3">
317
+ {summary.critical > 0 || summary.high > 0 ? (
318
+ <ShieldAlert className="h-5 w-5 text-red-500" />
319
+ ) : (
320
+ <ShieldCheck className="h-5 w-5 text-green-500" />
321
+ )}
322
+ <h3 className="font-semibold">{title}</h3>
323
+ </div>
324
+
325
+ <div className="flex items-center gap-3">
326
+ <div className="text-right">
327
+ <div className={cn("text-2xl font-bold", scoreColor)}>{securityScore}</div>
328
+ <div className="text-xs text-muted-foreground">Security Score</div>
329
+ </div>
330
+ </div>
331
+ </div>
332
+
333
+ {/* Summary */}
334
+ <div className="grid grid-cols-5 gap-2 p-3 border-b bg-muted/30">
335
+ {(["critical", "high", "medium", "low", "info"] as VulnerabilitySeverity[]).map((severity) => {
336
+ const config = severityConfig[severity]
337
+ const count = summary[severity]
338
+ return (
339
+ <button
340
+ key={severity}
341
+ className={cn(
342
+ "flex flex-col items-center p-2 rounded-lg transition-colors",
343
+ severityFilter === severity ? "bg-background ring-1 ring-primary" : "hover:bg-background/50",
344
+ count > 0 && severity === "critical" && "bg-red-500/10",
345
+ count > 0 && severity === "high" && "bg-orange-500/10"
346
+ )}
347
+ onClick={() => setSeverityFilter(severityFilter === severity ? "all" : severity)}
348
+ >
349
+ <span className={cn("text-2xl font-bold", config.color)}>{count}</span>
350
+ <span className="text-xs text-muted-foreground capitalize">{severity}</span>
351
+ </button>
352
+ )
353
+ })}
354
+ </div>
355
+
356
+ {/* Toolbar */}
357
+ <div className="flex items-center gap-2 p-2 border-b">
358
+ <div className="relative flex-1 max-w-sm">
359
+ <Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
360
+ <Input
361
+ placeholder="Search vulnerabilities..."
362
+ value={searchQuery}
363
+ onChange={(e) => setSearchQuery(e.target.value)}
364
+ className="pl-8 h-8"
365
+ />
366
+ </div>
367
+
368
+ {scannedAt && (
369
+ <span className="text-xs text-muted-foreground flex items-center gap-1">
370
+ <Clock className="h-3 w-3" />
371
+ Scanned {scannedAt.toLocaleString()}
372
+ </span>
373
+ )}
374
+ </div>
375
+
376
+ {/* Vulnerabilities list */}
377
+ <ScrollArea className="flex-1 max-h-[500px]">
378
+ <div className="p-3 space-y-2">
379
+ {filteredVulns.length === 0 ? (
380
+ <div className="flex flex-col items-center justify-center h-32 text-muted-foreground">
381
+ <ShieldCheck className="h-8 w-8 mb-2 text-green-500" />
382
+ <span>No vulnerabilities found</span>
383
+ </div>
384
+ ) : (
385
+ filteredVulns.map((vuln) => (
386
+ <VulnerabilityCard
387
+ key={vuln.id}
388
+ vulnerability={vuln}
389
+ isExpanded={expandedIds.has(vuln.id)}
390
+ onToggle={() => toggleExpand(vuln.id)}
391
+ onViewCVE={onViewCVE}
392
+ />
393
+ ))
394
+ )}
395
+ </div>
396
+ </ScrollArea>
397
+
398
+ {/* Footer */}
399
+ <div className="flex items-center justify-between px-3 py-2 border-t bg-muted/30 text-sm">
400
+ <span className="text-muted-foreground">
401
+ {filteredVulns.length} of {vulnerabilities.length} vulnerabilities
402
+ </span>
403
+ {summary.critical > 0 && (
404
+ <span className="text-red-500 font-medium">
405
+ {summary.critical} critical issues require immediate attention
406
+ </span>
407
+ )}
408
+ </div>
409
+ </div>
410
+ )
411
+ }
412
+
413
+ // Default sample vulnerabilities for demo
414
+ export const defaultVulnerabilities: Vulnerability[] = [
415
+ {
416
+ id: "1",
417
+ title: "SQL Injection in login endpoint",
418
+ description: "User input is directly concatenated into SQL query without proper sanitization, allowing attackers to execute arbitrary SQL commands.",
419
+ severity: "critical",
420
+ type: "sast",
421
+ cweId: "CWE-89",
422
+ cvssScore: 9.8,
423
+ file: "src/auth/login.ts",
424
+ line: 45,
425
+ remediation: "Use parameterized queries or prepared statements to prevent SQL injection attacks.",
426
+ },
427
+ {
428
+ id: "2",
429
+ title: "Prototype Pollution in lodash",
430
+ description: "Versions of lodash prior to 4.17.21 are vulnerable to prototype pollution via the template function.",
431
+ severity: "high",
432
+ type: "sca",
433
+ cveId: "CVE-2021-23337",
434
+ cvssScore: 7.2,
435
+ package: "lodash",
436
+ version: "4.17.19",
437
+ fixedVersion: "4.17.21",
438
+ url: "https://nvd.nist.gov/vuln/detail/CVE-2021-23337",
439
+ remediation: "Upgrade lodash to version 4.17.21 or later.",
440
+ },
441
+ {
442
+ id: "3",
443
+ title: "Hardcoded API Key",
444
+ description: "AWS API key found hardcoded in source code. This exposes the key to anyone with access to the repository.",
445
+ severity: "high",
446
+ type: "secret",
447
+ file: "config/aws.ts",
448
+ line: 12,
449
+ remediation: "Remove the hardcoded key and use environment variables or a secrets manager.",
450
+ },
451
+ {
452
+ id: "4",
453
+ title: "Cross-Site Scripting (XSS)",
454
+ description: "User-provided content is rendered without proper escaping, allowing script injection.",
455
+ severity: "medium",
456
+ type: "dast",
457
+ cweId: "CWE-79",
458
+ cvssScore: 6.1,
459
+ url: "https://owasp.org/www-community/attacks/xss/",
460
+ remediation: "Sanitize and escape all user-provided content before rendering.",
461
+ },
462
+ {
463
+ id: "5",
464
+ title: "Outdated base image",
465
+ description: "Container uses node:14 base image which has known vulnerabilities.",
466
+ severity: "low",
467
+ type: "container",
468
+ package: "node",
469
+ version: "14.0.0",
470
+ fixedVersion: "18.0.0",
471
+ remediation: "Update to a more recent Node.js LTS version.",
472
+ },
473
+ ]
@@ -49,26 +49,27 @@ export interface WakaServerRackProps {
49
49
  // Constants
50
50
  // ============================================================================
51
51
 
52
+ // Status colors using CSS variables for theme support
52
53
  const STATUS_COLORS = {
53
54
  online: {
54
- led: "#22c55e",
55
- glow: "rgba(34, 197, 94, 0.6)",
56
- bg: "rgba(34, 197, 94, 0.1)",
55
+ led: "hsl(var(--success))",
56
+ glow: "hsl(var(--success) / 0.6)",
57
+ bg: "hsl(var(--success) / 0.1)",
57
58
  },
58
59
  warning: {
59
- led: "#eab308",
60
- glow: "rgba(234, 179, 8, 0.6)",
61
- bg: "rgba(234, 179, 8, 0.1)",
60
+ led: "hsl(var(--warning))",
61
+ glow: "hsl(var(--warning) / 0.6)",
62
+ bg: "hsl(var(--warning) / 0.1)",
62
63
  },
63
64
  offline: {
64
- led: "#ef4444",
65
- glow: "rgba(239, 68, 68, 0.6)",
66
- bg: "rgba(239, 68, 68, 0.1)",
65
+ led: "hsl(var(--destructive))",
66
+ glow: "hsl(var(--destructive) / 0.6)",
67
+ bg: "hsl(var(--destructive) / 0.1)",
67
68
  },
68
69
  maintenance: {
69
- led: "#3b82f6",
70
- glow: "rgba(59, 130, 246, 0.6)",
71
- bg: "rgba(59, 130, 246, 0.1)",
70
+ led: "hsl(var(--info))",
71
+ glow: "hsl(var(--info) / 0.6)",
72
+ bg: "hsl(var(--info) / 0.1)",
72
73
  },
73
74
  }
74
75
 
@@ -87,8 +88,8 @@ interface MetricBarProps {
87
88
 
88
89
  function MetricBar({ value, label, color, animated }: MetricBarProps) {
89
90
  const getBarColor = () => {
90
- if (value >= 90) return "#ef4444"
91
- if (value >= 70) return "#eab308"
91
+ if (value >= 90) return "hsl(var(--destructive))"
92
+ if (value >= 70) return "hsl(var(--warning))"
92
93
  return color
93
94
  }
94
95
 
@@ -245,20 +246,20 @@ function ServerUnitRow({
245
246
  <MetricBar
246
247
  value={server.cpu}
247
248
  label="CPU"
248
- color="#3b82f6"
249
+ color="hsl(var(--chart-1))"
249
250
  animated={animated}
250
251
  />
251
252
  <MetricBar
252
253
  value={server.ram}
253
254
  label="RAM"
254
- color="#8b5cf6"
255
+ color="hsl(var(--chart-3))"
255
256
  animated={animated}
256
257
  />
257
258
  {server.height >= 3 && (
258
259
  <MetricBar
259
260
  value={server.disk}
260
261
  label="DSK"
261
- color="#06b6d4"
262
+ color="hsl(var(--info))"
262
263
  animated={animated}
263
264
  />
264
265
  )}
@@ -279,8 +280,8 @@ function ServerUnitRow({
279
280
  <div className="flex items-center gap-0.5">
280
281
  {server.network && (
281
282
  <div className="flex flex-col items-end mr-2 text-[7px] font-mono text-zinc-500">
282
- <span className="text-green-500">{"\u25B2"} {server.network.out}</span>
283
- <span className="text-blue-400">{"\u25BC"} {server.network.in}</span>
283
+ <span className="text-success">{"\u25B2"} {server.network.out}</span>
284
+ <span className="text-info">{"\u25BC"} {server.network.in}</span>
284
285
  </div>
285
286
  )}
286
287
  {/* Drive activity indicators */}
@@ -344,21 +345,21 @@ function ServerUnitRow({
344
345
  <span className="font-mono">{server.height}U</span>
345
346
  </div>
346
347
  <div className="border-t border-zinc-700 my-2" />
347
- <MetricBar value={server.cpu} label="CPU" color="#3b82f6" />
348
- <MetricBar value={server.ram} label="RAM" color="#8b5cf6" />
349
- <MetricBar value={server.disk} label="DSK" color="#06b6d4" />
348
+ <MetricBar value={server.cpu} label="CPU" color="hsl(var(--chart-1))" />
349
+ <MetricBar value={server.ram} label="RAM" color="hsl(var(--chart-3))" />
350
+ <MetricBar value={server.disk} label="DSK" color="hsl(var(--info))" />
350
351
  {server.network && (
351
352
  <>
352
353
  <div className="border-t border-zinc-700 my-2" />
353
354
  <div className="flex justify-between text-zinc-400">
354
355
  <span>Network In</span>
355
- <span className="font-mono text-blue-400">
356
+ <span className="font-mono text-info">
356
357
  {server.network.in} Mbps
357
358
  </span>
358
359
  </div>
359
360
  <div className="flex justify-between text-zinc-400">
360
361
  <span>Network Out</span>
361
- <span className="font-mono text-green-500">
362
+ <span className="font-mono text-success">
362
363
  {server.network.out} Mbps
363
364
  </span>
364
365
  </div>
@@ -626,10 +627,10 @@ export function WakaServerRack({
626
627
  style={{
627
628
  background: `radial-gradient(ellipse at center, ${
628
629
  servers.some((s) => s.status === "offline")
629
- ? "rgba(239,68,68,0.05)"
630
+ ? "hsl(var(--destructive) / 0.05)"
630
631
  : servers.some((s) => s.status === "warning")
631
- ? "rgba(234,179,8,0.05)"
632
- : "rgba(34,197,94,0.03)"
632
+ ? "hsl(var(--warning) / 0.05)"
633
+ : "hsl(var(--success) / 0.03)"
633
634
  } 0%, transparent 70%)`,
634
635
  filter: "blur(20px)",
635
636
  }}