@cyvest/cyvest-vis 4.0.0 → 4.1.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.
@@ -0,0 +1,730 @@
1
+ /**
2
+ * Professional SVG icons for the visualization components.
3
+ * Hand-crafted icons optimized for small sizes and clear recognition.
4
+ */
5
+
6
+ import React from "react";
7
+
8
+ export interface IconProps {
9
+ size?: number;
10
+ color?: string;
11
+ className?: string;
12
+ }
13
+
14
+ const defaultSize = 16;
15
+ const defaultColor = "currentColor";
16
+
17
+ /**
18
+ * Network/Globe icon for IP addresses
19
+ */
20
+ export const GlobeIcon: React.FC<IconProps> = ({
21
+ size = defaultSize,
22
+ color = defaultColor,
23
+ className,
24
+ }) => (
25
+ <svg
26
+ width={size}
27
+ height={size}
28
+ viewBox="0 0 24 24"
29
+ fill="none"
30
+ stroke={color}
31
+ strokeWidth="2"
32
+ strokeLinecap="round"
33
+ strokeLinejoin="round"
34
+ className={className}
35
+ >
36
+ <circle cx="12" cy="12" r="10" />
37
+ <path d="M2 12h20" />
38
+ <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
39
+ </svg>
40
+ );
41
+
42
+ /**
43
+ * Home/Domain icon for domain names
44
+ */
45
+ export const DomainIcon: React.FC<IconProps> = ({
46
+ size = defaultSize,
47
+ color = defaultColor,
48
+ className,
49
+ }) => (
50
+ <svg
51
+ width={size}
52
+ height={size}
53
+ viewBox="0 0 24 24"
54
+ fill="none"
55
+ stroke={color}
56
+ strokeWidth="2"
57
+ strokeLinecap="round"
58
+ strokeLinejoin="round"
59
+ className={className}
60
+ >
61
+ <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
62
+ <polyline points="9,22 9,12 15,12 15,22" />
63
+ </svg>
64
+ );
65
+
66
+ /**
67
+ * Link icon for URLs
68
+ */
69
+ export const LinkIcon: React.FC<IconProps> = ({
70
+ size = defaultSize,
71
+ color = defaultColor,
72
+ className,
73
+ }) => (
74
+ <svg
75
+ width={size}
76
+ height={size}
77
+ viewBox="0 0 24 24"
78
+ fill="none"
79
+ stroke={color}
80
+ strokeWidth="2"
81
+ strokeLinecap="round"
82
+ strokeLinejoin="round"
83
+ className={className}
84
+ >
85
+ <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
86
+ <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
87
+ </svg>
88
+ );
89
+
90
+ /**
91
+ * Mail icon for email addresses
92
+ */
93
+ export const MailIcon: React.FC<IconProps> = ({
94
+ size = defaultSize,
95
+ color = defaultColor,
96
+ className,
97
+ }) => (
98
+ <svg
99
+ width={size}
100
+ height={size}
101
+ viewBox="0 0 24 24"
102
+ fill="none"
103
+ stroke={color}
104
+ strokeWidth="2"
105
+ strokeLinecap="round"
106
+ strokeLinejoin="round"
107
+ className={className}
108
+ >
109
+ <rect x="2" y="4" width="20" height="16" rx="2" />
110
+ <path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" />
111
+ </svg>
112
+ );
113
+
114
+ /**
115
+ * Envelope icon for email messages
116
+ */
117
+ export const EnvelopeIcon: React.FC<IconProps> = ({
118
+ size = defaultSize,
119
+ color = defaultColor,
120
+ className,
121
+ }) => (
122
+ <svg
123
+ width={size}
124
+ height={size}
125
+ viewBox="0 0 24 24"
126
+ fill="none"
127
+ stroke={color}
128
+ strokeWidth="2"
129
+ strokeLinecap="round"
130
+ strokeLinejoin="round"
131
+ className={className}
132
+ >
133
+ <path d="M22 12h-6l-2 3h-4l-2-3H2" />
134
+ <path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" />
135
+ </svg>
136
+ );
137
+
138
+ /**
139
+ * File icon
140
+ */
141
+ export const FileIcon: React.FC<IconProps> = ({
142
+ size = defaultSize,
143
+ color = defaultColor,
144
+ className,
145
+ }) => (
146
+ <svg
147
+ width={size}
148
+ height={size}
149
+ viewBox="0 0 24 24"
150
+ fill="none"
151
+ stroke={color}
152
+ strokeWidth="2"
153
+ strokeLinecap="round"
154
+ strokeLinejoin="round"
155
+ className={className}
156
+ >
157
+ <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
158
+ <polyline points="14,2 14,8 20,8" />
159
+ </svg>
160
+ );
161
+
162
+ /**
163
+ * Hash/Fingerprint icon for file hashes
164
+ */
165
+ export const HashIcon: React.FC<IconProps> = ({
166
+ size = defaultSize,
167
+ color = defaultColor,
168
+ className,
169
+ }) => (
170
+ <svg
171
+ width={size}
172
+ height={size}
173
+ viewBox="0 0 24 24"
174
+ fill="none"
175
+ stroke={color}
176
+ strokeWidth="2"
177
+ strokeLinecap="round"
178
+ strokeLinejoin="round"
179
+ className={className}
180
+ >
181
+ <line x1="4" y1="9" x2="20" y2="9" />
182
+ <line x1="4" y1="15" x2="20" y2="15" />
183
+ <line x1="10" y1="3" x2="8" y2="21" />
184
+ <line x1="16" y1="3" x2="14" y2="21" />
185
+ </svg>
186
+ );
187
+
188
+ /**
189
+ * User icon
190
+ */
191
+ export const UserIcon: React.FC<IconProps> = ({
192
+ size = defaultSize,
193
+ color = defaultColor,
194
+ className,
195
+ }) => (
196
+ <svg
197
+ width={size}
198
+ height={size}
199
+ viewBox="0 0 24 24"
200
+ fill="none"
201
+ stroke={color}
202
+ strokeWidth="2"
203
+ strokeLinecap="round"
204
+ strokeLinejoin="round"
205
+ className={className}
206
+ >
207
+ <circle cx="12" cy="8" r="5" />
208
+ <path d="M20 21a8 8 0 1 0-16 0" />
209
+ </svg>
210
+ );
211
+
212
+ /**
213
+ * ID Card icon for identity
214
+ */
215
+ export const IdCardIcon: React.FC<IconProps> = ({
216
+ size = defaultSize,
217
+ color = defaultColor,
218
+ className,
219
+ }) => (
220
+ <svg
221
+ width={size}
222
+ height={size}
223
+ viewBox="0 0 24 24"
224
+ fill="none"
225
+ stroke={color}
226
+ strokeWidth="2"
227
+ strokeLinecap="round"
228
+ strokeLinejoin="round"
229
+ className={className}
230
+ >
231
+ <rect x="2" y="5" width="20" height="14" rx="2" />
232
+ <circle cx="8" cy="12" r="2" />
233
+ <path d="M14 10h4" />
234
+ <path d="M14 14h4" />
235
+ </svg>
236
+ );
237
+
238
+ /**
239
+ * Gear/Cog icon for processes
240
+ */
241
+ export const GearIcon: React.FC<IconProps> = ({
242
+ size = defaultSize,
243
+ color = defaultColor,
244
+ className,
245
+ }) => (
246
+ <svg
247
+ width={size}
248
+ height={size}
249
+ viewBox="0 0 24 24"
250
+ fill="none"
251
+ stroke={color}
252
+ strokeWidth="2"
253
+ strokeLinecap="round"
254
+ strokeLinejoin="round"
255
+ className={className}
256
+ >
257
+ <circle cx="12" cy="12" r="3" />
258
+ <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" />
259
+ </svg>
260
+ );
261
+
262
+ /**
263
+ * Software/App icon
264
+ */
265
+ export const AppIcon: React.FC<IconProps> = ({
266
+ size = defaultSize,
267
+ color = defaultColor,
268
+ className,
269
+ }) => (
270
+ <svg
271
+ width={size}
272
+ height={size}
273
+ viewBox="0 0 24 24"
274
+ fill="none"
275
+ stroke={color}
276
+ strokeWidth="2"
277
+ strokeLinecap="round"
278
+ strokeLinejoin="round"
279
+ className={className}
280
+ >
281
+ <rect x="3" y="3" width="18" height="18" rx="2" />
282
+ <path d="M9 3v18" />
283
+ <path d="M3 9h18" />
284
+ </svg>
285
+ );
286
+
287
+ /**
288
+ * Registry key icon
289
+ */
290
+ export const RegistryIcon: React.FC<IconProps> = ({
291
+ size = defaultSize,
292
+ color = defaultColor,
293
+ className,
294
+ }) => (
295
+ <svg
296
+ width={size}
297
+ height={size}
298
+ viewBox="0 0 24 24"
299
+ fill="none"
300
+ stroke={color}
301
+ strokeWidth="2"
302
+ strokeLinecap="round"
303
+ strokeLinejoin="round"
304
+ className={className}
305
+ >
306
+ <path d="m21 2-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0 3 3L22 7l-3-3m-3.5 3.5L19 4" />
307
+ </svg>
308
+ );
309
+
310
+ /**
311
+ * Skull icon for threat actors
312
+ */
313
+ export const ThreatActorIcon: React.FC<IconProps> = ({
314
+ size = defaultSize,
315
+ color = defaultColor,
316
+ className,
317
+ }) => (
318
+ <svg
319
+ width={size}
320
+ height={size}
321
+ viewBox="0 0 24 24"
322
+ fill="none"
323
+ stroke={color}
324
+ strokeWidth="2"
325
+ strokeLinecap="round"
326
+ strokeLinejoin="round"
327
+ className={className}
328
+ >
329
+ <circle cx="12" cy="10" r="7" />
330
+ <circle cx="9" cy="9" r="1.5" fill={color} />
331
+ <circle cx="15" cy="9" r="1.5" fill={color} />
332
+ <path d="M9 17v-2" />
333
+ <path d="M12 17v-2" />
334
+ <path d="M15 17v-2" />
335
+ </svg>
336
+ );
337
+
338
+ /**
339
+ * Bug icon for malware
340
+ */
341
+ export const BugIcon: React.FC<IconProps> = ({
342
+ size = defaultSize,
343
+ color = defaultColor,
344
+ className,
345
+ }) => (
346
+ <svg
347
+ width={size}
348
+ height={size}
349
+ viewBox="0 0 24 24"
350
+ fill="none"
351
+ stroke={color}
352
+ strokeWidth="2"
353
+ strokeLinecap="round"
354
+ strokeLinejoin="round"
355
+ className={className}
356
+ >
357
+ <path d="m8 2 1.88 1.88" />
358
+ <path d="M14.12 3.88 16 2" />
359
+ <path d="M9 7.13v-1a3.003 3.003 0 1 1 6 0v1" />
360
+ <path d="M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6" />
361
+ <path d="M12 20v-9" />
362
+ <path d="M6.53 9C4.6 8.8 3 7.1 3 5" />
363
+ <path d="M6 13H2" />
364
+ <path d="M3 21c0-2.1 1.7-3.9 3.8-4" />
365
+ <path d="M20.97 5c0 2.1-1.6 3.8-3.5 4" />
366
+ <path d="M22 13h-4" />
367
+ <path d="M17.2 17c2.1.1 3.8 1.9 3.8 4" />
368
+ </svg>
369
+ );
370
+
371
+ /**
372
+ * Sword icon for attack patterns
373
+ */
374
+ export const SwordIcon: React.FC<IconProps> = ({
375
+ size = defaultSize,
376
+ color = defaultColor,
377
+ className,
378
+ }) => (
379
+ <svg
380
+ width={size}
381
+ height={size}
382
+ viewBox="0 0 24 24"
383
+ fill="none"
384
+ stroke={color}
385
+ strokeWidth="2"
386
+ strokeLinecap="round"
387
+ strokeLinejoin="round"
388
+ className={className}
389
+ >
390
+ <polyline points="14.5,17.5 3,6 3,3 6,3 17.5,14.5" />
391
+ <line x1="13" y1="19" x2="19" y2="13" />
392
+ <line x1="16" y1="16" x2="20" y2="20" />
393
+ <line x1="19" y1="21" x2="21" y2="19" />
394
+ </svg>
395
+ );
396
+
397
+ /**
398
+ * Target icon for campaigns
399
+ */
400
+ export const TargetIcon: React.FC<IconProps> = ({
401
+ size = defaultSize,
402
+ color = defaultColor,
403
+ className,
404
+ }) => (
405
+ <svg
406
+ width={size}
407
+ height={size}
408
+ viewBox="0 0 24 24"
409
+ fill="none"
410
+ stroke={color}
411
+ strokeWidth="2"
412
+ strokeLinecap="round"
413
+ strokeLinejoin="round"
414
+ className={className}
415
+ >
416
+ <circle cx="12" cy="12" r="10" />
417
+ <circle cx="12" cy="12" r="6" />
418
+ <circle cx="12" cy="12" r="2" />
419
+ </svg>
420
+ );
421
+
422
+ /**
423
+ * Alert icon for indicators
424
+ */
425
+ export const AlertIcon: React.FC<IconProps> = ({
426
+ size = defaultSize,
427
+ color = defaultColor,
428
+ className,
429
+ }) => (
430
+ <svg
431
+ width={size}
432
+ height={size}
433
+ viewBox="0 0 24 24"
434
+ fill="none"
435
+ stroke={color}
436
+ strokeWidth="2"
437
+ strokeLinecap="round"
438
+ strokeLinejoin="round"
439
+ className={className}
440
+ >
441
+ <path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
442
+ <line x1="12" y1="9" x2="12" y2="13" />
443
+ <line x1="12" y1="17" x2="12.01" y2="17" />
444
+ </svg>
445
+ );
446
+
447
+ /**
448
+ * Flask icon for artifacts
449
+ */
450
+ export const FlaskIcon: React.FC<IconProps> = ({
451
+ size = defaultSize,
452
+ color = defaultColor,
453
+ className,
454
+ }) => (
455
+ <svg
456
+ width={size}
457
+ height={size}
458
+ viewBox="0 0 24 24"
459
+ fill="none"
460
+ stroke={color}
461
+ strokeWidth="2"
462
+ strokeLinecap="round"
463
+ strokeLinejoin="round"
464
+ className={className}
465
+ >
466
+ <path d="M10 2v7.527a2 2 0 0 1-.211.896L4.72 20.55a1 1 0 0 0 .9 1.45h12.76a1 1 0 0 0 .9-1.45l-5.069-10.127A2 2 0 0 1 14 9.527V2" />
467
+ <path d="M8.5 2h7" />
468
+ <path d="M7 16h10" />
469
+ </svg>
470
+ );
471
+
472
+ /**
473
+ * Certificate icon
474
+ */
475
+ export const CertificateIcon: React.FC<IconProps> = ({
476
+ size = defaultSize,
477
+ color = defaultColor,
478
+ className,
479
+ }) => (
480
+ <svg
481
+ width={size}
482
+ height={size}
483
+ viewBox="0 0 24 24"
484
+ fill="none"
485
+ stroke={color}
486
+ strokeWidth="2"
487
+ strokeLinecap="round"
488
+ strokeLinejoin="round"
489
+ className={className}
490
+ >
491
+ <path d="M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z" />
492
+ <path d="M2 6a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6Z" />
493
+ <path d="m9.5 15.5-3 3v3l3.5-1.5 3.5 1.5v-3l-3-3" />
494
+ </svg>
495
+ );
496
+
497
+ /**
498
+ * MAC address/Wifi icon
499
+ */
500
+ export const WifiIcon: React.FC<IconProps> = ({
501
+ size = defaultSize,
502
+ color = defaultColor,
503
+ className,
504
+ }) => (
505
+ <svg
506
+ width={size}
507
+ height={size}
508
+ viewBox="0 0 24 24"
509
+ fill="none"
510
+ stroke={color}
511
+ strokeWidth="2"
512
+ strokeLinecap="round"
513
+ strokeLinejoin="round"
514
+ className={className}
515
+ >
516
+ <path d="M5 12.55a11 11 0 0 1 14.08 0" />
517
+ <path d="M1.42 9a16 16 0 0 1 21.16 0" />
518
+ <path d="M8.53 16.11a6 6 0 0 1 6.95 0" />
519
+ <line x1="12" y1="20" x2="12.01" y2="20" />
520
+ </svg>
521
+ );
522
+
523
+ /**
524
+ * Autonomous System/World icon
525
+ */
526
+ export const WorldIcon: React.FC<IconProps> = ({
527
+ size = defaultSize,
528
+ color = defaultColor,
529
+ className,
530
+ }) => (
531
+ <svg
532
+ width={size}
533
+ height={size}
534
+ viewBox="0 0 24 24"
535
+ fill="none"
536
+ stroke={color}
537
+ strokeWidth="2"
538
+ strokeLinecap="round"
539
+ strokeLinejoin="round"
540
+ className={className}
541
+ >
542
+ <circle cx="12" cy="12" r="10" />
543
+ <path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" />
544
+ <path d="M2 12h20" />
545
+ </svg>
546
+ );
547
+
548
+ /**
549
+ * Question mark icon for unknown types
550
+ */
551
+ export const QuestionIcon: React.FC<IconProps> = ({
552
+ size = defaultSize,
553
+ color = defaultColor,
554
+ className,
555
+ }) => (
556
+ <svg
557
+ width={size}
558
+ height={size}
559
+ viewBox="0 0 24 24"
560
+ fill="none"
561
+ stroke={color}
562
+ strokeWidth="2"
563
+ strokeLinecap="round"
564
+ strokeLinejoin="round"
565
+ className={className}
566
+ >
567
+ <circle cx="12" cy="12" r="10" />
568
+ <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" />
569
+ <path d="M12 17h.01" />
570
+ </svg>
571
+ );
572
+
573
+ /**
574
+ * Checkmark icon for checks
575
+ */
576
+ export const CheckIcon: React.FC<IconProps> = ({
577
+ size = defaultSize,
578
+ color = defaultColor,
579
+ className,
580
+ }) => (
581
+ <svg
582
+ width={size}
583
+ height={size}
584
+ viewBox="0 0 24 24"
585
+ fill="none"
586
+ stroke={color}
587
+ strokeWidth="2"
588
+ strokeLinecap="round"
589
+ strokeLinejoin="round"
590
+ className={className}
591
+ >
592
+ <path d="M20 6 9 17l-5-5" />
593
+ </svg>
594
+ );
595
+
596
+ /**
597
+ * Box icon for containers
598
+ */
599
+ export const BoxIcon: React.FC<IconProps> = ({
600
+ size = defaultSize,
601
+ color = defaultColor,
602
+ className,
603
+ }) => (
604
+ <svg
605
+ width={size}
606
+ height={size}
607
+ viewBox="0 0 24 24"
608
+ fill="none"
609
+ stroke={color}
610
+ strokeWidth="2"
611
+ strokeLinecap="round"
612
+ strokeLinejoin="round"
613
+ className={className}
614
+ >
615
+ <path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z" />
616
+ <path d="m3.3 7 8.7 5 8.7-5" />
617
+ <path d="M12 22V12" />
618
+ </svg>
619
+ );
620
+
621
+ /**
622
+ * Crosshair icon for root/target
623
+ */
624
+ export const CrosshairIcon: React.FC<IconProps> = ({
625
+ size = defaultSize,
626
+ color = defaultColor,
627
+ className,
628
+ }) => (
629
+ <svg
630
+ width={size}
631
+ height={size}
632
+ viewBox="0 0 24 24"
633
+ fill="none"
634
+ stroke={color}
635
+ strokeWidth="2"
636
+ strokeLinecap="round"
637
+ strokeLinejoin="round"
638
+ className={className}
639
+ >
640
+ <circle cx="12" cy="12" r="10" />
641
+ <line x1="22" y1="12" x2="18" y2="12" />
642
+ <line x1="6" y1="12" x2="2" y2="12" />
643
+ <line x1="12" y1="6" x2="12" y2="2" />
644
+ <line x1="12" y1="22" x2="12" y2="18" />
645
+ </svg>
646
+ );
647
+
648
+ /**
649
+ * Map observable types to icon components
650
+ */
651
+ export const OBSERVABLE_ICON_MAP: Record<
652
+ string,
653
+ React.FC<IconProps>
654
+ > = {
655
+ // Network
656
+ "ipv4-addr": GlobeIcon,
657
+ "ipv6-addr": GlobeIcon,
658
+ "domain-name": DomainIcon,
659
+ url: LinkIcon,
660
+ "autonomous-system": WorldIcon,
661
+ "mac-addr": WifiIcon,
662
+
663
+ // Email
664
+ "email-addr": MailIcon,
665
+ "email-message": EnvelopeIcon,
666
+
667
+ // File
668
+ file: FileIcon,
669
+ "file-hash": HashIcon,
670
+ "file:hash:md5": HashIcon,
671
+ "file:hash:sha1": HashIcon,
672
+ "file:hash:sha256": HashIcon,
673
+
674
+ // User/Identity
675
+ user: UserIcon,
676
+ "user-account": UserIcon,
677
+ identity: IdCardIcon,
678
+
679
+ // Process/System
680
+ process: GearIcon,
681
+ software: AppIcon,
682
+ "windows-registry-key": RegistryIcon,
683
+
684
+ // Threat Intelligence
685
+ "threat-actor": ThreatActorIcon,
686
+ malware: BugIcon,
687
+ "attack-pattern": SwordIcon,
688
+ campaign: TargetIcon,
689
+ indicator: AlertIcon,
690
+
691
+ // Artifacts
692
+ artifact: FlaskIcon,
693
+ certificate: CertificateIcon,
694
+ "x509-certificate": CertificateIcon,
695
+
696
+ // Default
697
+ unknown: QuestionIcon,
698
+ };
699
+
700
+ /**
701
+ * Map investigation node types to icons
702
+ */
703
+ export const INVESTIGATION_ICON_MAP: Record<
704
+ string,
705
+ React.FC<IconProps>
706
+ > = {
707
+ root: CrosshairIcon,
708
+ check: CheckIcon,
709
+ container: BoxIcon,
710
+ };
711
+
712
+ /**
713
+ * Get the icon component for an observable type.
714
+ */
715
+ export function getObservableIcon(
716
+ observableType: string
717
+ ): React.FC<IconProps> {
718
+ const normalized = observableType.toLowerCase().trim();
719
+ return OBSERVABLE_ICON_MAP[normalized] ?? OBSERVABLE_ICON_MAP.unknown;
720
+ }
721
+
722
+ /**
723
+ * Get the icon component for an investigation node type.
724
+ */
725
+ export function getInvestigationIcon(
726
+ nodeType: string
727
+ ): React.FC<IconProps> {
728
+ return INVESTIGATION_ICON_MAP[nodeType] ?? QuestionIcon;
729
+ }
730
+