regscale-cli 6.27.3.0__py3-none-any.whl → 6.28.0.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.
- regscale/_version.py +1 -1
- regscale/core/app/utils/app_utils.py +11 -2
- regscale/dev/cli.py +26 -0
- regscale/dev/version.py +72 -0
- regscale/integrations/commercial/__init__.py +15 -1
- regscale/integrations/commercial/amazon/amazon/__init__.py +0 -0
- regscale/integrations/commercial/amazon/amazon/common.py +204 -0
- regscale/integrations/commercial/amazon/common.py +48 -58
- regscale/integrations/commercial/aws/audit_manager_compliance.py +2671 -0
- regscale/integrations/commercial/aws/cli.py +3093 -55
- regscale/integrations/commercial/aws/cloudtrail_control_mappings.py +333 -0
- regscale/integrations/commercial/aws/cloudtrail_evidence.py +501 -0
- regscale/integrations/commercial/aws/cloudwatch_control_mappings.py +357 -0
- regscale/integrations/commercial/aws/cloudwatch_evidence.py +490 -0
- regscale/integrations/commercial/aws/config_compliance.py +914 -0
- regscale/integrations/commercial/aws/conformance_pack_mappings.py +198 -0
- regscale/integrations/commercial/aws/evidence_generator.py +283 -0
- regscale/integrations/commercial/aws/guardduty_control_mappings.py +340 -0
- regscale/integrations/commercial/aws/guardduty_evidence.py +1053 -0
- regscale/integrations/commercial/aws/iam_control_mappings.py +368 -0
- regscale/integrations/commercial/aws/iam_evidence.py +574 -0
- regscale/integrations/commercial/aws/inventory/__init__.py +223 -22
- regscale/integrations/commercial/aws/inventory/base.py +107 -5
- regscale/integrations/commercial/aws/inventory/resources/audit_manager.py +513 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudtrail.py +315 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudtrail_logs_metadata.py +476 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudwatch.py +191 -0
- regscale/integrations/commercial/aws/inventory/resources/compute.py +66 -9
- regscale/integrations/commercial/aws/inventory/resources/config.py +464 -0
- regscale/integrations/commercial/aws/inventory/resources/containers.py +74 -9
- regscale/integrations/commercial/aws/inventory/resources/database.py +106 -31
- regscale/integrations/commercial/aws/inventory/resources/guardduty.py +286 -0
- regscale/integrations/commercial/aws/inventory/resources/iam.py +470 -0
- regscale/integrations/commercial/aws/inventory/resources/inspector.py +476 -0
- regscale/integrations/commercial/aws/inventory/resources/integration.py +175 -61
- regscale/integrations/commercial/aws/inventory/resources/kms.py +447 -0
- regscale/integrations/commercial/aws/inventory/resources/networking.py +103 -67
- regscale/integrations/commercial/aws/inventory/resources/s3.py +394 -0
- regscale/integrations/commercial/aws/inventory/resources/security.py +268 -72
- regscale/integrations/commercial/aws/inventory/resources/securityhub.py +473 -0
- regscale/integrations/commercial/aws/inventory/resources/storage.py +53 -29
- regscale/integrations/commercial/aws/inventory/resources/systems_manager.py +657 -0
- regscale/integrations/commercial/aws/inventory/resources/vpc.py +655 -0
- regscale/integrations/commercial/aws/kms_control_mappings.py +288 -0
- regscale/integrations/commercial/aws/kms_evidence.py +879 -0
- regscale/integrations/commercial/aws/ocsf/__init__.py +7 -0
- regscale/integrations/commercial/aws/ocsf/constants.py +115 -0
- regscale/integrations/commercial/aws/ocsf/mapper.py +435 -0
- regscale/integrations/commercial/aws/org_control_mappings.py +286 -0
- regscale/integrations/commercial/aws/org_evidence.py +666 -0
- regscale/integrations/commercial/aws/s3_control_mappings.py +356 -0
- regscale/integrations/commercial/aws/s3_evidence.py +632 -0
- regscale/integrations/commercial/aws/scanner.py +851 -206
- regscale/integrations/commercial/aws/security_hub.py +319 -0
- regscale/integrations/commercial/aws/session_manager.py +282 -0
- regscale/integrations/commercial/aws/ssm_control_mappings.py +291 -0
- regscale/integrations/commercial/aws/ssm_evidence.py +492 -0
- regscale/integrations/compliance_integration.py +308 -38
- regscale/integrations/due_date_handler.py +3 -0
- regscale/integrations/scanner_integration.py +399 -84
- regscale/models/integration_models/cisa_kev_data.json +34 -4
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +17 -9
- regscale/models/regscale_models/assessment.py +2 -1
- regscale/models/regscale_models/control_objective.py +74 -5
- regscale/models/regscale_models/file.py +2 -0
- regscale/models/regscale_models/issue.py +2 -5
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/METADATA +1 -1
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/RECORD +112 -33
- tests/regscale/integrations/commercial/aws/__init__.py +0 -0
- tests/regscale/integrations/commercial/aws/test_audit_manager_compliance.py +1304 -0
- tests/regscale/integrations/commercial/aws/test_audit_manager_evidence_aggregation.py +341 -0
- tests/regscale/integrations/commercial/aws/test_aws_audit_manager_collector.py +1155 -0
- tests/regscale/integrations/commercial/aws/test_aws_cloudtrail_collector.py +534 -0
- tests/regscale/integrations/commercial/aws/test_aws_config_collector.py +400 -0
- tests/regscale/integrations/commercial/aws/test_aws_guardduty_collector.py +315 -0
- tests/regscale/integrations/commercial/aws/test_aws_iam_collector.py +458 -0
- tests/regscale/integrations/commercial/aws/test_aws_inspector_collector.py +353 -0
- tests/regscale/integrations/commercial/aws/test_aws_inventory_integration.py +530 -0
- tests/regscale/integrations/commercial/aws/test_aws_kms_collector.py +919 -0
- tests/regscale/integrations/commercial/aws/test_aws_s3_collector.py +722 -0
- tests/regscale/integrations/commercial/aws/test_aws_scanner_integration.py +722 -0
- tests/regscale/integrations/commercial/aws/test_aws_securityhub_collector.py +792 -0
- tests/regscale/integrations/commercial/aws/test_aws_systems_manager_collector.py +918 -0
- tests/regscale/integrations/commercial/aws/test_aws_vpc_collector.py +996 -0
- tests/regscale/integrations/commercial/aws/test_cli_evidence.py +431 -0
- tests/regscale/integrations/commercial/aws/test_cloudtrail_control_mappings.py +452 -0
- tests/regscale/integrations/commercial/aws/test_cloudtrail_evidence.py +788 -0
- tests/regscale/integrations/commercial/aws/test_config_compliance.py +298 -0
- tests/regscale/integrations/commercial/aws/test_conformance_pack_mappings.py +200 -0
- tests/regscale/integrations/commercial/aws/test_evidence_generator.py +386 -0
- tests/regscale/integrations/commercial/aws/test_guardduty_control_mappings.py +564 -0
- tests/regscale/integrations/commercial/aws/test_guardduty_evidence.py +1041 -0
- tests/regscale/integrations/commercial/aws/test_iam_control_mappings.py +718 -0
- tests/regscale/integrations/commercial/aws/test_iam_evidence.py +1375 -0
- tests/regscale/integrations/commercial/aws/test_kms_control_mappings.py +656 -0
- tests/regscale/integrations/commercial/aws/test_kms_evidence.py +1163 -0
- tests/regscale/integrations/commercial/aws/test_ocsf_mapper.py +370 -0
- tests/regscale/integrations/commercial/aws/test_org_control_mappings.py +546 -0
- tests/regscale/integrations/commercial/aws/test_org_evidence.py +1240 -0
- tests/regscale/integrations/commercial/aws/test_s3_control_mappings.py +672 -0
- tests/regscale/integrations/commercial/aws/test_s3_evidence.py +987 -0
- tests/regscale/integrations/commercial/aws/test_scanner_evidence.py +373 -0
- tests/regscale/integrations/commercial/aws/test_security_hub_config_filtering.py +539 -0
- tests/regscale/integrations/commercial/aws/test_session_manager.py +516 -0
- tests/regscale/integrations/commercial/aws/test_ssm_control_mappings.py +588 -0
- tests/regscale/integrations/commercial/aws/test_ssm_evidence.py +735 -0
- tests/regscale/integrations/commercial/test_aws.py +55 -56
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/top_level.txt +0 -0
|
@@ -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")
|