sraverify 0.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.
- sraverify/__init__.py +36 -0
- sraverify/checks/__init__.py +56 -0
- sraverify/checks/accessanalyzer/SRA_IAA_1.py +188 -0
- sraverify/checks/accessanalyzer/SRA_IAA_2.py +162 -0
- sraverify/checks/accessanalyzer/SRA_IAA_3.py +260 -0
- sraverify/checks/accessanalyzer/SRA_IAA_4.py +207 -0
- sraverify/checks/accessanalyzer/__init__.py +3 -0
- sraverify/checks/cloudtrail/SRA-CT-1.py +220 -0
- sraverify/checks/cloudtrail/SRA-CT-10.py +229 -0
- sraverify/checks/cloudtrail/SRA-CT-11.py +242 -0
- sraverify/checks/cloudtrail/SRA-CT-12.py +163 -0
- sraverify/checks/cloudtrail/SRA-CT-13.py +279 -0
- sraverify/checks/cloudtrail/SRA-CT-2.py +218 -0
- sraverify/checks/cloudtrail/SRA-CT-3.py +196 -0
- sraverify/checks/cloudtrail/SRA-CT-4.py +161 -0
- sraverify/checks/cloudtrail/SRA-CT-5.py +200 -0
- sraverify/checks/cloudtrail/SRA-CT-6.py +161 -0
- sraverify/checks/cloudtrail/SRA-CT-7.py +194 -0
- sraverify/checks/cloudtrail/SRA-CT-8.py +226 -0
- sraverify/checks/cloudtrail/SRA-CT-9.py +226 -0
- sraverify/checks/cloudtrail/__init__.py +3 -0
- sraverify/checks/config/SRA-CONFIG-1.py +197 -0
- sraverify/checks/config/__init__.py +3 -0
- sraverify/core/__init__.py +3 -0
- sraverify/core/check.py +227 -0
- sraverify/core/logging.py +37 -0
- sraverify/core/session.py +47 -0
- sraverify/lib/__init__.py +4 -0
- sraverify/lib/audit_info.py +37 -0
- sraverify/lib/banner.py +42 -0
- sraverify/lib/check_loader.py +80 -0
- sraverify/lib/org_mgmt_checker.py +86 -0
- sraverify/lib/outputs.py +46 -0
- sraverify/lib/progress.py +75 -0
- sraverify/lib/regions.py +27 -0
- sraverify/lib/session.py +23 -0
- sraverify/main.py +350 -0
- sraverify/services/__init__.py +3 -0
- sraverify/services/accessanalyzer/__init__.py +15 -0
- sraverify/services/accessanalyzer/base.py +123 -0
- sraverify/services/accessanalyzer/checks/__init__.py +3 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_01.py +82 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_02.py +82 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_03.py +103 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_04.py +139 -0
- sraverify/services/accessanalyzer/client.py +123 -0
- sraverify/services/account/__init__.py +9 -0
- sraverify/services/account/base.py +56 -0
- sraverify/services/account/checks/__init__.py +1 -0
- sraverify/services/account/checks/sra_account_01.py +65 -0
- sraverify/services/account/checks/sra_account_02.py +63 -0
- sraverify/services/account/checks/sra_account_03.py +63 -0
- sraverify/services/account/client.py +51 -0
- sraverify/services/auditmanager/__init__.py +10 -0
- sraverify/services/auditmanager/base.py +72 -0
- sraverify/services/auditmanager/checks/__init__.py +1 -0
- sraverify/services/auditmanager/checks/sra_auditmanager_01.py +58 -0
- sraverify/services/auditmanager/checks/sra_auditmanager_02.py +80 -0
- sraverify/services/auditmanager/client.py +58 -0
- sraverify/services/cloudtrail/__init__.py +33 -0
- sraverify/services/cloudtrail/base.py +167 -0
- sraverify/services/cloudtrail/checks/__init__.py +1 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_01.py +83 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_02.py +99 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_03.py +94 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_04.py +92 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_05.py +106 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_06.py +93 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_07.py +96 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_08.py +145 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_09.py +167 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_10.py +162 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_11.py +178 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_12.py +77 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_13.py +120 -0
- sraverify/services/cloudtrail/client.py +118 -0
- sraverify/services/config/__init__.py +25 -0
- sraverify/services/config/base.py +249 -0
- sraverify/services/config/checks/__init__.py +1 -0
- sraverify/services/config/checks/sra_config_01.py +123 -0
- sraverify/services/config/checks/sra_config_02.py +156 -0
- sraverify/services/config/checks/sra_config_03.py +149 -0
- sraverify/services/config/checks/sra_config_04.py +104 -0
- sraverify/services/config/checks/sra_config_05.py +104 -0
- sraverify/services/config/checks/sra_config_06.py +194 -0
- sraverify/services/config/checks/sra_config_07.py +162 -0
- sraverify/services/config/checks/sra_config_08.py +185 -0
- sraverify/services/config/checks/sra_config_09.py +177 -0
- sraverify/services/config/client.py +264 -0
- sraverify/services/ec2/__init__.py +8 -0
- sraverify/services/ec2/base.py +75 -0
- sraverify/services/ec2/checks/__init__.py +1 -0
- sraverify/services/ec2/checks/sra_ec2_01.py +83 -0
- sraverify/services/ec2/client.py +63 -0
- sraverify/services/firewallmanager/__init__.py +23 -0
- sraverify/services/firewallmanager/base.py +48 -0
- sraverify/services/firewallmanager/checks/__init__.py +1 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_01.py +75 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_02.py +57 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_03.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_04.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_05.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_06.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_07.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_08.py +61 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_09.py +61 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_10.py +71 -0
- sraverify/services/firewallmanager/client.py +40 -0
- sraverify/services/guardduty/__init__.py +58 -0
- sraverify/services/guardduty/base.py +207 -0
- sraverify/services/guardduty/checks/__init__.py +3 -0
- sraverify/services/guardduty/checks/sra_guardduty_01.py +51 -0
- sraverify/services/guardduty/checks/sra_guardduty_02.py +80 -0
- sraverify/services/guardduty/checks/sra_guardduty_03.py +77 -0
- sraverify/services/guardduty/checks/sra_guardduty_04.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_05.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_06.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_07.py +85 -0
- sraverify/services/guardduty/checks/sra_guardduty_08.py +83 -0
- sraverify/services/guardduty/checks/sra_guardduty_09.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_10.py +83 -0
- sraverify/services/guardduty/checks/sra_guardduty_11.py +93 -0
- sraverify/services/guardduty/checks/sra_guardduty_12.py +83 -0
- sraverify/services/guardduty/checks/sra_guardduty_13.py +90 -0
- sraverify/services/guardduty/checks/sra_guardduty_14.py +136 -0
- sraverify/services/guardduty/checks/sra_guardduty_15.py +94 -0
- sraverify/services/guardduty/checks/sra_guardduty_16.py +94 -0
- sraverify/services/guardduty/checks/sra_guardduty_17.py +91 -0
- sraverify/services/guardduty/checks/sra_guardduty_18.py +91 -0
- sraverify/services/guardduty/checks/sra_guardduty_19.py +91 -0
- sraverify/services/guardduty/checks/sra_guardduty_20.py +111 -0
- sraverify/services/guardduty/checks/sra_guardduty_21.py +112 -0
- sraverify/services/guardduty/checks/sra_guardduty_22.py +111 -0
- sraverify/services/guardduty/checks/sra_guardduty_23.py +154 -0
- sraverify/services/guardduty/checks/sra_guardduty_24.py +111 -0
- sraverify/services/guardduty/checks/sra_guardduty_25.py +111 -0
- sraverify/services/guardduty/client.py +107 -0
- sraverify/services/inspector/__init__.py +29 -0
- sraverify/services/inspector/base.py +233 -0
- sraverify/services/inspector/checks/__init__.py +3 -0
- sraverify/services/inspector/checks/sra_inspector_01.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_02.py +68 -0
- sraverify/services/inspector/checks/sra_inspector_03.py +68 -0
- sraverify/services/inspector/checks/sra_inspector_04.py +70 -0
- sraverify/services/inspector/checks/sra_inspector_05.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_06.py +115 -0
- sraverify/services/inspector/checks/sra_inspector_07.py +109 -0
- sraverify/services/inspector/checks/sra_inspector_08.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_09.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_10.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_11.py +69 -0
- sraverify/services/inspector/client.py +99 -0
- sraverify/services/macie/__init__.py +27 -0
- sraverify/services/macie/base.py +271 -0
- sraverify/services/macie/checks/__init__.py +1 -0
- sraverify/services/macie/checks/sra_macie_01.py +100 -0
- sraverify/services/macie/checks/sra_macie_02.py +102 -0
- sraverify/services/macie/checks/sra_macie_03.py +152 -0
- sraverify/services/macie/checks/sra_macie_04.py +120 -0
- sraverify/services/macie/checks/sra_macie_05.py +85 -0
- sraverify/services/macie/checks/sra_macie_06.py +124 -0
- sraverify/services/macie/checks/sra_macie_07.py +138 -0
- sraverify/services/macie/checks/sra_macie_08.py +82 -0
- sraverify/services/macie/checks/sra_macie_09.py +103 -0
- sraverify/services/macie/checks/sra_macie_10.py +81 -0
- sraverify/services/macie/client.py +220 -0
- sraverify/services/s3/__init__.py +16 -0
- sraverify/services/s3/base.py +69 -0
- sraverify/services/s3/checks/__init__.py +1 -0
- sraverify/services/s3/checks/sra_s3_01.py +89 -0
- sraverify/services/s3/checks/sra_s3_02.py +89 -0
- sraverify/services/s3/checks/sra_s3_03.py +88 -0
- sraverify/services/s3/checks/sra_s3_04.py +88 -0
- sraverify/services/s3/client.py +52 -0
- sraverify/services/securityhub/__init__.py +27 -0
- sraverify/services/securityhub/base.py +349 -0
- sraverify/services/securityhub/checks/__init__.py +1 -0
- sraverify/services/securityhub/checks/sra_securityhub_01.py +115 -0
- sraverify/services/securityhub/checks/sra_securityhub_02.py +114 -0
- sraverify/services/securityhub/checks/sra_securityhub_03.py +136 -0
- sraverify/services/securityhub/checks/sra_securityhub_04.py +75 -0
- sraverify/services/securityhub/checks/sra_securityhub_05.py +102 -0
- sraverify/services/securityhub/checks/sra_securityhub_06.py +113 -0
- sraverify/services/securityhub/checks/sra_securityhub_07.py +121 -0
- sraverify/services/securityhub/checks/sra_securityhub_08.py +113 -0
- sraverify/services/securityhub/checks/sra_securityhub_09.py +100 -0
- sraverify/services/securityhub/checks/sra_securityhub_10.py +94 -0
- sraverify/services/securityhub/checks/sra_securityhub_11.py +73 -0
- sraverify/services/securityhub/client.py +249 -0
- sraverify/services/securityincidentresponse/__init__.py +13 -0
- sraverify/services/securityincidentresponse/base.py +95 -0
- sraverify/services/securityincidentresponse/checks/__init__.py +1 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_01.py +77 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_02.py +72 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_03.py +86 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_04.py +117 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_05.py +55 -0
- sraverify/services/securityincidentresponse/client.py +71 -0
- sraverify/services/securitylake/__init__.py +39 -0
- sraverify/services/securitylake/base.py +461 -0
- sraverify/services/securitylake/checks/__init__.py +1 -0
- sraverify/services/securitylake/checks/sra_securitylake_01.py +98 -0
- sraverify/services/securitylake/checks/sra_securitylake_02.py +133 -0
- sraverify/services/securitylake/checks/sra_securitylake_03.py +116 -0
- sraverify/services/securitylake/checks/sra_securitylake_04.py +72 -0
- sraverify/services/securitylake/checks/sra_securitylake_05.py +116 -0
- sraverify/services/securitylake/checks/sra_securitylake_06.py +104 -0
- sraverify/services/securitylake/checks/sra_securitylake_07.py +108 -0
- sraverify/services/securitylake/checks/sra_securitylake_08.py +107 -0
- sraverify/services/securitylake/checks/sra_securitylake_09.py +107 -0
- sraverify/services/securitylake/checks/sra_securitylake_10.py +106 -0
- sraverify/services/securitylake/checks/sra_securitylake_11.py +109 -0
- sraverify/services/securitylake/checks/sra_securitylake_12.py +108 -0
- sraverify/services/securitylake/checks/sra_securitylake_13.py +108 -0
- sraverify/services/securitylake/checks/sra_securitylake_14.py +72 -0
- sraverify/services/securitylake/checks/sra_securitylake_15.py +120 -0
- sraverify/services/securitylake/checks/sra_securitylake_16.py +104 -0
- sraverify/services/securitylake/checks/sra_securitylake_17.py +103 -0
- sraverify/services/securitylake/client.py +247 -0
- sraverify/services/shield/__init__.py +33 -0
- sraverify/services/shield/base.py +199 -0
- sraverify/services/shield/checks/__init__.py +1 -0
- sraverify/services/shield/checks/sra_shield_01.py +68 -0
- sraverify/services/shield/checks/sra_shield_02.py +77 -0
- sraverify/services/shield/checks/sra_shield_03.py +84 -0
- sraverify/services/shield/checks/sra_shield_04.py +84 -0
- sraverify/services/shield/checks/sra_shield_05.py +84 -0
- sraverify/services/shield/checks/sra_shield_06.py +84 -0
- sraverify/services/shield/checks/sra_shield_07.py +84 -0
- sraverify/services/shield/checks/sra_shield_08.py +69 -0
- sraverify/services/shield/checks/sra_shield_09.py +86 -0
- sraverify/services/shield/checks/sra_shield_10.py +100 -0
- sraverify/services/shield/checks/sra_shield_11.py +71 -0
- sraverify/services/shield/checks/sra_shield_12.py +130 -0
- sraverify/services/shield/checks/sra_shield_13.py +112 -0
- sraverify/services/shield/checks/sra_shield_14.py +111 -0
- sraverify/services/shield/client.py +214 -0
- sraverify/services/waf/__init__.py +21 -0
- sraverify/services/waf/base.py +100 -0
- sraverify/services/waf/checks/__init__.py +1 -0
- sraverify/services/waf/checks/sra_waf_01.py +63 -0
- sraverify/services/waf/checks/sra_waf_02.py +82 -0
- sraverify/services/waf/checks/sra_waf_03.py +123 -0
- sraverify/services/waf/checks/sra_waf_04.py +94 -0
- sraverify/services/waf/checks/sra_waf_05.py +94 -0
- sraverify/services/waf/checks/sra_waf_06.py +91 -0
- sraverify/services/waf/checks/sra_waf_07.py +94 -0
- sraverify/services/waf/checks/sra_waf_08.py +66 -0
- sraverify/services/waf/checks/sra_waf_09.py +95 -0
- sraverify/services/waf/client.py +109 -0
- sraverify/utils/__init__.py +3 -0
- sraverify/utils/banner.py +65 -0
- sraverify/utils/outputs.py +57 -0
- sraverify/utils/progress.py +97 -0
- sraverify-0.1.0.dist-info/LICENSE +175 -0
- sraverify-0.1.0.dist-info/METADATA +516 -0
- sraverify-0.1.0.dist-info/NOTICE +1 -0
- sraverify-0.1.0.dist-info/RECORD +261 -0
- sraverify-0.1.0.dist-info/WHEEL +5 -0
- sraverify-0.1.0.dist-info/entry_points.txt +2 -0
- sraverify-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base class for Macie security checks.
|
|
3
|
+
"""
|
|
4
|
+
from typing import List, Optional, Dict, Any
|
|
5
|
+
from sraverify.core.check import SecurityCheck
|
|
6
|
+
from sraverify.services.macie.client import MacieClient
|
|
7
|
+
from sraverify.core.logging import logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MacieCheck(SecurityCheck):
|
|
11
|
+
"""Base class for all Macie security checks."""
|
|
12
|
+
|
|
13
|
+
# Class-level caches shared across all instances
|
|
14
|
+
_findings_publication_cache = {}
|
|
15
|
+
_export_configuration_cache = {}
|
|
16
|
+
_macie_delegated_admin_cache = {}
|
|
17
|
+
_macie_members_cache = {}
|
|
18
|
+
_org_members_cache = {}
|
|
19
|
+
_auto_enable_cache = {}
|
|
20
|
+
|
|
21
|
+
def __init__(self):
|
|
22
|
+
"""Initialize Macie base check."""
|
|
23
|
+
super().__init__(
|
|
24
|
+
account_type="application", # Default, can be overridden in subclasses
|
|
25
|
+
service="Macie",
|
|
26
|
+
resource_type="AWS::Macie::Session"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def _setup_clients(self):
|
|
30
|
+
"""Set up Macie clients for each region."""
|
|
31
|
+
# Clear existing clients
|
|
32
|
+
self._clients.clear()
|
|
33
|
+
# Set up new clients only if regions are initialized
|
|
34
|
+
if hasattr(self, 'regions') and self.regions:
|
|
35
|
+
for region in self.regions:
|
|
36
|
+
self._clients[region] = MacieClient(region, session=self.session)
|
|
37
|
+
|
|
38
|
+
def get_client(self, region: str) -> Optional[MacieClient]:
|
|
39
|
+
"""
|
|
40
|
+
Get Macie client for a specific region.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
region: AWS region name
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
MacieClient for the region or None if not available
|
|
47
|
+
"""
|
|
48
|
+
return self._clients.get(region)
|
|
49
|
+
|
|
50
|
+
def get_findings_publication_configuration(self, region: str) -> Dict[str, Any]:
|
|
51
|
+
"""
|
|
52
|
+
Get the findings publication configuration for Macie with caching.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
region: AWS region name
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Dictionary containing findings publication configuration
|
|
59
|
+
"""
|
|
60
|
+
# Check cache first
|
|
61
|
+
account_id = self.account_id
|
|
62
|
+
cache_key = f"{account_id}:{region}"
|
|
63
|
+
|
|
64
|
+
if cache_key in self.__class__._findings_publication_cache:
|
|
65
|
+
logger.debug(f"Using cached Macie findings publication configuration for {region}")
|
|
66
|
+
return self.__class__._findings_publication_cache[cache_key]
|
|
67
|
+
|
|
68
|
+
client = self.get_client(region)
|
|
69
|
+
if not client:
|
|
70
|
+
logger.warning(f"No Macie client available for region {region}")
|
|
71
|
+
return {}
|
|
72
|
+
|
|
73
|
+
# Get findings publication configuration from client
|
|
74
|
+
config = client.get_findings_publication_configuration()
|
|
75
|
+
|
|
76
|
+
# Cache the result
|
|
77
|
+
self.__class__._findings_publication_cache[cache_key] = config
|
|
78
|
+
logger.debug(f"Cached Macie findings publication configuration for {region}")
|
|
79
|
+
|
|
80
|
+
return config
|
|
81
|
+
|
|
82
|
+
def get_classification_export_configuration(self, region: str) -> Dict[str, Any]:
|
|
83
|
+
"""
|
|
84
|
+
Get the classification export configuration for Macie with caching.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
region: AWS region name
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Dictionary containing classification export configuration
|
|
91
|
+
"""
|
|
92
|
+
# Check cache first
|
|
93
|
+
account_id = self.account_id
|
|
94
|
+
cache_key = f"{account_id}:{region}"
|
|
95
|
+
|
|
96
|
+
if cache_key in self.__class__._export_configuration_cache:
|
|
97
|
+
logger.debug(f"Using cached Macie classification export configuration for {region}")
|
|
98
|
+
return self.__class__._export_configuration_cache[cache_key]
|
|
99
|
+
|
|
100
|
+
client = self.get_client(region)
|
|
101
|
+
if not client:
|
|
102
|
+
logger.warning(f"No Macie client available for region {region}")
|
|
103
|
+
return {}
|
|
104
|
+
|
|
105
|
+
# Get classification export configuration from client
|
|
106
|
+
config = client.get_classification_export_configuration()
|
|
107
|
+
|
|
108
|
+
# Cache the result
|
|
109
|
+
self.__class__._export_configuration_cache[cache_key] = config
|
|
110
|
+
logger.debug(f"Cached Macie classification export configuration for {region}")
|
|
111
|
+
|
|
112
|
+
return config
|
|
113
|
+
|
|
114
|
+
def get_macie_delegated_admin(self, region: str) -> List[Dict[str, Any]]:
|
|
115
|
+
"""
|
|
116
|
+
Get the Macie delegated administrator with caching.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
region: AWS region name
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
List of delegated administrators
|
|
123
|
+
"""
|
|
124
|
+
# Check cache first
|
|
125
|
+
account_id = self.account_id
|
|
126
|
+
cache_key = f"{account_id}:{region}"
|
|
127
|
+
|
|
128
|
+
if cache_key in self.__class__._macie_delegated_admin_cache:
|
|
129
|
+
logger.debug(f"Using cached Macie delegated administrator for {region}")
|
|
130
|
+
return self.__class__._macie_delegated_admin_cache[cache_key]
|
|
131
|
+
|
|
132
|
+
client = self.get_client(region)
|
|
133
|
+
if not client:
|
|
134
|
+
logger.warning(f"No Macie client available for region {region}")
|
|
135
|
+
return []
|
|
136
|
+
|
|
137
|
+
# Get delegated administrator from client
|
|
138
|
+
delegated_admin = client.list_delegated_administrators()
|
|
139
|
+
|
|
140
|
+
# Cache the result
|
|
141
|
+
self.__class__._macie_delegated_admin_cache[cache_key] = delegated_admin
|
|
142
|
+
logger.debug(f"Cached Macie delegated administrator for {region}")
|
|
143
|
+
|
|
144
|
+
return delegated_admin
|
|
145
|
+
|
|
146
|
+
def get_macie_members(self, region: str) -> List[Dict[str, Any]]:
|
|
147
|
+
"""
|
|
148
|
+
Get Macie members with caching.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
region: AWS region name
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
List of Macie members
|
|
155
|
+
"""
|
|
156
|
+
# Check cache first
|
|
157
|
+
account_id = self.account_id
|
|
158
|
+
cache_key = f"{account_id}:{region}"
|
|
159
|
+
|
|
160
|
+
if cache_key in self.__class__._macie_members_cache:
|
|
161
|
+
logger.debug(f"Using cached Macie members for {region}")
|
|
162
|
+
return self.__class__._macie_members_cache[cache_key]
|
|
163
|
+
|
|
164
|
+
client = self.get_client(region)
|
|
165
|
+
if not client:
|
|
166
|
+
logger.warning(f"No Macie client available for region {region}")
|
|
167
|
+
return []
|
|
168
|
+
|
|
169
|
+
# Get members from client
|
|
170
|
+
members = client.list_members()
|
|
171
|
+
|
|
172
|
+
# Cache the result
|
|
173
|
+
self.__class__._macie_members_cache[cache_key] = members
|
|
174
|
+
logger.debug(f"Cached {len(members)} Macie members for {region}")
|
|
175
|
+
|
|
176
|
+
return members
|
|
177
|
+
|
|
178
|
+
def get_organization_members(self, region: str) -> List[Dict[str, Any]]:
|
|
179
|
+
"""
|
|
180
|
+
Get AWS Organization members with caching.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
region: AWS region name
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
List of AWS Organization members
|
|
187
|
+
"""
|
|
188
|
+
# Check cache first
|
|
189
|
+
account_id = self.account_id
|
|
190
|
+
cache_key = f"{account_id}:{region}"
|
|
191
|
+
|
|
192
|
+
if cache_key in self.__class__._org_members_cache:
|
|
193
|
+
logger.debug(f"Using cached AWS Organization members for {region}")
|
|
194
|
+
return self.__class__._org_members_cache[cache_key]
|
|
195
|
+
|
|
196
|
+
client = self.get_client(region)
|
|
197
|
+
if not client:
|
|
198
|
+
logger.warning(f"No Macie client available for region {region}")
|
|
199
|
+
return []
|
|
200
|
+
|
|
201
|
+
# Get organization members from client
|
|
202
|
+
members = client.list_organization_accounts()
|
|
203
|
+
|
|
204
|
+
# Cache the result
|
|
205
|
+
self.__class__._org_members_cache[cache_key] = members
|
|
206
|
+
logger.debug(f"Cached {len(members)} AWS Organization members for {region}")
|
|
207
|
+
|
|
208
|
+
return members
|
|
209
|
+
|
|
210
|
+
def get_organization_configuration(self, region: str) -> Dict[str, Any]:
|
|
211
|
+
"""
|
|
212
|
+
Get Macie organization configuration with caching.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
region: AWS region name
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
Dictionary containing Macie organization configuration
|
|
219
|
+
"""
|
|
220
|
+
# Check cache first
|
|
221
|
+
account_id = self.account_id
|
|
222
|
+
cache_key = f"{account_id}:{region}"
|
|
223
|
+
|
|
224
|
+
if cache_key in self.__class__._auto_enable_cache:
|
|
225
|
+
logger.debug(f"Using cached Macie organization configuration for {region}")
|
|
226
|
+
return self.__class__._auto_enable_cache[cache_key]
|
|
227
|
+
|
|
228
|
+
client = self.get_client(region)
|
|
229
|
+
if not client:
|
|
230
|
+
logger.warning(f"No Macie client available for region {region}")
|
|
231
|
+
return {}
|
|
232
|
+
|
|
233
|
+
# Get organization configuration from client
|
|
234
|
+
config = client.describe_organization_configuration()
|
|
235
|
+
|
|
236
|
+
# Cache the result
|
|
237
|
+
self.__class__._auto_enable_cache[cache_key] = config
|
|
238
|
+
logger.debug(f"Cached Macie organization configuration for {region}")
|
|
239
|
+
|
|
240
|
+
return config
|
|
241
|
+
def get_macie_administrator_account(self, region: str) -> Dict[str, Any]:
|
|
242
|
+
"""
|
|
243
|
+
Get the Macie administrator account with caching.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
region: AWS region name
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
Dictionary containing Macie administrator account information
|
|
250
|
+
"""
|
|
251
|
+
# Check cache first
|
|
252
|
+
account_id = self.account_id
|
|
253
|
+
cache_key = f"{account_id}:{region}"
|
|
254
|
+
|
|
255
|
+
if cache_key in self.__class__._macie_delegated_admin_cache:
|
|
256
|
+
logger.debug(f"Using cached Macie administrator account for {region}")
|
|
257
|
+
return self.__class__._macie_delegated_admin_cache[cache_key]
|
|
258
|
+
|
|
259
|
+
client = self.get_client(region)
|
|
260
|
+
if not client:
|
|
261
|
+
logger.warning(f"No Macie client available for region {region}")
|
|
262
|
+
return {}
|
|
263
|
+
|
|
264
|
+
# Get administrator account from client
|
|
265
|
+
admin_account = client.get_administrator_account()
|
|
266
|
+
|
|
267
|
+
# Cache the result
|
|
268
|
+
self.__class__._macie_delegated_admin_cache[cache_key] = admin_account
|
|
269
|
+
logger.debug(f"Cached Macie administrator account for {region}")
|
|
270
|
+
|
|
271
|
+
return admin_account
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Macie security checks."""
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SRA-MACIE-01: Macie publish policy findings to Security Hub is enabled.
|
|
3
|
+
"""
|
|
4
|
+
from typing import List, Dict, Any
|
|
5
|
+
from sraverify.services.macie.base import MacieCheck
|
|
6
|
+
from sraverify.core.logging import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SRA_MACIE_01(MacieCheck):
|
|
10
|
+
"""Check if Macie publish policy findings to Security Hub is enabled."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
"""Initialize the check."""
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.check_id = "SRA-MACIE-01"
|
|
16
|
+
self.check_name = "Macie publish policy findings to Security Hub is enabled"
|
|
17
|
+
self.description = (
|
|
18
|
+
"This check verifies whether Macie is configured to publish new and updated policy findings to AWS Security Hub. "
|
|
19
|
+
"Policy findings denotes potential security or privacy issue with a S3 bucket."
|
|
20
|
+
)
|
|
21
|
+
self.severity = "HIGH"
|
|
22
|
+
self.account_type = "application"
|
|
23
|
+
self.check_logic = "Check validates macie2 get-findings-publication-configuration. Check PASS if 'publishPolicyFindings': true"
|
|
24
|
+
self.resource_type = "AWS::Macie::Session"
|
|
25
|
+
|
|
26
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
27
|
+
"""
|
|
28
|
+
Execute the check.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
List of findings
|
|
32
|
+
"""
|
|
33
|
+
findings = []
|
|
34
|
+
|
|
35
|
+
for region in self.regions:
|
|
36
|
+
# Get findings publication configuration using the base class method with caching
|
|
37
|
+
config = self.get_findings_publication_configuration(region)
|
|
38
|
+
|
|
39
|
+
# Check if the API call was successful
|
|
40
|
+
if not config:
|
|
41
|
+
findings.append(
|
|
42
|
+
self.create_finding(
|
|
43
|
+
status="FAIL",
|
|
44
|
+
region=region,
|
|
45
|
+
resource_id=f"macie2/{self.account_id}/{region}",
|
|
46
|
+
checked_value="publishPolicyFindings: true",
|
|
47
|
+
actual_value="Failed to retrieve Macie findings publication configuration",
|
|
48
|
+
remediation="Ensure Macie is enabled and you have the necessary permissions to call the Macie GetFindingsPublicationConfiguration API"
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
continue
|
|
52
|
+
|
|
53
|
+
# Check if Security Hub configuration exists
|
|
54
|
+
security_hub_config = config.get('securityHubConfiguration', {})
|
|
55
|
+
if not security_hub_config:
|
|
56
|
+
findings.append(
|
|
57
|
+
self.create_finding(
|
|
58
|
+
status="FAIL",
|
|
59
|
+
region=region,
|
|
60
|
+
resource_id=f"macie2/{self.account_id}/{region}",
|
|
61
|
+
checked_value="publishPolicyFindings: true",
|
|
62
|
+
actual_value="Security Hub configuration not found in Macie findings publication configuration",
|
|
63
|
+
remediation=(
|
|
64
|
+
f"Configure Macie to publish findings to Security Hub in region {region} using the AWS CLI command: "
|
|
65
|
+
f"aws macie2 put-findings-publication-configuration --security-hub-configuration publishPolicyFindings=true --region {region}"
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
continue
|
|
70
|
+
|
|
71
|
+
# Check if policy findings are published to Security Hub
|
|
72
|
+
publish_policy_findings = security_hub_config.get('publishPolicyFindings', False)
|
|
73
|
+
|
|
74
|
+
if publish_policy_findings:
|
|
75
|
+
findings.append(
|
|
76
|
+
self.create_finding(
|
|
77
|
+
status="PASS",
|
|
78
|
+
region=region,
|
|
79
|
+
resource_id=f"macie2/{self.account_id}/{region}",
|
|
80
|
+
checked_value="publishPolicyFindings: true",
|
|
81
|
+
actual_value=f"Macie is configured to publish policy findings to Security Hub in region {region}",
|
|
82
|
+
remediation="No remediation needed"
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
else:
|
|
86
|
+
findings.append(
|
|
87
|
+
self.create_finding(
|
|
88
|
+
status="FAIL",
|
|
89
|
+
region=region,
|
|
90
|
+
resource_id=f"macie2/{self.account_id}/{region}",
|
|
91
|
+
checked_value="publishPolicyFindings: true",
|
|
92
|
+
actual_value=f"Macie is not configured to publish policy findings to Security Hub in region {region}",
|
|
93
|
+
remediation=(
|
|
94
|
+
f"Configure Macie to publish policy findings to Security Hub in region {region} using the AWS CLI command: "
|
|
95
|
+
f"aws macie2 put-findings-publication-configuration --security-hub-configuration publishPolicyFindings=true --region {region}"
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
return findings
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SRA-MACIE-02: Macie publish classification findings to Security Hub is enabled.
|
|
3
|
+
"""
|
|
4
|
+
from typing import List, Dict, Any
|
|
5
|
+
from sraverify.services.macie.base import MacieCheck
|
|
6
|
+
from sraverify.core.logging import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SRA_MACIE_02(MacieCheck):
|
|
10
|
+
"""Check if Macie publish classification findings to Security Hub is enabled."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
"""Initialize the check."""
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.check_id = "SRA-MACIE-02"
|
|
16
|
+
self.check_name = "Macie publish classification findings to Security Hub is enabled"
|
|
17
|
+
self.description = (
|
|
18
|
+
"This check verifies whether Macie is configured to publish sensitive data findings to AWS Security Hub. "
|
|
19
|
+
"Sensitive data findings denotes potential sensitive data in as S3 object. Macie continually evaluates your "
|
|
20
|
+
"S3 bucket inventory and uses sampling techniques to identify and select representative S3 objects from your buckets. "
|
|
21
|
+
"Macie then retrieves and analyzes the selected objects, inspecting them for sensitive data."
|
|
22
|
+
)
|
|
23
|
+
self.severity = "HIGH"
|
|
24
|
+
self.account_type = "application"
|
|
25
|
+
self.check_logic = "Check validates macie2 get-findings-publication-configuration. Check PASS if 'publishClassificationFindings': true"
|
|
26
|
+
self.resource_type = "AWS::Macie::Session"
|
|
27
|
+
|
|
28
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
29
|
+
"""
|
|
30
|
+
Execute the check.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
List of findings
|
|
34
|
+
"""
|
|
35
|
+
findings = []
|
|
36
|
+
|
|
37
|
+
for region in self.regions:
|
|
38
|
+
# Get findings publication configuration using the base class method with caching
|
|
39
|
+
config = self.get_findings_publication_configuration(region)
|
|
40
|
+
|
|
41
|
+
# Check if the API call was successful
|
|
42
|
+
if not config:
|
|
43
|
+
findings.append(
|
|
44
|
+
self.create_finding(
|
|
45
|
+
status="FAIL",
|
|
46
|
+
region=region,
|
|
47
|
+
resource_id=f"macie2/{self.account_id}/{region}",
|
|
48
|
+
checked_value="publishClassificationFindings: true",
|
|
49
|
+
actual_value="Failed to retrieve Macie findings publication configuration",
|
|
50
|
+
remediation="Ensure Macie is enabled and you have the necessary permissions to call the Macie GetFindingsPublicationConfiguration API"
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
# Check if Security Hub configuration exists
|
|
56
|
+
security_hub_config = config.get('securityHubConfiguration', {})
|
|
57
|
+
if not security_hub_config:
|
|
58
|
+
findings.append(
|
|
59
|
+
self.create_finding(
|
|
60
|
+
status="FAIL",
|
|
61
|
+
region=region,
|
|
62
|
+
resource_id=f"macie2/{self.account_id}/{region}",
|
|
63
|
+
checked_value="publishClassificationFindings: true",
|
|
64
|
+
actual_value="Security Hub configuration not found in Macie findings publication configuration",
|
|
65
|
+
remediation=(
|
|
66
|
+
f"Configure Macie to publish findings to Security Hub in region {region} using the AWS CLI command: "
|
|
67
|
+
f"aws macie2 put-findings-publication-configuration --security-hub-configuration publishClassificationFindings=true --region {region}"
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
continue
|
|
72
|
+
|
|
73
|
+
# Check if classification findings are published to Security Hub
|
|
74
|
+
publish_classification_findings = security_hub_config.get('publishClassificationFindings', False)
|
|
75
|
+
|
|
76
|
+
if publish_classification_findings:
|
|
77
|
+
findings.append(
|
|
78
|
+
self.create_finding(
|
|
79
|
+
status="PASS",
|
|
80
|
+
region=region,
|
|
81
|
+
resource_id=f"macie2/{self.account_id}/{region}",
|
|
82
|
+
checked_value="publishClassificationFindings: true",
|
|
83
|
+
actual_value=f"Macie is configured to publish classification findings to Security Hub in region {region}",
|
|
84
|
+
remediation="No remediation needed"
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
findings.append(
|
|
89
|
+
self.create_finding(
|
|
90
|
+
status="FAIL",
|
|
91
|
+
region=region,
|
|
92
|
+
resource_id=f"macie2/{self.account_id}/{region}",
|
|
93
|
+
checked_value="publishClassificationFindings: true",
|
|
94
|
+
actual_value=f"Macie is not configured to publish classification findings to Security Hub in region {region}",
|
|
95
|
+
remediation=(
|
|
96
|
+
f"Configure Macie to publish classification findings to Security Hub in region {region} using the AWS CLI command: "
|
|
97
|
+
f"aws macie2 put-findings-publication-configuration --security-hub-configuration publishClassificationFindings=true --region {region}"
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
return findings
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SRA-MACIE-03: Macie findings exported to a S3 bucket in Log Archive account are encrypted at rest.
|
|
3
|
+
"""
|
|
4
|
+
from typing import List, Dict, Any
|
|
5
|
+
from sraverify.services.macie.base import MacieCheck
|
|
6
|
+
from sraverify.core.logging import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SRA_MACIE_03(MacieCheck):
|
|
10
|
+
"""Check if Macie findings are exported to a S3 bucket in Log Archive account."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
"""Initialize the check."""
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.check_id = "SRA-MACIE-03"
|
|
16
|
+
self.check_name = "Macie findings exported to a S3 bucket in Log Archive account are encrypted at rest"
|
|
17
|
+
self.description = (
|
|
18
|
+
"This check verifies whether all Macie findings are being exported to a S3 bucket within the Log Archive account. "
|
|
19
|
+
"Log Archive account is the central repository of all AWS Organization logs."
|
|
20
|
+
)
|
|
21
|
+
self.severity = "HIGH"
|
|
22
|
+
self.account_type = "application"
|
|
23
|
+
self.check_logic = (
|
|
24
|
+
"Check validates using get-classification-export-configuration if Macie is set to export findings to S3 AND "
|
|
25
|
+
"if the S3 bucket is in the log archive account validated by EITHER the bucket name containing OR the KMS key "
|
|
26
|
+
"arn containing the --log-archive account ID. If bucket account can't be validated to --log-archive FAIL with value of bucket."
|
|
27
|
+
)
|
|
28
|
+
self.resource_type = "AWS::Macie::Session"
|
|
29
|
+
|
|
30
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
31
|
+
"""
|
|
32
|
+
Execute the check.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
List of findings
|
|
36
|
+
"""
|
|
37
|
+
findings = []
|
|
38
|
+
|
|
39
|
+
# Check if log archive accounts are provided
|
|
40
|
+
log_archive_accounts = []
|
|
41
|
+
if hasattr(self, '_log_archive_accounts') and self._log_archive_accounts:
|
|
42
|
+
log_archive_accounts = self._log_archive_accounts
|
|
43
|
+
|
|
44
|
+
if not log_archive_accounts:
|
|
45
|
+
for region in self.regions:
|
|
46
|
+
findings.append(
|
|
47
|
+
self.create_finding(
|
|
48
|
+
status="FAIL",
|
|
49
|
+
region=region,
|
|
50
|
+
resource_id=f"macie2/{self.account_id}/{region}",
|
|
51
|
+
checked_value="S3 bucket in Log Archive account",
|
|
52
|
+
actual_value="Log Archive account ID not provided",
|
|
53
|
+
remediation="Provide the Log Archive account IDs using --log-archive-account flag"
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
return findings
|
|
57
|
+
|
|
58
|
+
for region in self.regions:
|
|
59
|
+
# Get classification export configuration using the base class method with caching
|
|
60
|
+
export_config = self.get_classification_export_configuration(region)
|
|
61
|
+
|
|
62
|
+
# Check if the API call was successful
|
|
63
|
+
if not export_config:
|
|
64
|
+
findings.append(
|
|
65
|
+
self.create_finding(
|
|
66
|
+
status="FAIL",
|
|
67
|
+
region=region,
|
|
68
|
+
resource_id=f"macie2/{self.account_id}/{region}",
|
|
69
|
+
checked_value="S3 bucket in Log Archive account",
|
|
70
|
+
actual_value="Failed to retrieve Macie classification export configuration",
|
|
71
|
+
remediation="Ensure Macie is enabled and you have the necessary permissions to call the Macie GetClassificationExportConfiguration API"
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
# Check if export configuration exists
|
|
77
|
+
configuration = export_config.get('configuration', {})
|
|
78
|
+
if not configuration:
|
|
79
|
+
findings.append(
|
|
80
|
+
self.create_finding(
|
|
81
|
+
status="FAIL",
|
|
82
|
+
region=region,
|
|
83
|
+
resource_id=f"macie2/{self.account_id}/{region}",
|
|
84
|
+
checked_value="S3 bucket in Log Archive account",
|
|
85
|
+
actual_value="Macie findings export configuration not found",
|
|
86
|
+
remediation=(
|
|
87
|
+
f"Configure Macie to export findings to a S3 bucket in the Log Archive account in region {region} using the AWS CLI command: "
|
|
88
|
+
f"aws macie2 put-classification-export-configuration --s3-destination bucketName=macie-findings-{log_archive_accounts[0]},kmsKeyArn=arn:aws:kms:{region}:{log_archive_accounts[0]}:key/your-key-id --region {region}"
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
# Check if S3 destination exists
|
|
95
|
+
s3_destination = configuration.get('s3Destination', {})
|
|
96
|
+
if not s3_destination:
|
|
97
|
+
findings.append(
|
|
98
|
+
self.create_finding(
|
|
99
|
+
status="FAIL",
|
|
100
|
+
region=region,
|
|
101
|
+
resource_id=f"macie2/{self.account_id}/{region}",
|
|
102
|
+
checked_value="S3 bucket in Log Archive account",
|
|
103
|
+
actual_value="S3 destination not found in Macie findings export configuration",
|
|
104
|
+
remediation=(
|
|
105
|
+
f"Configure Macie to export findings to a S3 bucket in the Log Archive account in region {region} using the AWS CLI command: "
|
|
106
|
+
f"aws macie2 put-classification-export-configuration --s3-destination bucketName=macie-findings-{log_archive_accounts[0]},kmsKeyArn=arn:aws:kms:{region}:{log_archive_accounts[0]}:key/your-key-id --region {region}"
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
continue
|
|
111
|
+
|
|
112
|
+
# Get bucket name and KMS key ARN
|
|
113
|
+
bucket_name = s3_destination.get('bucketName', '')
|
|
114
|
+
kms_key_arn = s3_destination.get('kmsKeyArn', '')
|
|
115
|
+
|
|
116
|
+
# Check if bucket name or KMS key ARN contains log archive account ID
|
|
117
|
+
is_in_log_archive = False
|
|
118
|
+
log_archive_account_found = None
|
|
119
|
+
|
|
120
|
+
for log_archive_account in log_archive_accounts:
|
|
121
|
+
if log_archive_account in bucket_name or log_archive_account in kms_key_arn:
|
|
122
|
+
is_in_log_archive = True
|
|
123
|
+
log_archive_account_found = log_archive_account
|
|
124
|
+
break
|
|
125
|
+
|
|
126
|
+
if is_in_log_archive:
|
|
127
|
+
findings.append(
|
|
128
|
+
self.create_finding(
|
|
129
|
+
status="PASS",
|
|
130
|
+
region=region,
|
|
131
|
+
resource_id=f"macie2/{self.account_id}/{region}",
|
|
132
|
+
checked_value="S3 bucket in Log Archive account",
|
|
133
|
+
actual_value=f"Macie findings are exported to S3 bucket '{bucket_name}' in Log Archive account {log_archive_account_found} in region {region}",
|
|
134
|
+
remediation="No remediation needed"
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
else:
|
|
138
|
+
findings.append(
|
|
139
|
+
self.create_finding(
|
|
140
|
+
status="FAIL",
|
|
141
|
+
region=region,
|
|
142
|
+
resource_id=f"macie2/{self.account_id}/{region}",
|
|
143
|
+
checked_value="S3 bucket in Log Archive account",
|
|
144
|
+
actual_value=f"Macie findings are exported to S3 bucket '{bucket_name}' which is not in any of the specified Log Archive accounts {', '.join(log_archive_accounts)} in region {region}",
|
|
145
|
+
remediation=(
|
|
146
|
+
f"Configure Macie to export findings to a S3 bucket in the Log Archive account in region {region} using the AWS CLI command: "
|
|
147
|
+
f"aws macie2 put-classification-export-configuration --s3-destination bucketName=macie-findings-{log_archive_accounts[0]},kmsKeyArn=arn:aws:kms:{region}:{log_archive_accounts[0]}:key/your-key-id --region {region}"
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
return findings
|