regscale-cli 6.27.3.0__py3-none-any.whl → 6.28.1.0__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.

Potentially problematic release.


This version of regscale-cli might be problematic. Click here for more details.

Files changed (113) hide show
  1. regscale/_version.py +1 -1
  2. regscale/core/app/utils/app_utils.py +11 -2
  3. regscale/dev/cli.py +26 -0
  4. regscale/dev/version.py +72 -0
  5. regscale/integrations/commercial/__init__.py +15 -1
  6. regscale/integrations/commercial/amazon/amazon/__init__.py +0 -0
  7. regscale/integrations/commercial/amazon/amazon/common.py +204 -0
  8. regscale/integrations/commercial/amazon/common.py +48 -58
  9. regscale/integrations/commercial/aws/audit_manager_compliance.py +2671 -0
  10. regscale/integrations/commercial/aws/cli.py +3093 -55
  11. regscale/integrations/commercial/aws/cloudtrail_control_mappings.py +333 -0
  12. regscale/integrations/commercial/aws/cloudtrail_evidence.py +501 -0
  13. regscale/integrations/commercial/aws/cloudwatch_control_mappings.py +357 -0
  14. regscale/integrations/commercial/aws/cloudwatch_evidence.py +490 -0
  15. regscale/integrations/commercial/aws/config_compliance.py +914 -0
  16. regscale/integrations/commercial/aws/conformance_pack_mappings.py +198 -0
  17. regscale/integrations/commercial/aws/evidence_generator.py +283 -0
  18. regscale/integrations/commercial/aws/guardduty_control_mappings.py +340 -0
  19. regscale/integrations/commercial/aws/guardduty_evidence.py +1053 -0
  20. regscale/integrations/commercial/aws/iam_control_mappings.py +368 -0
  21. regscale/integrations/commercial/aws/iam_evidence.py +574 -0
  22. regscale/integrations/commercial/aws/inventory/__init__.py +223 -22
  23. regscale/integrations/commercial/aws/inventory/base.py +107 -5
  24. regscale/integrations/commercial/aws/inventory/resources/audit_manager.py +513 -0
  25. regscale/integrations/commercial/aws/inventory/resources/cloudtrail.py +315 -0
  26. regscale/integrations/commercial/aws/inventory/resources/cloudtrail_logs_metadata.py +476 -0
  27. regscale/integrations/commercial/aws/inventory/resources/cloudwatch.py +191 -0
  28. regscale/integrations/commercial/aws/inventory/resources/compute.py +66 -9
  29. regscale/integrations/commercial/aws/inventory/resources/config.py +464 -0
  30. regscale/integrations/commercial/aws/inventory/resources/containers.py +74 -9
  31. regscale/integrations/commercial/aws/inventory/resources/database.py +106 -31
  32. regscale/integrations/commercial/aws/inventory/resources/guardduty.py +286 -0
  33. regscale/integrations/commercial/aws/inventory/resources/iam.py +470 -0
  34. regscale/integrations/commercial/aws/inventory/resources/inspector.py +476 -0
  35. regscale/integrations/commercial/aws/inventory/resources/integration.py +175 -61
  36. regscale/integrations/commercial/aws/inventory/resources/kms.py +447 -0
  37. regscale/integrations/commercial/aws/inventory/resources/networking.py +103 -67
  38. regscale/integrations/commercial/aws/inventory/resources/s3.py +394 -0
  39. regscale/integrations/commercial/aws/inventory/resources/security.py +268 -72
  40. regscale/integrations/commercial/aws/inventory/resources/securityhub.py +473 -0
  41. regscale/integrations/commercial/aws/inventory/resources/storage.py +53 -29
  42. regscale/integrations/commercial/aws/inventory/resources/systems_manager.py +657 -0
  43. regscale/integrations/commercial/aws/inventory/resources/vpc.py +655 -0
  44. regscale/integrations/commercial/aws/kms_control_mappings.py +288 -0
  45. regscale/integrations/commercial/aws/kms_evidence.py +879 -0
  46. regscale/integrations/commercial/aws/ocsf/__init__.py +7 -0
  47. regscale/integrations/commercial/aws/ocsf/constants.py +115 -0
  48. regscale/integrations/commercial/aws/ocsf/mapper.py +435 -0
  49. regscale/integrations/commercial/aws/org_control_mappings.py +286 -0
  50. regscale/integrations/commercial/aws/org_evidence.py +666 -0
  51. regscale/integrations/commercial/aws/s3_control_mappings.py +356 -0
  52. regscale/integrations/commercial/aws/s3_evidence.py +632 -0
  53. regscale/integrations/commercial/aws/scanner.py +851 -206
  54. regscale/integrations/commercial/aws/security_hub.py +319 -0
  55. regscale/integrations/commercial/aws/session_manager.py +282 -0
  56. regscale/integrations/commercial/aws/ssm_control_mappings.py +291 -0
  57. regscale/integrations/commercial/aws/ssm_evidence.py +492 -0
  58. regscale/integrations/commercial/synqly/ticketing.py +27 -0
  59. regscale/integrations/compliance_integration.py +308 -38
  60. regscale/integrations/due_date_handler.py +3 -0
  61. regscale/integrations/scanner_integration.py +399 -84
  62. regscale/models/integration_models/cisa_kev_data.json +65 -5
  63. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  64. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +17 -9
  65. regscale/models/regscale_models/assessment.py +2 -1
  66. regscale/models/regscale_models/control_objective.py +74 -5
  67. regscale/models/regscale_models/file.py +2 -0
  68. regscale/models/regscale_models/issue.py +2 -5
  69. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/METADATA +1 -1
  70. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/RECORD +113 -34
  71. tests/regscale/integrations/commercial/aws/__init__.py +0 -0
  72. tests/regscale/integrations/commercial/aws/test_audit_manager_compliance.py +1304 -0
  73. tests/regscale/integrations/commercial/aws/test_audit_manager_evidence_aggregation.py +341 -0
  74. tests/regscale/integrations/commercial/aws/test_aws_audit_manager_collector.py +1155 -0
  75. tests/regscale/integrations/commercial/aws/test_aws_cloudtrail_collector.py +534 -0
  76. tests/regscale/integrations/commercial/aws/test_aws_config_collector.py +400 -0
  77. tests/regscale/integrations/commercial/aws/test_aws_guardduty_collector.py +315 -0
  78. tests/regscale/integrations/commercial/aws/test_aws_iam_collector.py +458 -0
  79. tests/regscale/integrations/commercial/aws/test_aws_inspector_collector.py +353 -0
  80. tests/regscale/integrations/commercial/aws/test_aws_inventory_integration.py +530 -0
  81. tests/regscale/integrations/commercial/aws/test_aws_kms_collector.py +919 -0
  82. tests/regscale/integrations/commercial/aws/test_aws_s3_collector.py +722 -0
  83. tests/regscale/integrations/commercial/aws/test_aws_scanner_integration.py +722 -0
  84. tests/regscale/integrations/commercial/aws/test_aws_securityhub_collector.py +792 -0
  85. tests/regscale/integrations/commercial/aws/test_aws_systems_manager_collector.py +918 -0
  86. tests/regscale/integrations/commercial/aws/test_aws_vpc_collector.py +996 -0
  87. tests/regscale/integrations/commercial/aws/test_cli_evidence.py +431 -0
  88. tests/regscale/integrations/commercial/aws/test_cloudtrail_control_mappings.py +452 -0
  89. tests/regscale/integrations/commercial/aws/test_cloudtrail_evidence.py +788 -0
  90. tests/regscale/integrations/commercial/aws/test_config_compliance.py +298 -0
  91. tests/regscale/integrations/commercial/aws/test_conformance_pack_mappings.py +200 -0
  92. tests/regscale/integrations/commercial/aws/test_evidence_generator.py +386 -0
  93. tests/regscale/integrations/commercial/aws/test_guardduty_control_mappings.py +564 -0
  94. tests/regscale/integrations/commercial/aws/test_guardduty_evidence.py +1041 -0
  95. tests/regscale/integrations/commercial/aws/test_iam_control_mappings.py +718 -0
  96. tests/regscale/integrations/commercial/aws/test_iam_evidence.py +1375 -0
  97. tests/regscale/integrations/commercial/aws/test_kms_control_mappings.py +656 -0
  98. tests/regscale/integrations/commercial/aws/test_kms_evidence.py +1163 -0
  99. tests/regscale/integrations/commercial/aws/test_ocsf_mapper.py +370 -0
  100. tests/regscale/integrations/commercial/aws/test_org_control_mappings.py +546 -0
  101. tests/regscale/integrations/commercial/aws/test_org_evidence.py +1240 -0
  102. tests/regscale/integrations/commercial/aws/test_s3_control_mappings.py +672 -0
  103. tests/regscale/integrations/commercial/aws/test_s3_evidence.py +987 -0
  104. tests/regscale/integrations/commercial/aws/test_scanner_evidence.py +373 -0
  105. tests/regscale/integrations/commercial/aws/test_security_hub_config_filtering.py +539 -0
  106. tests/regscale/integrations/commercial/aws/test_session_manager.py +516 -0
  107. tests/regscale/integrations/commercial/aws/test_ssm_control_mappings.py +588 -0
  108. tests/regscale/integrations/commercial/aws/test_ssm_evidence.py +735 -0
  109. tests/regscale/integrations/commercial/test_aws.py +55 -56
  110. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/LICENSE +0 -0
  111. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/WHEEL +0 -0
  112. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/entry_points.txt +0 -0
  113. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """OCSF (Open Cybersecurity Schema Framework) mapper for AWS services"""
