howler-sentinel-plugin 0.2.0.dev31__py3-none-any.whl → 0.2.0.dev87__py3-none-any.whl

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,779 @@
1
+ from typing import Any
2
+ from uuid import uuid4
3
+
4
+
5
+ def default_unknown_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
6
+ """Default function to handle unknown evidence types."""
7
+ return {}
8
+
9
+
10
+ class XDRAlertEvidence:
11
+ """Parse graph evidence into howler evidence format."""
12
+
13
+ @staticmethod
14
+ def amazon_resource_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
15
+ """Convert Amazon resource evidence to howler evidence format."""
16
+ return {
17
+ "aws": {
18
+ "account": {
19
+ "id": evidence.get("amazonAccountId"),
20
+ },
21
+ "resource": {
22
+ "id": evidence.get("amazonResourceId"),
23
+ "type": evidence.get("resourceType"),
24
+ "name": evidence.get("resourceName"),
25
+ },
26
+ },
27
+ }
28
+
29
+ @staticmethod
30
+ def analyzed_message_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
31
+ """Convert analyzed message evidence to howler evidence format."""
32
+ sender = [
33
+ evidence.get("p1Sender", {}).get("emailAddress", "Unknown"),
34
+ evidence.get("p2Sender", {}).get("emailAddress", "Unknown"),
35
+ ]
36
+ if not sender:
37
+ sender = []
38
+ return {
39
+ "email": {
40
+ "direction": evidence.get("antiSpamDirection"),
41
+ "local_id": evidence.get("networkMessageId"),
42
+ "to": evidence.get("recipientEmailAddress"),
43
+ "sender": ", ".join(sender),
44
+ "subject": evidence.get("subject"),
45
+ },
46
+ "host": {"ip": [evidence.get("senderIp")]},
47
+ "url": {
48
+ "full": ", ".join(evidence.get("urls", [])),
49
+ },
50
+ }
51
+
52
+ @staticmethod
53
+ def azure_resource_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
54
+ """Convert Azure resource evidence to howler evidence format."""
55
+ return {
56
+ "azure": {
57
+ "resource": [
58
+ {
59
+ "resource_id": evidence.get("resourceId"),
60
+ "resource_type": evidence.get("resourceType"),
61
+ "resource_name": evidence.get("resourceName"),
62
+ }
63
+ ]
64
+ },
65
+ }
66
+
67
+ @staticmethod
68
+ def blob_container_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
69
+ """Convert blob container evidence to howler evidence format."""
70
+ resource = evidence.get("storageResource", {})
71
+ _hashes = evidence.get("fileHashes", {})
72
+ if not resource:
73
+ resource = {}
74
+ if not _hashes:
75
+ _hashes = {}
76
+ return {
77
+ "Azure": {
78
+ "resource": [
79
+ {
80
+ "resource_id": resource.get("resourceId"),
81
+ "resource_type": resource.get("resourceType"),
82
+ "resource_name": resource.get("resourceName"),
83
+ }
84
+ ]
85
+ },
86
+ "file": {
87
+ "created": evidence.get("createdDateTime"),
88
+ "hash": {
89
+ "sha1": _hashes.get("sha1"),
90
+ "sha256": _hashes.get("sha256"),
91
+ "md5": _hashes.get("md5"),
92
+ },
93
+ "name": evidence.get("name"),
94
+ },
95
+ }
96
+
97
+ @staticmethod
98
+ def blob_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
99
+ """Convert blob evidence to howler evidence format."""
100
+ container_evidence = evidence.get("blobContainer", {})
101
+ resource = container_evidence.get("storageResource", {})
102
+ _hashes = evidence.get("fileHashes", {})
103
+ if not container_evidence:
104
+ container_evidence = {}
105
+ if not resource:
106
+ resource = {}
107
+ if not _hashes:
108
+ _hashes = {}
109
+ return {
110
+ "Azure": {
111
+ "resource": [
112
+ {
113
+ "resource_id": resource.get("resourceId"),
114
+ "resource_type": resource.get("resourceType"),
115
+ "resource_name": resource.get("resourceName"),
116
+ }
117
+ ]
118
+ },
119
+ "file": {
120
+ "created": evidence.get("createdDateTime"),
121
+ "hash": {
122
+ "sha1": _hashes.get("sha1"),
123
+ "sha256": _hashes.get("sha256"),
124
+ "md5": _hashes.get("md5"),
125
+ },
126
+ "name": evidence.get("name"),
127
+ },
128
+ }
129
+
130
+ @staticmethod
131
+ def cloud_application_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
132
+ """Convert cloud application evidence to howler evidence format."""
133
+ return {
134
+ "cloud": {
135
+ "project": {
136
+ "id": str(evidence.get("appId")),
137
+ "name": evidence.get("displayName"),
138
+ }
139
+ },
140
+ }
141
+
142
+ @staticmethod
143
+ def cloud_logon_session_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
144
+ """Convert cloud logon session evidence to howler evidence format."""
145
+ account = evidence.get("account", {})
146
+ user = account.get("userAccount", {})
147
+ if not account:
148
+ account = {}
149
+ if not user:
150
+ user = {}
151
+ return {
152
+ "host": {
153
+ "name": evidence.get("deviceName"),
154
+ "os": {
155
+ "full": evidence.get("operatingSystem"),
156
+ },
157
+ },
158
+ "network": {
159
+ "protocol": evidence.get("protocol"),
160
+ },
161
+ "user": {
162
+ "name": user.get("accountName"),
163
+ "full_name": user.get("userPrincipalName"),
164
+ "id": user.get("azureAdUserId"),
165
+ "domain": user.get("domainName"),
166
+ },
167
+ "user_agent": {
168
+ "original": evidence.get("userAgent"),
169
+ },
170
+ }
171
+
172
+ @staticmethod
173
+ def container_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
174
+ """Convert container evidence to howler evidence format."""
175
+ image = evidence.get("image", {})
176
+ pod = evidence.get("pod", {})
177
+ containers = pod.get("containers", [])
178
+ if not image:
179
+ image = {}
180
+ if not pod:
181
+ pod = {}
182
+ if not containers:
183
+ containers = []
184
+ return {
185
+ "container": {
186
+ "id": evidence.get("containerId"),
187
+ "image": evidence.get("name"),
188
+ },
189
+ "image": {"name": image.get("digestImage")},
190
+ "kubernetes": {
191
+ "pod": {
192
+ "containers": [
193
+ {
194
+ "name": container.get("name"),
195
+ "image": container.get("image"),
196
+ "id": container.get("containerId"),
197
+ "digest": container.get("digestImage"),
198
+ }
199
+ for container in containers
200
+ ],
201
+ "controller": pod.get("controller"),
202
+ "ephemeral_containers": pod.get("ephemeralContainers"),
203
+ "init_containers": pod.get("initContainers"),
204
+ "labels": pod.get("labels"),
205
+ "name": pod.get("name"),
206
+ "namespace": pod.get("namespace"),
207
+ "pod_ip": pod.get("podIp"),
208
+ "service_account": {
209
+ "name": pod.get("serviceAccountName"),
210
+ "namespace": pod.get("namespace"),
211
+ },
212
+ }
213
+ },
214
+ }
215
+
216
+ @staticmethod
217
+ def container_image_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
218
+ """Convert container image evidence to howler evidence format."""
219
+ return {
220
+ "image": {"name": evidence.get("digestImage")},
221
+ }
222
+
223
+ @staticmethod
224
+ def container_registry_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
225
+ """Convert container registry evidence to howler evidence format."""
226
+ return {
227
+ "container": {
228
+ "registry": evidence.get("registry"),
229
+ },
230
+ }
231
+
232
+ @staticmethod
233
+ def device_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
234
+ """Convert device evidence to howler evidence format."""
235
+ return {
236
+ "host": {
237
+ "id": evidence.get("mdeDeviceId"),
238
+ "name": evidence.get("hostName"),
239
+ "domain": [evidence.get("ntDomain", "unknown")],
240
+ "os": {
241
+ "platform": evidence.get("osPlatform"),
242
+ "version": str(evidence.get("osBuild")),
243
+ },
244
+ "logged_on_users": [
245
+ {
246
+ "name": user.get("accountName"),
247
+ "domain": user.get("domainName"),
248
+ }
249
+ for user in evidence.get("loggedOnUsers", [])
250
+ ],
251
+ "risk_score": evidence.get("riskScore"),
252
+ "status_av": evidence.get("defenderAvStatus"),
253
+ "status_health": evidence.get("healthStatus"),
254
+ "status_onboarding": evidence.get("onboardingStatus"),
255
+ },
256
+ }
257
+
258
+ @staticmethod
259
+ def file_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
260
+ """Convert file evidence to howler evidence format."""
261
+ file_details = evidence.get("fileDetails", {})
262
+ if not file_details:
263
+ file_details = {}
264
+ return {
265
+ "file": {
266
+ "created": evidence.get("createdDateTime"),
267
+ "hash": {
268
+ "sha1": file_details.get("sha1"),
269
+ "sha256": file_details.get("sha256"),
270
+ "md5": file_details.get("md5"),
271
+ },
272
+ "name": file_details.get("fileName"),
273
+ "path": file_details.get("filePath"),
274
+ "size": file_details.get("fileSize"),
275
+ "code_signature": {
276
+ "signing_id": file_details.get("signer"),
277
+ "team_id": file_details.get("issuer"),
278
+ },
279
+ },
280
+ }
281
+
282
+ @staticmethod
283
+ def google_cloud_resource_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
284
+ """Convert Google Cloud resource evidence to howler evidence format."""
285
+ return {
286
+ "gcp": {
287
+ "zone": evidence.get("location"),
288
+ "zone_type": evidence.get("locationType"),
289
+ "project_id": evidence.get("projectId"),
290
+ "project_number": evidence.get("projectNumber"),
291
+ "resource_id": evidence.get("resourceName"),
292
+ "resource_type": evidence.get("resourceType"),
293
+ },
294
+ }
295
+
296
+ @staticmethod
297
+ def iot_device_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
298
+ """Convert IoT device evidence to howler evidence format."""
299
+ return {
300
+ "iot": {
301
+ "hub": evidence.get("iotHub"),
302
+ "id": evidence.get("deviceId"),
303
+ "name": evidence.get("deviceName"),
304
+ "owners": evidence.get("owners"),
305
+ "ioTSecurityAgentId": evidence.get("securityAgentId"),
306
+ "deviceType": evidence.get("deviceType"),
307
+ "source": evidence.get("source"),
308
+ "source_ref": evidence.get("sourceRef"),
309
+ "manufacturer": evidence.get("manufacturer"),
310
+ "model": evidence.get("model"),
311
+ "serial": evidence.get("serialNumber"),
312
+ "site": evidence.get("site"),
313
+ "zone": evidence.get("zone"),
314
+ "sensor": evidence.get("sensor"),
315
+ "importance": evidence.get("importance"),
316
+ "purdue_layer": evidence.get("purdueLayer"),
317
+ "is_programming": evidence.get("isProgramming"),
318
+ "is_authorized": evidence.get("isAuthorized"),
319
+ "is_scanner": evidence.get("isScanner"),
320
+ "device_link": evidence.get("devicePageLink"),
321
+ "device_subtype": evidence.get("deviceSubtype"),
322
+ },
323
+ "host": {
324
+ "ip": evidence.get("ipAddress"),
325
+ "mac": evidence.get("macAddress"),
326
+ "os": {
327
+ "full": evidence.get("operatingSystem"),
328
+ },
329
+ },
330
+ }
331
+
332
+ @staticmethod
333
+ def ip_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
334
+ """Convert IP evidence to howler evidence format."""
335
+ return {
336
+ "host": {
337
+ "ip": [evidence.get("ipAddress")],
338
+ "geo": {
339
+ "country_iso_code": evidence.get("countryCode"),
340
+ },
341
+ },
342
+ }
343
+
344
+ @staticmethod
345
+ def kubernetes_cluster_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
346
+ """Convert Kubernetes cluster evidence to howler evidence format."""
347
+ return {
348
+ "kubernetes": {
349
+ "namespace": {
350
+ "cluster": {
351
+ "cloud_resource": evidence.get("cloudResource"),
352
+ "distribution": evidence.get("distribution"),
353
+ "name": evidence.get("name"),
354
+ "platform": evidence.get("platform"),
355
+ "version": evidence.get("version"),
356
+ }
357
+ }
358
+ },
359
+ }
360
+
361
+ @staticmethod
362
+ def kubernetes_controller_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
363
+ """Convert Kubernetes controller evidence to howler evidence format."""
364
+ return {
365
+ "kubernetes": {
366
+ "pod": {
367
+ "controller": {
368
+ "labels": evidence.get("labels"),
369
+ "name": evidence.get("name"),
370
+ "namespace": evidence.get("namespace"),
371
+ "controller_type": evidence.get("type"),
372
+ }
373
+ }
374
+ },
375
+ }
376
+
377
+ @staticmethod
378
+ def kubernetes_namespace_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
379
+ """Convert Kubernetes namespace evidence to howler evidence format."""
380
+ return {
381
+ "kubernetes": {
382
+ "namespace": {
383
+ "cluster": {
384
+ "cloud_resource": evidence.get("cloudResource"),
385
+ "distribution": evidence.get("distribution"),
386
+ "name": evidence.get("name"),
387
+ "platform": evidence.get("platform"),
388
+ "version": evidence.get("version"),
389
+ }
390
+ },
391
+ "labels": evidence.get("labels"),
392
+ "name": evidence.get("name"),
393
+ },
394
+ }
395
+
396
+ @staticmethod
397
+ def kubernetes_pod_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
398
+ """Convert Kubernetes pod evidence to howler evidence format."""
399
+ return {
400
+ "kubernetes": {
401
+ "controller": {
402
+ "labels": evidence.get("labels"),
403
+ "name": evidence.get("name"),
404
+ "namespace": evidence.get("namespace"),
405
+ "controller_type": evidence.get("type"),
406
+ "ephemeral_containers": [
407
+ {
408
+ "id": container.get("containerId"),
409
+ "args": container.get("args"),
410
+ "command": container.get("command"),
411
+ "name": container.get("name"),
412
+ }
413
+ for container in evidence.get("ephemeralContainers", [])
414
+ ],
415
+ "init_containers": [
416
+ {
417
+ "id": container.get("containerId"),
418
+ "args": container.get("args"),
419
+ "command": container.get("command"),
420
+ "name": container.get("name"),
421
+ }
422
+ for container in evidence.get("ephemeralContainers", [])
423
+ ],
424
+ "pod_ip": evidence.get("podIp"),
425
+ "service_account": {
426
+ "name": evidence.get("serviceAccountName"),
427
+ "namespace": evidence.get("namespace"),
428
+ },
429
+ }
430
+ },
431
+ }
432
+
433
+ @staticmethod
434
+ def kubernetes_secret_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
435
+ """Convert Kubernetes secret evidence to howler evidence format."""
436
+ return {
437
+ "kubernetes": {
438
+ "secret": {
439
+ "name": evidence.get("name"),
440
+ "namespace": evidence.get("namespace"),
441
+ "secret_type": evidence.get("secretType"),
442
+ }
443
+ },
444
+ }
445
+
446
+ @staticmethod
447
+ def kubernetes_service_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
448
+ """Convert Kubernetes service evidence to howler evidence format."""
449
+ return {
450
+ "kubernetes": {
451
+ "service": {
452
+ "cluster_ip": evidence.get("clusterIp"),
453
+ "external_ips": [x.get("ipAddress") for x in evidence.get("externalIps", [])],
454
+ "labels": evidence.get("labels"),
455
+ "name": evidence.get("name"),
456
+ "namespace": evidence.get("namespace"),
457
+ "selector": evidence.get("selector"),
458
+ "service_ports": [
459
+ {
460
+ "app_protocol": port.get("appProtocol"),
461
+ "name": port.get("name"),
462
+ "node_port": port.get("nodePort"),
463
+ "port": port.get("port"),
464
+ "protocol": port.get("protocol"),
465
+ "target_port": port.get("targetPort"),
466
+ }
467
+ for port in evidence.get("servicePorts", [])
468
+ ],
469
+ "service_type": evidence.get("serviceType"),
470
+ }
471
+ },
472
+ }
473
+
474
+ @staticmethod
475
+ def kubernetes_service_account_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
476
+ """Convert Kubernetes service account evidence to howler evidence format."""
477
+ return {
478
+ "kubernetes": {
479
+ "pod": {
480
+ "service_account": {
481
+ "name": evidence.get("name"),
482
+ "namespace": evidence.get("namespace"),
483
+ }
484
+ }
485
+ },
486
+ }
487
+
488
+ @staticmethod
489
+ def mail_cluster_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
490
+ """Convert mail cluster evidence to howler evidence format."""
491
+ return {
492
+ "metadata": {
493
+ "id": str(uuid4()),
494
+ "kind": "evidence",
495
+ "provider": "Alert",
496
+ "rawdata": evidence,
497
+ },
498
+ "mailcluster": {
499
+ "cluster_by": evidence.get("clusterBy"),
500
+ "cluster_by_value": evidence.get("clusterByValue"),
501
+ "email_count": evidence.get("emailCount"),
502
+ "network_message_ids": evidence.get("networkMessageIds"),
503
+ "query": evidence.get("query"),
504
+ "urn": evidence.get("urn"),
505
+ },
506
+ }
507
+
508
+ @staticmethod
509
+ def mailbox_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
510
+ """Convert mailbox evidence to howler evidence format."""
511
+ user = evidence.get("userAccount", {})
512
+ if not user:
513
+ user = {}
514
+ return {
515
+ "metadata": {
516
+ "kind": "evidence",
517
+ "provider": "Alert",
518
+ "rawdata": evidence,
519
+ },
520
+ "email": {
521
+ "id": str(uuid4()),
522
+ "to": evidence.get("primaryAddress"),
523
+ },
524
+ "user": {
525
+ "id": user.get("azureAdUserId"),
526
+ "name": user.get("accountName"),
527
+ "full_name": user.get("userPrincipalName"),
528
+ "domain": user.get("domain"),
529
+ },
530
+ }
531
+
532
+ @staticmethod
533
+ def nic_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
534
+ """Convert NIC evidence to howler evidence format."""
535
+ return {
536
+ "host": {
537
+ "ip": evidence.get("ipAddress"),
538
+ "mac": evidence.get("macAddress"),
539
+ },
540
+ }
541
+
542
+ @staticmethod
543
+ def oauth_application_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
544
+ """Convert OAuth application evidence to howler evidence format."""
545
+ return {
546
+ "metadata": {
547
+ "id": str(uuid4()),
548
+ "kind": "evidence",
549
+ "provider": "Alert",
550
+ "rawdata": evidence,
551
+ },
552
+ "oauth": {
553
+ "app_id": evidence.get("appId"),
554
+ "display_name": evidence.get("displayName"),
555
+ "object_id": evidence.get("objectId"),
556
+ "publisher": evidence.get("publisher"),
557
+ },
558
+ }
559
+
560
+ @staticmethod
561
+ def process_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
562
+ """Convert process evidence to howler evidence format."""
563
+ image_file = evidence.get("imageFile", {})
564
+ parent_image_file = evidence.get("parentImageFile", {})
565
+ if not image_file:
566
+ image_file = {}
567
+ if not parent_image_file:
568
+ parent_image_file = {}
569
+ executable = f"{image_file.get('filePath')}\\{image_file.get('fileName')}"
570
+ return {
571
+ "metadata": {
572
+ "kind": "evidence",
573
+ "provider": "Alert",
574
+ "rawdata": evidence,
575
+ "status_remediation": evidence.get("detectionStatus"),
576
+ },
577
+ "process": {
578
+ "start": evidence.get("processCreationDateTime"),
579
+ "pid": evidence.get("processId"),
580
+ "name": image_file.get("fileName"),
581
+ "hash": {
582
+ "sha1": image_file.get("sha1"),
583
+ "sha256": image_file.get("sha256"),
584
+ "md5": image_file.get("md5"),
585
+ },
586
+ "executable": executable,
587
+ "code_signature": {
588
+ "team_id": image_file.get("issuer"),
589
+ "signing_id": image_file.get("signer"),
590
+ },
591
+ "parent": {
592
+ "start": evidence.get("parentProcessCreationDateTime"),
593
+ "pid": evidence.get("parentProcessId"),
594
+ "name": parent_image_file.get("fileName"),
595
+ "hash": {
596
+ "sha1": parent_image_file.get("sha1"),
597
+ "sha256": parent_image_file.get("sha256"),
598
+ "md5": parent_image_file.get("md5"),
599
+ },
600
+ "executable": parent_image_file.get("filePath"),
601
+ "code_signature": {
602
+ "team_id": parent_image_file.get("issuer"),
603
+ "signing_id": parent_image_file.get("signer"),
604
+ },
605
+ },
606
+ },
607
+ }
608
+
609
+ @staticmethod
610
+ def registry_key_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
611
+ """Convert registry key evidence to howler evidence format."""
612
+ return {
613
+ "metadata": {
614
+ "kind": "evidence",
615
+ "provider": "Alert",
616
+ "rawdata": evidence,
617
+ },
618
+ "key": evidence.get("registryKey"),
619
+ "hive": evidence.get("registryHive"),
620
+ }
621
+
622
+ @staticmethod
623
+ def registry_value_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
624
+ """Convert registry value evidence to howler evidence format."""
625
+ return {
626
+ "metadata": {
627
+ "kind": "evidence",
628
+ "provider": "Alert",
629
+ "rawdata": evidence,
630
+ },
631
+ "registry": {
632
+ "hive": evidence.get("registryHive"),
633
+ "key": evidence.get("registryKey"),
634
+ "value": evidence.get("registryValue"),
635
+ },
636
+ "host": {
637
+ "id": evidence.get("mdeDeviceId"),
638
+ },
639
+ }
640
+
641
+ @staticmethod
642
+ def security_group_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
643
+ """Convert security group evidence to howler evidence format."""
644
+ return {
645
+ "metadata": {
646
+ "kind": "evidence",
647
+ "provider": "Alert",
648
+ "rawdata": evidence,
649
+ },
650
+ "group": {
651
+ "id": evidence.get("securityGroupId"),
652
+ "name": evidence.get("displayName"),
653
+ },
654
+ }
655
+
656
+ @staticmethod
657
+ def teams_message_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
658
+ """Convert Teams message evidence to howler evidence format."""
659
+ return {
660
+ "metadata": {
661
+ "kind": "evidence",
662
+ "provider": "Alert",
663
+ "rawdata": evidence,
664
+ "roles": evidence.get("roles"),
665
+ "tags": evidence.get("tags"),
666
+ "verdict": evidence.get("verdict"),
667
+ "status_remediation": evidence.get("remediationStatus"),
668
+ "status_remediation_details": evidence.get("remediationStatusDetails"),
669
+ },
670
+ "teams": {
671
+ "campaign_id": evidence.get("CampaignId"),
672
+ "channel_id": evidence.get("ChannelId"),
673
+ "delivery_action": evidence.get("DeliveryAction"),
674
+ "delivery_location": evidence.get("DeliveryLocation"),
675
+ "detailed_roles": evidence.get("DetailedRoles"),
676
+ "files": [
677
+ {
678
+ "path": file.get("FileDetails", {}).get("FilePath"),
679
+ "name": file.get("FileDetails", {}).get("FileName"),
680
+ "hash": {
681
+ "sha1": file.get("FileDetails", {}).get("Sha1"),
682
+ "sha256": file.get("FileDetails", {}).get("Sha256"),
683
+ "md5": file.get("FileDetails", {}).get("Md5"),
684
+ },
685
+ "code_signature": {
686
+ "signing_id": file.get("FileDetails", {}).get("Signer"),
687
+ "team_id": file.get("FileDetails", {}).get("Issuer"),
688
+ },
689
+ "size": file.get("FileDetails", {}).get("FileSize"),
690
+ }
691
+ for file in evidence.get("Files", [])
692
+ ],
693
+ "group_id": evidence.get("GroupId"),
694
+ "is_external": evidence.get("IsExternal"),
695
+ "is_owned": evidence.get("IsOwned"),
696
+ "last_modified": evidence.get("LastModified"),
697
+ "message_direction": evidence.get("MessageDirection"),
698
+ "message_id": evidence.get("MessageId"),
699
+ "owning_tenant_id": evidence.get("OwningTenantId"),
700
+ "parent_message_id": evidence.get("ParentMessageId"),
701
+ "received": evidence.get("Received"),
702
+ "recipients": evidence.get("Recipients"),
703
+ "sender_from_address": evidence.get("SenderFromAddress"),
704
+ "sender_ip": evidence.get("SenderIp"),
705
+ "source_add_name": evidence.get("SourceAddName"),
706
+ "source_id": evidence.get("SourceId"),
707
+ "subject": evidence.get("Subject"),
708
+ "suspicious_recipients": evidence.get("SuspiciousRecipients"),
709
+ "thread_id": evidence.get("ThreadId"),
710
+ "thread_type": evidence.get("ThreadType"),
711
+ "urls": [
712
+ {
713
+ "full": url.get("Url"),
714
+ }
715
+ for url in evidence.get("Urls", [])
716
+ ],
717
+ },
718
+ }
719
+
720
+ @staticmethod
721
+ def url_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
722
+ """Convert URL evidence to howler evidence format."""
723
+ return {
724
+ "url": {
725
+ "full": evidence.get("Url"),
726
+ },
727
+ }
728
+
729
+ @staticmethod
730
+ def user_evidence(evidence: dict[str, Any]) -> dict[str, Any]:
731
+ """Convert user evidence to howler evidence format."""
732
+ user = evidence.get("UserAccount", {})
733
+ if not user:
734
+ user = {}
735
+ return {
736
+ "user": {
737
+ "name": user.get("AccountName"),
738
+ "full_name": user.get("UserPrincipalName"),
739
+ "id": user.get("AzureAdUserId"),
740
+ "domain": user.get("DomainName"),
741
+ },
742
+ }
743
+
744
+
745
+ evidence_function_map = {
746
+ "amazonResourceEvidence": XDRAlertEvidence.amazon_resource_evidence,
747
+ "analyzedMessageEvidence": XDRAlertEvidence.analyzed_message_evidence,
748
+ "azureResourceEvidence": XDRAlertEvidence.azure_resource_evidence,
749
+ "blobContainerEvidence": XDRAlertEvidence.blob_container_evidence,
750
+ "blobEvidence": XDRAlertEvidence.blob_evidence,
751
+ "cloudApplicationEvidence": XDRAlertEvidence.cloud_application_evidence,
752
+ "cloudLogonSessionEvidence": XDRAlertEvidence.cloud_logon_session_evidence,
753
+ "containerEvidence": XDRAlertEvidence.container_evidence,
754
+ "containerImageEvidence": XDRAlertEvidence.container_image_evidence,
755
+ "containerRegistryEvidence": XDRAlertEvidence.container_registry_evidence,
756
+ "deviceEvidence": XDRAlertEvidence.device_evidence,
757
+ "fileEvidence": XDRAlertEvidence.file_evidence,
758
+ "googleCloudResourceEvidence": XDRAlertEvidence.google_cloud_resource_evidence,
759
+ "iotDeviceEvidence": XDRAlertEvidence.iot_device_evidence,
760
+ "ipEvidence": XDRAlertEvidence.ip_evidence,
761
+ "kubernetesClusterEvidence": XDRAlertEvidence.kubernetes_cluster_evidence,
762
+ "kubernetesControllerEvidence": XDRAlertEvidence.kubernetes_controller_evidence,
763
+ "kubernetesNamespaceEvidence": XDRAlertEvidence.kubernetes_namespace_evidence,
764
+ "kubernetesPodEvidence": XDRAlertEvidence.kubernetes_pod_evidence,
765
+ "kubernetesSecretEvidence": XDRAlertEvidence.kubernetes_secret_evidence,
766
+ "kubernetesServiceEvidence": XDRAlertEvidence.kubernetes_service_evidence,
767
+ "kubernetesServiceAccountEvidence": XDRAlertEvidence.kubernetes_service_account_evidence,
768
+ "mailClusterEvidence": XDRAlertEvidence.mail_cluster_evidence,
769
+ "mailboxEvidence": XDRAlertEvidence.mailbox_evidence,
770
+ "nicEvidence": XDRAlertEvidence.nic_evidence,
771
+ "oauthApplicationEvidence": XDRAlertEvidence.oauth_application_evidence,
772
+ "processEvidence": XDRAlertEvidence.process_evidence,
773
+ "registryKeyEvidence": XDRAlertEvidence.registry_key_evidence,
774
+ "registryValueEvidence": XDRAlertEvidence.registry_value_evidence,
775
+ "securityGroupEvidence": XDRAlertEvidence.security_group_evidence,
776
+ "teamsMessageEvidence": XDRAlertEvidence.teams_message_evidence,
777
+ "urlEvidence": XDRAlertEvidence.url_evidence,
778
+ "userEvidence": XDRAlertEvidence.user_evidence,
779
+ }