4
+
5
+ from regscale.integrations.commercial.aws.ocsf.mapper import AWSOCSFMapper
6
+
7
+ __all__ = ["AWSOCSFMapper"]
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """OCSF constants for AWS service mappings"""
4
+
5
+ # OCSF Version
6
+ OCSF_VERSION = "1.3.0"
7
+
8
+ # OCSF Class UIDs
9
+ # Reference: https://schema.ocsf.io/1.3.0/classes
10
+ CLASS_SECURITY_FINDING = 2001 # Vulnerability Finding
11
+ CLASS_COMPLIANCE_FINDING = 2003 # Compliance Finding
12
+ CLASS_DETECTION_FINDING = 2004 # Detection Finding
13
+ CLASS_CLOUD_API = 3005 # Cloud API Activity
14
+
15
+ # Activity IDs for Detection Finding (Class 2004)
16
+ ACTIVITY_CREATE = 1
17
+ ACTIVITY_UPDATE = 2
18
+ ACTIVITY_CLOSE = 3
19
+ ACTIVITY_OTHER = 99
20
+
21
+ # Finding Status IDs
22
+ STATUS_NEW = 1
23
+ STATUS_IN_PROGRESS = 2
24
+ STATUS_SUPPRESSED = 3
25
+ STATUS_RESOLVED = 4
26
+ STATUS_OTHER = 99
27
+
28
+ # Severity IDs
29
+ SEVERITY_UNKNOWN = 0
30
+ SEVERITY_INFORMATIONAL = 1
31
+ SEVERITY_LOW = 2
32
+ SEVERITY_MEDIUM = 3
33
+ SEVERITY_HIGH = 4
34
+ SEVERITY_CRITICAL = 5
35
+ SEVERITY_FATAL = 6
36
+
37
+ # Confidence IDs
38
+ CONFIDENCE_UNKNOWN = 0
39
+ CONFIDENCE_LOW = 1
40
+ CONFIDENCE_MEDIUM = 2
41
+ CONFIDENCE_HIGH = 3
42
+
43
+ # AWS Service to OCSF Class UID Mapping
44
+ SERVICE_CLASS_MAPPING = {
45
+ "GuardDuty": CLASS_DETECTION_FINDING,
46
+ "SecurityHub": CLASS_SECURITY_FINDING, # Can also be CLASS_COMPLIANCE_FINDING
47
+ "CloudTrail": CLASS_CLOUD_API,
48
+ "Inspector": CLASS_SECURITY_FINDING,
49
+ }
50
+
51
+ # GuardDuty Severity to OCSF Severity Mapping
52
+ GUARDDUTY_SEVERITY_MAP = {
53
+ # GuardDuty uses 0.0-8.9 scale
54
+ # 7.0-8.9: High
55
+ # 4.0-6.9: Medium
56
+ # 0.1-3.9: Low
57
+ }
58
+
59
+
60
+ def map_guardduty_severity(severity: float) -> int:
61
+ """
62
+ Map GuardDuty numeric severity to OCSF severity ID
63
+
64
+ :param float severity: GuardDuty severity (0.0-8.9)
65
+ :return: OCSF severity ID
66
+ :rtype: int
67
+ """
68
+ if severity >= 7.0:
69
+ return SEVERITY_HIGH
70
+ elif severity >= 4.0:
71
+ return SEVERITY_MEDIUM
72
+ elif severity > 0:
73
+ return SEVERITY_LOW
74
+ return SEVERITY_UNKNOWN
75
+
76
+
77
+ # Security Hub Severity to OCSF Severity Mapping
78
+ SECURITYHUB_SEVERITY_MAP = {
79
+ "CRITICAL": SEVERITY_CRITICAL,
80
+ "HIGH": SEVERITY_HIGH,
81
+ "MEDIUM": SEVERITY_MEDIUM,
82
+ "LOW": SEVERITY_LOW,
83
+ "INFORMATIONAL": SEVERITY_INFORMATIONAL,
84
+ }
85
+
86
+
87
+ def map_securityhub_severity(severity_label: str) -> int:
88
+ """
89
+ Map Security Hub severity label to OCSF severity ID
90
+
91
+ :param str severity_label: Security Hub severity label
92
+ :return: OCSF severity ID
93
+ :rtype: int
94
+ """
95
+ return SECURITYHUB_SEVERITY_MAP.get(severity_label.upper(), SEVERITY_UNKNOWN)
96
+
97
+
98
+ # Security Hub Workflow Status to OCSF Status Mapping
99
+ SECURITYHUB_STATUS_MAP = {
100
+ "NEW": STATUS_NEW,
101
+ "NOTIFIED": STATUS_IN_PROGRESS,
102
+ "SUPPRESSED": STATUS_SUPPRESSED,
103
+ "RESOLVED": STATUS_RESOLVED,
104
+ }
105
+
106
+
107
+ def map_securityhub_status(workflow_status: str) -> int:
108
+ """
109
+ Map Security Hub workflow status to OCSF status ID
110
+
111
+ :param str workflow_status: Security Hub workflow status
112
+ :return: OCSF status ID
113
+ :rtype: int
114
+ """
115
+ return SECURITYHUB_STATUS_MAP.get(workflow_status.upper(), STATUS_OTHER)
@@ -0,0 +1,435 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """AWS to OCSF mapper for normalizing AWS security findings"""
4
+
5
+ import logging
6
+ from datetime import datetime
7
+ from typing import Optional
8
+
9
+ from regscale.integrations.commercial.aws.ocsf import constants
10
+
11
+ logger = logging.getLogger("regscale")
12
+
13
+ # Constants
14
+ AWS_VENDOR_NAME = "Amazon Web Services"
15
+
16
+
17
+ class AWSOCSFMapper:
18
+ """Maps AWS security findings to OCSF (Open Cybersecurity Schema Framework) format"""
19
+
20
+ def __init__(self):
21
+ """Initialize OCSF mapper"""
22
+ self.ocsf_version = constants.OCSF_VERSION
23
+
24
+ def guardduty_to_ocsf(self, finding: dict) -> dict:
25
+ """
26
+ Map AWS GuardDuty finding to OCSF Detection Finding (Class 2004)
27
+
28
+ :param dict finding: GuardDuty finding in native AWS format
29
+ :return: OCSF-formatted detection finding
30
+ :rtype: dict
31
+ """
32
+ severity_score = finding.get("Severity", 0)
33
+ severity_id = constants.map_guardduty_severity(severity_score)
34
+
35
+ # Extract resource information
36
+ resource = finding.get("Resource", {})
37
+ resource_type = resource.get("ResourceType", "Unknown")
38
+
39
+ # Build OCSF finding
40
+ ocsf_finding = {
41
+ "metadata": {
42
+ "version": self.ocsf_version,
43
+ "product": {
44
+ "name": "AWS GuardDuty",
45
+ "vendor_name": AWS_VENDOR_NAME,
46
+ },
47
+ "logged_time": self._parse_aws_timestamp(finding.get("UpdatedAt")),
48
+ },
49
+ "class_uid": constants.CLASS_DETECTION_FINDING,
50
+ "class_name": "Detection Finding",
51
+ "category_uid": 2, # Findings
52
+ "category_name": "Findings",
53
+ "activity_id": self._map_guardduty_activity(finding),
54
+ "activity_name": self._get_activity_name(self._map_guardduty_activity(finding)),
55
+ "severity_id": severity_id,
56
+ "severity": self._get_severity_name(severity_id),
57
+ "confidence_id": self._map_guardduty_confidence(finding.get("Confidence", 0)),
58
+ "finding_info": {
59
+ "uid": finding.get("Id"),
60
+ "title": finding.get("Title", ""),
61
+ "desc": finding.get("Description", ""),
62
+ "types": [finding.get("Type", "")],
63
+ "created_time": self._parse_aws_timestamp(finding.get("CreatedAt")),
64
+ "modified_time": self._parse_aws_timestamp(finding.get("UpdatedAt")),
65
+ "product_uid": finding.get("Arn", ""),
66
+ },
67
+ "resources": [
68
+ {
69
+ "uid": resource.get("AccessKeyDetails", {}).get("AccessKeyId")
70
+ or resource.get("InstanceDetails", {}).get("InstanceId")
71
+ or "unknown",
72
+ "type": resource_type,
73
+ "cloud_partition": finding.get("Partition", "aws"),
74
+ "region": finding.get("Region", ""),
75
+ }
76
+ ],
77
+ "cloud": {
78
+ "provider": "AWS",
79
+ "account": {
80
+ "uid": finding.get("AccountId", ""),
81
+ },
82
+ "region": finding.get("Region", ""),
83
+ },
84
+ "raw_data": finding,
85
+ }
86
+
87
+ return ocsf_finding
88
+
89
+ def securityhub_to_ocsf(self, finding: dict) -> dict:
90
+ """
91
+ Map AWS Security Hub finding to OCSF Security Finding (Class 2001)
92
+
93
+ :param dict finding: Security Hub finding in native AWS format
94
+ :return: OCSF-formatted security finding
95
+ :rtype: dict
96
+ """
97
+ severity = finding.get("Severity", {})
98
+ severity_label = severity.get("Label", "INFORMATIONAL")
99
+ severity_id = constants.map_securityhub_severity(severity_label)
100
+
101
+ # Extract workflow status
102
+ workflow = finding.get("Workflow", {})
103
+ workflow_status = workflow.get("Status", "NEW")
104
+ status_id = constants.map_securityhub_status(workflow_status)
105
+
106
+ # Extract resource information
107
+ resources = finding.get("Resources", [{}])
108
+
109
+ # Build OCSF finding
110
+ ocsf_finding = {
111
+ "metadata": {
112
+ "version": self.ocsf_version,
113
+ "product": {
114
+ "name": "AWS Security Hub",
115
+ "vendor_name": AWS_VENDOR_NAME,
116
+ "feature": {
117
+ "name": finding.get("ProductName", ""),
118
+ },
119
+ },
120
+ "logged_time": self._parse_aws_timestamp(finding.get("UpdatedAt")),
121
+ },
122
+ "class_uid": constants.CLASS_SECURITY_FINDING,
123
+ "class_name": "Security Finding",
124
+ "category_uid": 2, # Findings
125
+ "category_name": "Findings",
126
+ "activity_id": self._map_securityhub_activity(workflow_status),
127
+ "activity_name": self._get_activity_name(self._map_securityhub_activity(workflow_status)),
128
+ "severity_id": severity_id,
129
+ "severity": self._get_severity_name(severity_id),
130
+ "status_id": status_id,
131
+ "status": self._get_status_name(status_id),
132
+ "finding_info": {
133
+ "uid": finding.get("Id"),
134
+ "title": finding.get("Title", ""),
135
+ "desc": finding.get("Description", ""),
136
+ "types": finding.get("Types", []),
137
+ "created_time": self._parse_aws_timestamp(finding.get("CreatedAt")),
138
+ "modified_time": self._parse_aws_timestamp(finding.get("UpdatedAt")),
139
+ "first_seen_time": self._parse_aws_timestamp(finding.get("FirstObservedAt")),
140
+ "last_seen_time": self._parse_aws_timestamp(finding.get("LastObservedAt")),
141
+ "product_uid": finding.get("ProductArn", ""),
142
+ },
143
+ "compliance": {
144
+ "status": finding.get("Compliance", {}).get("Status", ""),
145
+ "requirements": finding.get("Compliance", {}).get("RelatedRequirements", []),
146
+ },
147
+ "resources": self._map_securityhub_resources(resources),
148
+ "cloud": {
149
+ "provider": "AWS",
150
+ "account": {
151
+ "uid": finding.get("AwsAccountId", ""),
152
+ },
153
+ "region": finding.get("Region", ""),
154
+ },
155
+ "vulnerabilities": self._extract_vulnerabilities(finding),
156
+ "remediation": {
157
+ "desc": finding.get("Remediation", {}).get("Recommendation", {}).get("Text", ""),
158
+ "references": finding.get("Remediation", {}).get("Recommendation", {}).get("Url", ""),
159
+ },
160
+ "raw_data": finding,
161
+ }
162
+
163
+ return ocsf_finding
164
+
165
+ def cloudtrail_event_to_ocsf(self, event: dict) -> dict:
166
+ """
167
+ Map AWS CloudTrail event to OCSF Cloud API Activity (Class 3005)
168
+
169
+ :param dict event: CloudTrail event in native AWS format
170
+ :return: OCSF-formatted cloud API activity
171
+ :rtype: dict
172
+ """
173
+ ocsf_event = {
174
+ "metadata": {
175
+ "version": self.ocsf_version,
176
+ "product": {
177
+ "name": "AWS CloudTrail",
178
+ "vendor_name": AWS_VENDOR_NAME,
179
+ },
180
+ "logged_time": self._parse_aws_timestamp(event.get("EventTime")),
181
+ },
182
+ "class_uid": constants.CLASS_CLOUD_API,
183
+ "class_name": "Cloud API",
184
+ "category_uid": 3, # Cloud Activity
185
+ "category_name": "Cloud Activity",
186
+ "activity_id": 1 if event.get("EventName") else 99,
187
+ "activity_name": event.get("EventName", "Unknown"),
188
+ "severity_id": self._determine_cloudtrail_severity(event),
189
+ "api": {
190
+ "operation": event.get("EventName", ""),
191
+ "service": {
192
+ "name": event.get("EventSource", "").split(".")[0],
193
+ },
194
+ "request": {
195
+ "uid": event.get("RequestID", ""),
196
+ },
197
+ "response": {
198
+ "error": event.get("ErrorCode"),
199
+ "message": event.get("ErrorMessage"),
200
+ },
201
+ },
202
+ "cloud": {
203
+ "provider": "AWS",
204
+ "account": {
205
+ "uid": event.get("RecipientAccountId", ""),
206
+ },
207
+ "region": event.get("AwsRegion", ""),
208
+ },
209
+ "actor": {
210
+ "user": {
211
+ "name": event.get("UserIdentity", {}).get("UserName"),
212
+ "uid": event.get("UserIdentity", {}).get("PrincipalId"),
213
+ "type": event.get("UserIdentity", {}).get("Type"),
214
+ },
215
+ },
216
+ "src_endpoint": {
217
+ "ip": event.get("SourceIPAddress"),
218
+ "domain": event.get("UserAgent"),
219
+ },
220
+ "http_request": {
221
+ "user_agent": event.get("UserAgent"),
222
+ },
223
+ "resources": self._map_cloudtrail_resources(event.get("Resources", [])),
224
+ "raw_data": event,
225
+ }
226
+
227
+ return ocsf_event
228
+
229
+ def _map_guardduty_activity(self, finding: dict) -> int:
230
+ """
231
+ Map GuardDuty finding to OCSF activity ID
232
+
233
+ :param dict finding: GuardDuty finding
234
+ :return: OCSF activity ID
235
+ :rtype: int
236
+ """
237
+ service_info = finding.get("Service", {})
238
+ if service_info.get("Archived"):
239
+ return constants.ACTIVITY_CLOSE
240
+ elif service_info.get("Count", 1) == 1:
241
+ return constants.ACTIVITY_CREATE
242
+ else:
243
+ return constants.ACTIVITY_UPDATE
244
+
245
+ def _map_securityhub_activity(self, workflow_status: str) -> int:
246
+ """
247
+ Map Security Hub workflow status to OCSF activity ID
248
+
249
+ :param str workflow_status: Security Hub workflow status
250
+ :return: OCSF activity ID
251
+ :rtype: int
252
+ """
253
+ status_activity_map = {
254
+ "NEW": constants.ACTIVITY_CREATE,
255
+ "NOTIFIED": constants.ACTIVITY_UPDATE,
256
+ "RESOLVED": constants.ACTIVITY_CLOSE,
257
+ "SUPPRESSED": constants.ACTIVITY_CLOSE,
258
+ }
259
+ return status_activity_map.get(workflow_status.upper(), constants.ACTIVITY_OTHER)
260
+
261
+ def _map_guardduty_confidence(self, confidence: float) -> int:
262
+ """
263
+ Map GuardDuty confidence score to OCSF confidence ID
264
+
265
+ :param float confidence: GuardDuty confidence (0.0-10.0)
266
+ :return: OCSF confidence ID
267
+ :rtype: int
268
+ """
269
+ if confidence >= 7.0:
270
+ return constants.CONFIDENCE_HIGH
271
+ elif confidence >= 4.0:
272
+ return constants.CONFIDENCE_MEDIUM
273
+ elif confidence > 0:
274
+ return constants.CONFIDENCE_LOW
275
+ return constants.CONFIDENCE_UNKNOWN
276
+
277
+ def _map_securityhub_resources(self, resources: list) -> list:
278
+ """
279
+ Map Security Hub resources to OCSF resource format
280
+
281
+ :param list resources: Security Hub resources
282
+ :return: OCSF resources
283
+ :rtype: list
284
+ """
285
+ ocsf_resources = []
286
+ for resource in resources:
287
+ ocsf_resource = {
288
+ "uid": resource.get("Id", ""),
289
+ "type": resource.get("Type", ""),
290
+ "region": resource.get("Region", ""),
291
+ "partition": resource.get("Partition", "aws"),
292
+ }
293
+
294
+ # Add resource details if available
295
+ details = resource.get("Details", {})
296
+ if details:
297
+ ocsf_resource["data"] = details
298
+
299
+ ocsf_resources.append(ocsf_resource)
300
+
301
+ return ocsf_resources
302
+
303
+ def _map_cloudtrail_resources(self, resources: list) -> list:
304
+ """
305
+ Map CloudTrail resources to OCSF resource format
306
+
307
+ :param list resources: CloudTrail resources
308
+ :return: OCSF resources
309
+ :rtype: list
310
+ """
311
+ ocsf_resources = []
312
+ for resource in resources:
313
+ ocsf_resources.append(
314
+ {
315
+ "uid": resource.get("ARN", ""),
316
+ "type": resource.get("ResourceType", ""),
317
+ "name": resource.get("ResourceName", ""),
318
+ }
319
+ )
320
+ return ocsf_resources
321
+
322
+ def _extract_vulnerabilities(self, finding: dict) -> list:
323
+ """
324
+ Extract vulnerability information from Security Hub finding
325
+
326
+ :param dict finding: Security Hub finding
327
+ :return: OCSF vulnerabilities
328
+ :rtype: list
329
+ """
330
+ vulnerabilities = finding.get("Vulnerabilities", [])
331
+ ocsf_vulns = []
332
+
333
+ for vuln in vulnerabilities:
334
+ ocsf_vuln = {
335
+ "cve": {
336
+ "uid": vuln.get("Id", ""),
337
+ },
338
+ "references": vuln.get("ReferenceUrls", []),
339
+ "vendor_name": vuln.get("Vendor", {}).get("Name", ""),
340
+ }
341
+
342
+ # Add CVSS scores if available
343
+ cvss = vuln.get("Cvss", [])
344
+ if cvss:
345
+ ocsf_vuln["cvss"] = cvss
346
+
347
+ ocsf_vulns.append(ocsf_vuln)
348
+
349
+ return ocsf_vulns if ocsf_vulns else None
350
+
351
+ def _determine_cloudtrail_severity(self, event: dict) -> int:
352
+ """
353
+ Determine severity for CloudTrail event based on error codes
354
+
355
+ :param dict event: CloudTrail event
356
+ :return: OCSF severity ID
357
+ :rtype: int
358
+ """
359
+ error_code = event.get("ErrorCode")
360
+ if error_code:
361
+ # Failed API calls are more severe
362
+ if error_code in ["UnauthorizedOperation", "AccessDenied"]:
363
+ return constants.SEVERITY_MEDIUM
364
+ return constants.SEVERITY_LOW
365
+ return constants.SEVERITY_INFORMATIONAL
366
+
367
+ def _parse_aws_timestamp(self, timestamp: Optional[str]) -> Optional[int]:
368
+ """
369
+ Parse AWS timestamp to Unix epoch milliseconds
370
+
371
+ :param Optional[str] timestamp: AWS timestamp string
372
+ :return: Unix epoch milliseconds or None
373
+ :rtype: Optional[int]
374
+ """
375
+ if not timestamp:
376
+ return None
377
+
378
+ try:
379
+ dt = datetime.fromisoformat(timestamp.replace("Z", "+00:00"))
380
+ return int(dt.timestamp() * 1000)
381
+ except (ValueError, AttributeError) as ex:
382
+ logger.warning("Failed to parse timestamp %s: %s", timestamp, ex)
383
+ return None
384
+
385
+ def _get_severity_name(self, severity_id: int) -> str:
386
+ """
387
+ Get OCSF severity name from severity ID
388
+
389
+ :param int severity_id: OCSF severity ID
390
+ :return: Severity name
391
+ :rtype: str
392
+ """
393
+ severity_names = {
394
+ constants.SEVERITY_UNKNOWN: "Unknown",
395
+ constants.SEVERITY_INFORMATIONAL: "Informational",
396
+ constants.SEVERITY_LOW: "Low",
397
+ constants.SEVERITY_MEDIUM: "Medium",
398
+ constants.SEVERITY_HIGH: "High",
399
+ constants.SEVERITY_CRITICAL: "Critical",
400
+ constants.SEVERITY_FATAL: "Fatal",
401
+ }
402
+ return severity_names.get(severity_id, "Unknown")
403
+
404
+ def _get_status_name(self, status_id: int) -> str:
405
+ """
406
+ Get OCSF status name from status ID
407
+
408
+ :param int status_id: OCSF status ID
409
+ :return: Status name
410
+ :rtype: str
411
+ """
412
+ status_names = {
413
+ constants.STATUS_NEW: "New",
414
+ constants.STATUS_IN_PROGRESS: "In Progress",
415
+ constants.STATUS_SUPPRESSED: "Suppressed",
416
+ constants.STATUS_RESOLVED: "Resolved",
417
+ constants.STATUS_OTHER: "Other",
418
+ }
419
+ return status_names.get(status_id, "Other")
420
+
421
+ def _get_activity_name(self, activity_id: int) -> str:
422
+ """
423
+ Get OCSF activity name from activity ID
424
+
425
+ :param int activity_id: OCSF activity ID
426
+ :return: Activity name
427
+ :rtype: str
428
+ """
429
+ activity_names = {
430
+ constants.ACTIVITY_CREATE: "Create",
431
+ constants.ACTIVITY_UPDATE: "Update",
432
+ constants.ACTIVITY_CLOSE: "Close",
433
+ constants.ACTIVITY_OTHER: "Other",
434
+ }
435
+ return activity_names.get(activity_id, "Other")