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.
Files changed (261) hide show
  1. sraverify/__init__.py +36 -0
  2. sraverify/checks/__init__.py +56 -0
  3. sraverify/checks/accessanalyzer/SRA_IAA_1.py +188 -0
  4. sraverify/checks/accessanalyzer/SRA_IAA_2.py +162 -0
  5. sraverify/checks/accessanalyzer/SRA_IAA_3.py +260 -0
  6. sraverify/checks/accessanalyzer/SRA_IAA_4.py +207 -0
  7. sraverify/checks/accessanalyzer/__init__.py +3 -0
  8. sraverify/checks/cloudtrail/SRA-CT-1.py +220 -0
  9. sraverify/checks/cloudtrail/SRA-CT-10.py +229 -0
  10. sraverify/checks/cloudtrail/SRA-CT-11.py +242 -0
  11. sraverify/checks/cloudtrail/SRA-CT-12.py +163 -0
  12. sraverify/checks/cloudtrail/SRA-CT-13.py +279 -0
  13. sraverify/checks/cloudtrail/SRA-CT-2.py +218 -0
  14. sraverify/checks/cloudtrail/SRA-CT-3.py +196 -0
  15. sraverify/checks/cloudtrail/SRA-CT-4.py +161 -0
  16. sraverify/checks/cloudtrail/SRA-CT-5.py +200 -0
  17. sraverify/checks/cloudtrail/SRA-CT-6.py +161 -0
  18. sraverify/checks/cloudtrail/SRA-CT-7.py +194 -0
  19. sraverify/checks/cloudtrail/SRA-CT-8.py +226 -0
  20. sraverify/checks/cloudtrail/SRA-CT-9.py +226 -0
  21. sraverify/checks/cloudtrail/__init__.py +3 -0
  22. sraverify/checks/config/SRA-CONFIG-1.py +197 -0
  23. sraverify/checks/config/__init__.py +3 -0
  24. sraverify/core/__init__.py +3 -0
  25. sraverify/core/check.py +227 -0
  26. sraverify/core/logging.py +37 -0
  27. sraverify/core/session.py +47 -0
  28. sraverify/lib/__init__.py +4 -0
  29. sraverify/lib/audit_info.py +37 -0
  30. sraverify/lib/banner.py +42 -0
  31. sraverify/lib/check_loader.py +80 -0
  32. sraverify/lib/org_mgmt_checker.py +86 -0
  33. sraverify/lib/outputs.py +46 -0
  34. sraverify/lib/progress.py +75 -0
  35. sraverify/lib/regions.py +27 -0
  36. sraverify/lib/session.py +23 -0
  37. sraverify/main.py +350 -0
  38. sraverify/services/__init__.py +3 -0
  39. sraverify/services/accessanalyzer/__init__.py +15 -0
  40. sraverify/services/accessanalyzer/base.py +123 -0
  41. sraverify/services/accessanalyzer/checks/__init__.py +3 -0
  42. sraverify/services/accessanalyzer/checks/sra_accessanalyzer_01.py +82 -0
  43. sraverify/services/accessanalyzer/checks/sra_accessanalyzer_02.py +82 -0
  44. sraverify/services/accessanalyzer/checks/sra_accessanalyzer_03.py +103 -0
  45. sraverify/services/accessanalyzer/checks/sra_accessanalyzer_04.py +139 -0
  46. sraverify/services/accessanalyzer/client.py +123 -0
  47. sraverify/services/account/__init__.py +9 -0
  48. sraverify/services/account/base.py +56 -0
  49. sraverify/services/account/checks/__init__.py +1 -0
  50. sraverify/services/account/checks/sra_account_01.py +65 -0
  51. sraverify/services/account/checks/sra_account_02.py +63 -0
  52. sraverify/services/account/checks/sra_account_03.py +63 -0
  53. sraverify/services/account/client.py +51 -0
  54. sraverify/services/auditmanager/__init__.py +10 -0
  55. sraverify/services/auditmanager/base.py +72 -0
  56. sraverify/services/auditmanager/checks/__init__.py +1 -0
  57. sraverify/services/auditmanager/checks/sra_auditmanager_01.py +58 -0
  58. sraverify/services/auditmanager/checks/sra_auditmanager_02.py +80 -0
  59. sraverify/services/auditmanager/client.py +58 -0
  60. sraverify/services/cloudtrail/__init__.py +33 -0
  61. sraverify/services/cloudtrail/base.py +167 -0
  62. sraverify/services/cloudtrail/checks/__init__.py +1 -0
  63. sraverify/services/cloudtrail/checks/sra_cloudtrail_01.py +83 -0
  64. sraverify/services/cloudtrail/checks/sra_cloudtrail_02.py +99 -0
  65. sraverify/services/cloudtrail/checks/sra_cloudtrail_03.py +94 -0
  66. sraverify/services/cloudtrail/checks/sra_cloudtrail_04.py +92 -0
  67. sraverify/services/cloudtrail/checks/sra_cloudtrail_05.py +106 -0
  68. sraverify/services/cloudtrail/checks/sra_cloudtrail_06.py +93 -0
  69. sraverify/services/cloudtrail/checks/sra_cloudtrail_07.py +96 -0
  70. sraverify/services/cloudtrail/checks/sra_cloudtrail_08.py +145 -0
  71. sraverify/services/cloudtrail/checks/sra_cloudtrail_09.py +167 -0
  72. sraverify/services/cloudtrail/checks/sra_cloudtrail_10.py +162 -0
  73. sraverify/services/cloudtrail/checks/sra_cloudtrail_11.py +178 -0
  74. sraverify/services/cloudtrail/checks/sra_cloudtrail_12.py +77 -0
  75. sraverify/services/cloudtrail/checks/sra_cloudtrail_13.py +120 -0
  76. sraverify/services/cloudtrail/client.py +118 -0
  77. sraverify/services/config/__init__.py +25 -0
  78. sraverify/services/config/base.py +249 -0
  79. sraverify/services/config/checks/__init__.py +1 -0
  80. sraverify/services/config/checks/sra_config_01.py +123 -0
  81. sraverify/services/config/checks/sra_config_02.py +156 -0
  82. sraverify/services/config/checks/sra_config_03.py +149 -0
  83. sraverify/services/config/checks/sra_config_04.py +104 -0
  84. sraverify/services/config/checks/sra_config_05.py +104 -0
  85. sraverify/services/config/checks/sra_config_06.py +194 -0
  86. sraverify/services/config/checks/sra_config_07.py +162 -0
  87. sraverify/services/config/checks/sra_config_08.py +185 -0
  88. sraverify/services/config/checks/sra_config_09.py +177 -0
  89. sraverify/services/config/client.py +264 -0
  90. sraverify/services/ec2/__init__.py +8 -0
  91. sraverify/services/ec2/base.py +75 -0
  92. sraverify/services/ec2/checks/__init__.py +1 -0
  93. sraverify/services/ec2/checks/sra_ec2_01.py +83 -0
  94. sraverify/services/ec2/client.py +63 -0
  95. sraverify/services/firewallmanager/__init__.py +23 -0
  96. sraverify/services/firewallmanager/base.py +48 -0
  97. sraverify/services/firewallmanager/checks/__init__.py +1 -0
  98. sraverify/services/firewallmanager/checks/sra_firewallmanager_01.py +75 -0
  99. sraverify/services/firewallmanager/checks/sra_firewallmanager_02.py +57 -0
  100. sraverify/services/firewallmanager/checks/sra_firewallmanager_03.py +51 -0
  101. sraverify/services/firewallmanager/checks/sra_firewallmanager_04.py +51 -0
  102. sraverify/services/firewallmanager/checks/sra_firewallmanager_05.py +51 -0
  103. sraverify/services/firewallmanager/checks/sra_firewallmanager_06.py +51 -0
  104. sraverify/services/firewallmanager/checks/sra_firewallmanager_07.py +51 -0
  105. sraverify/services/firewallmanager/checks/sra_firewallmanager_08.py +61 -0
  106. sraverify/services/firewallmanager/checks/sra_firewallmanager_09.py +61 -0
  107. sraverify/services/firewallmanager/checks/sra_firewallmanager_10.py +71 -0
  108. sraverify/services/firewallmanager/client.py +40 -0
  109. sraverify/services/guardduty/__init__.py +58 -0
  110. sraverify/services/guardduty/base.py +207 -0
  111. sraverify/services/guardduty/checks/__init__.py +3 -0
  112. sraverify/services/guardduty/checks/sra_guardduty_01.py +51 -0
  113. sraverify/services/guardduty/checks/sra_guardduty_02.py +80 -0
  114. sraverify/services/guardduty/checks/sra_guardduty_03.py +77 -0
  115. sraverify/services/guardduty/checks/sra_guardduty_04.py +84 -0
  116. sraverify/services/guardduty/checks/sra_guardduty_05.py +84 -0
  117. sraverify/services/guardduty/checks/sra_guardduty_06.py +84 -0
  118. sraverify/services/guardduty/checks/sra_guardduty_07.py +85 -0
  119. sraverify/services/guardduty/checks/sra_guardduty_08.py +83 -0
  120. sraverify/services/guardduty/checks/sra_guardduty_09.py +84 -0
  121. sraverify/services/guardduty/checks/sra_guardduty_10.py +83 -0
  122. sraverify/services/guardduty/checks/sra_guardduty_11.py +93 -0
  123. sraverify/services/guardduty/checks/sra_guardduty_12.py +83 -0
  124. sraverify/services/guardduty/checks/sra_guardduty_13.py +90 -0
  125. sraverify/services/guardduty/checks/sra_guardduty_14.py +136 -0
  126. sraverify/services/guardduty/checks/sra_guardduty_15.py +94 -0
  127. sraverify/services/guardduty/checks/sra_guardduty_16.py +94 -0
  128. sraverify/services/guardduty/checks/sra_guardduty_17.py +91 -0
  129. sraverify/services/guardduty/checks/sra_guardduty_18.py +91 -0
  130. sraverify/services/guardduty/checks/sra_guardduty_19.py +91 -0
  131. sraverify/services/guardduty/checks/sra_guardduty_20.py +111 -0
  132. sraverify/services/guardduty/checks/sra_guardduty_21.py +112 -0
  133. sraverify/services/guardduty/checks/sra_guardduty_22.py +111 -0
  134. sraverify/services/guardduty/checks/sra_guardduty_23.py +154 -0
  135. sraverify/services/guardduty/checks/sra_guardduty_24.py +111 -0
  136. sraverify/services/guardduty/checks/sra_guardduty_25.py +111 -0
  137. sraverify/services/guardduty/client.py +107 -0
  138. sraverify/services/inspector/__init__.py +29 -0
  139. sraverify/services/inspector/base.py +233 -0
  140. sraverify/services/inspector/checks/__init__.py +3 -0
  141. sraverify/services/inspector/checks/sra_inspector_01.py +69 -0
  142. sraverify/services/inspector/checks/sra_inspector_02.py +68 -0
  143. sraverify/services/inspector/checks/sra_inspector_03.py +68 -0
  144. sraverify/services/inspector/checks/sra_inspector_04.py +70 -0
  145. sraverify/services/inspector/checks/sra_inspector_05.py +69 -0
  146. sraverify/services/inspector/checks/sra_inspector_06.py +115 -0
  147. sraverify/services/inspector/checks/sra_inspector_07.py +109 -0
  148. sraverify/services/inspector/checks/sra_inspector_08.py +69 -0
  149. sraverify/services/inspector/checks/sra_inspector_09.py +69 -0
  150. sraverify/services/inspector/checks/sra_inspector_10.py +69 -0
  151. sraverify/services/inspector/checks/sra_inspector_11.py +69 -0
  152. sraverify/services/inspector/client.py +99 -0
  153. sraverify/services/macie/__init__.py +27 -0
  154. sraverify/services/macie/base.py +271 -0
  155. sraverify/services/macie/checks/__init__.py +1 -0
  156. sraverify/services/macie/checks/sra_macie_01.py +100 -0
  157. sraverify/services/macie/checks/sra_macie_02.py +102 -0
  158. sraverify/services/macie/checks/sra_macie_03.py +152 -0
  159. sraverify/services/macie/checks/sra_macie_04.py +120 -0
  160. sraverify/services/macie/checks/sra_macie_05.py +85 -0
  161. sraverify/services/macie/checks/sra_macie_06.py +124 -0
  162. sraverify/services/macie/checks/sra_macie_07.py +138 -0
  163. sraverify/services/macie/checks/sra_macie_08.py +82 -0
  164. sraverify/services/macie/checks/sra_macie_09.py +103 -0
  165. sraverify/services/macie/checks/sra_macie_10.py +81 -0
  166. sraverify/services/macie/client.py +220 -0
  167. sraverify/services/s3/__init__.py +16 -0
  168. sraverify/services/s3/base.py +69 -0
  169. sraverify/services/s3/checks/__init__.py +1 -0
  170. sraverify/services/s3/checks/sra_s3_01.py +89 -0
  171. sraverify/services/s3/checks/sra_s3_02.py +89 -0
  172. sraverify/services/s3/checks/sra_s3_03.py +88 -0
  173. sraverify/services/s3/checks/sra_s3_04.py +88 -0
  174. sraverify/services/s3/client.py +52 -0
  175. sraverify/services/securityhub/__init__.py +27 -0
  176. sraverify/services/securityhub/base.py +349 -0
  177. sraverify/services/securityhub/checks/__init__.py +1 -0
  178. sraverify/services/securityhub/checks/sra_securityhub_01.py +115 -0
  179. sraverify/services/securityhub/checks/sra_securityhub_02.py +114 -0
  180. sraverify/services/securityhub/checks/sra_securityhub_03.py +136 -0
  181. sraverify/services/securityhub/checks/sra_securityhub_04.py +75 -0
  182. sraverify/services/securityhub/checks/sra_securityhub_05.py +102 -0
  183. sraverify/services/securityhub/checks/sra_securityhub_06.py +113 -0
  184. sraverify/services/securityhub/checks/sra_securityhub_07.py +121 -0
  185. sraverify/services/securityhub/checks/sra_securityhub_08.py +113 -0
  186. sraverify/services/securityhub/checks/sra_securityhub_09.py +100 -0
  187. sraverify/services/securityhub/checks/sra_securityhub_10.py +94 -0
  188. sraverify/services/securityhub/checks/sra_securityhub_11.py +73 -0
  189. sraverify/services/securityhub/client.py +249 -0
  190. sraverify/services/securityincidentresponse/__init__.py +13 -0
  191. sraverify/services/securityincidentresponse/base.py +95 -0
  192. sraverify/services/securityincidentresponse/checks/__init__.py +1 -0
  193. sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_01.py +77 -0
  194. sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_02.py +72 -0
  195. sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_03.py +86 -0
  196. sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_04.py +117 -0
  197. sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_05.py +55 -0
  198. sraverify/services/securityincidentresponse/client.py +71 -0
  199. sraverify/services/securitylake/__init__.py +39 -0
  200. sraverify/services/securitylake/base.py +461 -0
  201. sraverify/services/securitylake/checks/__init__.py +1 -0
  202. sraverify/services/securitylake/checks/sra_securitylake_01.py +98 -0
  203. sraverify/services/securitylake/checks/sra_securitylake_02.py +133 -0
  204. sraverify/services/securitylake/checks/sra_securitylake_03.py +116 -0
  205. sraverify/services/securitylake/checks/sra_securitylake_04.py +72 -0
  206. sraverify/services/securitylake/checks/sra_securitylake_05.py +116 -0
  207. sraverify/services/securitylake/checks/sra_securitylake_06.py +104 -0
  208. sraverify/services/securitylake/checks/sra_securitylake_07.py +108 -0
  209. sraverify/services/securitylake/checks/sra_securitylake_08.py +107 -0
  210. sraverify/services/securitylake/checks/sra_securitylake_09.py +107 -0
  211. sraverify/services/securitylake/checks/sra_securitylake_10.py +106 -0
  212. sraverify/services/securitylake/checks/sra_securitylake_11.py +109 -0
  213. sraverify/services/securitylake/checks/sra_securitylake_12.py +108 -0
  214. sraverify/services/securitylake/checks/sra_securitylake_13.py +108 -0
  215. sraverify/services/securitylake/checks/sra_securitylake_14.py +72 -0
  216. sraverify/services/securitylake/checks/sra_securitylake_15.py +120 -0
  217. sraverify/services/securitylake/checks/sra_securitylake_16.py +104 -0
  218. sraverify/services/securitylake/checks/sra_securitylake_17.py +103 -0
  219. sraverify/services/securitylake/client.py +247 -0
  220. sraverify/services/shield/__init__.py +33 -0
  221. sraverify/services/shield/base.py +199 -0
  222. sraverify/services/shield/checks/__init__.py +1 -0
  223. sraverify/services/shield/checks/sra_shield_01.py +68 -0
  224. sraverify/services/shield/checks/sra_shield_02.py +77 -0
  225. sraverify/services/shield/checks/sra_shield_03.py +84 -0
  226. sraverify/services/shield/checks/sra_shield_04.py +84 -0
  227. sraverify/services/shield/checks/sra_shield_05.py +84 -0
  228. sraverify/services/shield/checks/sra_shield_06.py +84 -0
  229. sraverify/services/shield/checks/sra_shield_07.py +84 -0
  230. sraverify/services/shield/checks/sra_shield_08.py +69 -0
  231. sraverify/services/shield/checks/sra_shield_09.py +86 -0
  232. sraverify/services/shield/checks/sra_shield_10.py +100 -0
  233. sraverify/services/shield/checks/sra_shield_11.py +71 -0
  234. sraverify/services/shield/checks/sra_shield_12.py +130 -0
  235. sraverify/services/shield/checks/sra_shield_13.py +112 -0
  236. sraverify/services/shield/checks/sra_shield_14.py +111 -0
  237. sraverify/services/shield/client.py +214 -0
  238. sraverify/services/waf/__init__.py +21 -0
  239. sraverify/services/waf/base.py +100 -0
  240. sraverify/services/waf/checks/__init__.py +1 -0
  241. sraverify/services/waf/checks/sra_waf_01.py +63 -0
  242. sraverify/services/waf/checks/sra_waf_02.py +82 -0
  243. sraverify/services/waf/checks/sra_waf_03.py +123 -0
  244. sraverify/services/waf/checks/sra_waf_04.py +94 -0
  245. sraverify/services/waf/checks/sra_waf_05.py +94 -0
  246. sraverify/services/waf/checks/sra_waf_06.py +91 -0
  247. sraverify/services/waf/checks/sra_waf_07.py +94 -0
  248. sraverify/services/waf/checks/sra_waf_08.py +66 -0
  249. sraverify/services/waf/checks/sra_waf_09.py +95 -0
  250. sraverify/services/waf/client.py +109 -0
  251. sraverify/utils/__init__.py +3 -0
  252. sraverify/utils/banner.py +65 -0
  253. sraverify/utils/outputs.py +57 -0
  254. sraverify/utils/progress.py +97 -0
  255. sraverify-0.1.0.dist-info/LICENSE +175 -0
  256. sraverify-0.1.0.dist-info/METADATA +516 -0
  257. sraverify-0.1.0.dist-info/NOTICE +1 -0
  258. sraverify-0.1.0.dist-info/RECORD +261 -0
  259. sraverify-0.1.0.dist-info/WHEEL +5 -0
  260. sraverify-0.1.0.dist-info/entry_points.txt +2 -0
  261. sraverify-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,461 @@
1
+ """Security Lake service module."""
2
+
3
+ from typing import List, Dict, Any, Optional
4
+ from sraverify.core.check import SecurityCheck
5
+ from sraverify.services.securitylake.client import SecurityLakeClient
6
+ from sraverify.core.logging import logger
7
+
8
+
9
+ class SecurityLakeCheck(SecurityCheck):
10
+ """Security Lake service class with integrated check functionality."""
11
+
12
+ # Class-level caches shared across all instances
13
+ _subscribers_cache = {}
14
+ _security_lake_status_cache = {}
15
+ _organization_configuration_cache = {}
16
+ _delegated_admin_cache = {}
17
+ _organization_accounts_cache = {}
18
+ _log_sources_cache = {}
19
+ _sqs_encryption_cache = {}
20
+
21
+ def __init__(self):
22
+ """Initialize Security Lake service."""
23
+ super().__init__(
24
+ account_type="log-archive",
25
+ service="SecurityLake",
26
+ resource_type="AWS::SecurityLake::SecurityLake"
27
+ )
28
+ # Initialize log archive account attribute
29
+ self._log_archive_accounts = None
30
+
31
+ def _setup_clients(self):
32
+ """Set up Security Lake clients for each region."""
33
+ # Clear existing clients
34
+ self._clients.clear()
35
+ # Set up new clients only if regions are initialized
36
+ if hasattr(self, 'regions') and self.regions:
37
+ for region in self.regions:
38
+ self._clients[region] = SecurityLakeClient(region, session=self.session)
39
+
40
+ def get_client(self, region: str) -> Optional[SecurityLakeClient]:
41
+ """
42
+ Get Security Lake client for a specific region.
43
+
44
+ Args:
45
+ region: AWS region name
46
+
47
+ Returns:
48
+ SecurityLakeClient for the region or None if not available
49
+ """
50
+ client = self._clients.get(region)
51
+ if not client:
52
+ logger.debug(f"No Security Lake client available for region {region}")
53
+ return client
54
+
55
+ def get_subscribers(self, region: str) -> List[Dict[str, Any]]:
56
+ """
57
+ Get Security Lake subscribers with caching.
58
+
59
+ Args:
60
+ region: AWS region name
61
+
62
+ Returns:
63
+ List of subscribers
64
+ """
65
+ if not self.account_id:
66
+ logger.debug("Could not determine account ID")
67
+ return []
68
+
69
+ # Check cache first
70
+ cache_key = f"{self.account_id}:{region}"
71
+ if cache_key in self.__class__._subscribers_cache:
72
+ logger.debug(f"Using cached subscribers for {cache_key}")
73
+ return self.__class__._subscribers_cache[cache_key]
74
+
75
+ client = self.get_client(region)
76
+ if not client:
77
+ return []
78
+
79
+ try:
80
+ # Get subscribers from client
81
+ subscribers = client.list_subscribers()
82
+
83
+ # Cache the results
84
+ self.__class__._subscribers_cache[cache_key] = subscribers
85
+ logger.debug(f"Cached {len(subscribers)} subscribers for {cache_key}")
86
+
87
+ return subscribers
88
+ except Exception as e:
89
+ logger.debug(f"Error getting subscribers in {region}: {e}")
90
+ return []
91
+
92
+ def is_security_lake_enabled(self, region: str) -> bool:
93
+ """
94
+ Check if Security Lake is enabled with caching.
95
+
96
+ Args:
97
+ region: AWS region name
98
+
99
+ Returns:
100
+ True if Security Lake is enabled, False otherwise
101
+ """
102
+ if not self.account_id:
103
+ logger.debug("Could not determine account ID")
104
+ return False
105
+
106
+ # Check cache first
107
+ cache_key = f"{self.account_id}:{region}"
108
+ if cache_key in self.__class__._security_lake_status_cache:
109
+ logger.debug(f"Using cached Security Lake status for {cache_key}")
110
+ return self.__class__._security_lake_status_cache[cache_key]
111
+
112
+ client = self.get_client(region)
113
+ if not client:
114
+ return False
115
+
116
+ try:
117
+ # Check if Security Lake is enabled
118
+ is_enabled = client.is_security_lake_enabled()
119
+
120
+ # Cache the results
121
+ self.__class__._security_lake_status_cache[cache_key] = is_enabled
122
+ logger.debug(f"Cached Security Lake status for {cache_key}: {is_enabled}")
123
+
124
+ return is_enabled
125
+ except Exception as e:
126
+ logger.debug(f"Error checking Security Lake status in {region}: {e}")
127
+ self.__class__._security_lake_status_cache[cache_key] = False
128
+ return False
129
+
130
+ def get_organization_configuration(self, region: str) -> Dict[str, Any]:
131
+ """
132
+ Get Security Lake organization configuration with caching.
133
+
134
+ Args:
135
+ region: AWS region name
136
+
137
+ Returns:
138
+ Organization configuration
139
+ """
140
+ if not self.account_id:
141
+ logger.debug("Could not determine account ID")
142
+ return {}
143
+
144
+ # Check cache first
145
+ cache_key = f"{self.account_id}:{region}"
146
+ if cache_key in self.__class__._organization_configuration_cache:
147
+ logger.debug(f"Using cached organization configuration for {cache_key}")
148
+ return self.__class__._organization_configuration_cache[cache_key]
149
+
150
+ client = self.get_client(region)
151
+ if not client:
152
+ return {}
153
+
154
+ try:
155
+ # Get organization configuration from client
156
+ org_config = client.get_organization_configuration()
157
+
158
+ # Cache the results
159
+ self.__class__._organization_configuration_cache[cache_key] = org_config
160
+ logger.debug(f"Cached organization configuration for {cache_key}")
161
+
162
+ return org_config
163
+ except Exception as e:
164
+ logger.debug(f"Error getting organization configuration in {region}: {e}")
165
+ self.__class__._organization_configuration_cache[cache_key] = {}
166
+ return {}
167
+
168
+ def get_log_source_status(self, region: str, source_name: str) -> bool:
169
+ """
170
+ Check if a specific log source is enabled in a region.
171
+
172
+ Args:
173
+ region: AWS region name
174
+ source_name: Name of the log source to check (e.g., 'ROUTE53', 'VPC_FLOW')
175
+
176
+ Returns:
177
+ True if the log source is enabled, False otherwise
178
+ """
179
+ if not self.account_id:
180
+ logger.debug("Could not determine account ID")
181
+ return False
182
+
183
+ # Check cache first
184
+ cache_key = f"{self.account_id}:{region}"
185
+ if cache_key not in self.__class__._log_sources_cache:
186
+ client = self.get_client(region)
187
+ if not client:
188
+ return False
189
+
190
+ # Get log sources from client and cache them
191
+ log_sources = client.list_log_sources()
192
+ self.__class__._log_sources_cache[cache_key] = log_sources
193
+ logger.debug(f"Cached {len(log_sources)} log source entries for {cache_key}")
194
+ else:
195
+ log_sources = self.__class__._log_sources_cache[cache_key]
196
+ logger.debug(f"Using cached log sources for {cache_key}")
197
+
198
+ # Navigate the nested structure to find the source
199
+ # Structure: sources[].sources[].awsLogSource.sourceName
200
+ for log_source_entry in log_sources:
201
+ for source in log_source_entry.get("sources", []):
202
+ aws_log_source = source.get("awsLogSource", {})
203
+ if aws_log_source.get("sourceName") == source_name:
204
+ # Check if source is collecting
205
+ source_status = source.get("sourceStatus", [])
206
+ for status in source_status:
207
+ if status.get("status") == "COLLECTING":
208
+ return True
209
+ return False
210
+
211
+ return False
212
+
213
+ def get_delegated_administrators(self, region: str) -> List[Dict[str, Any]]:
214
+ """
215
+ Get Security Lake delegated administrators with caching.
216
+
217
+ Args:
218
+ region: AWS region name
219
+
220
+ Returns:
221
+ List of delegated administrators
222
+ """
223
+ if not self.account_id:
224
+ logger.debug("Could not determine account ID")
225
+ return []
226
+
227
+ # Check cache first
228
+ cache_key = f"{self.account_id}:{region}"
229
+ if cache_key in self.__class__._delegated_admin_cache:
230
+ logger.debug(f"Using cached delegated administrators for {cache_key}")
231
+ return self.__class__._delegated_admin_cache[cache_key]
232
+
233
+ client = self.get_client(region)
234
+ if not client:
235
+ return []
236
+
237
+ try:
238
+ # Get delegated administrators from client
239
+ delegated_admins = client.list_delegated_administrators()
240
+
241
+ # Cache the results
242
+ self.__class__._delegated_admin_cache[cache_key] = delegated_admins
243
+ logger.debug(f"Cached {len(delegated_admins)} delegated administrators for {cache_key}")
244
+
245
+ return delegated_admins
246
+ except Exception as e:
247
+ logger.debug(f"Error getting delegated administrators in {region}: {e}")
248
+ return []
249
+
250
+ def get_organization_accounts(self, region: str) -> List[Dict[str, Any]]:
251
+ """
252
+ Get all organization accounts with caching.
253
+
254
+ Args:
255
+ region: AWS region name
256
+
257
+ Returns:
258
+ List of organization accounts
259
+ """
260
+ if not self.account_id:
261
+ logger.debug("Could not determine account ID")
262
+ return []
263
+
264
+ # Check cache first
265
+ cache_key = f"{self.account_id}:{region}"
266
+ if cache_key in self.__class__._organization_accounts_cache:
267
+ logger.debug(f"Using cached organization accounts for {cache_key}")
268
+ return self.__class__._organization_accounts_cache[cache_key]
269
+
270
+ client = self.get_client(region)
271
+ if not client:
272
+ return []
273
+
274
+ try:
275
+ # Get organization accounts from client
276
+ accounts = client.list_organization_accounts()
277
+
278
+ # Cache the results
279
+ self.__class__._organization_accounts_cache[cache_key] = accounts
280
+ logger.debug(f"Cached {len(accounts)} organization accounts for {cache_key}")
281
+
282
+ return accounts
283
+ except Exception as e:
284
+ logger.debug(f"Error getting organization accounts in {region}: {e}")
285
+ return []
286
+
287
+ def get_sqs_queue_encryption(self, region: str, queue_url: str) -> Optional[str]:
288
+ """
289
+ Get SQS queue encryption key with caching.
290
+
291
+ Args:
292
+ region: AWS region name
293
+ queue_url: SQS queue URL
294
+
295
+ Returns:
296
+ KMS key ID or None if not encrypted/error
297
+ """
298
+ cache_key = f"{self.account_id}:{region}:{queue_url}"
299
+ if cache_key in self.__class__._sqs_encryption_cache:
300
+ logger.debug(f"Using cached SQS encryption for {queue_url}")
301
+ return self.__class__._sqs_encryption_cache[cache_key]
302
+
303
+ client = self.get_client(region)
304
+ if not client:
305
+ logger.debug(f"No client available for region {region}")
306
+ self.__class__._sqs_encryption_cache[cache_key] = None
307
+ return None
308
+
309
+ try:
310
+ kms_key = client.get_sqs_queue_encryption(queue_url)
311
+ self.__class__._sqs_encryption_cache[cache_key] = kms_key
312
+ logger.debug(f"SQS queue {queue_url} encryption: {kms_key}")
313
+ return kms_key
314
+ except Exception as e:
315
+ logger.error(f"Error getting SQS encryption for {queue_url} in {region}: {e}")
316
+ self.__class__._sqs_encryption_cache[cache_key] = None
317
+ return None
318
+
319
+ def get_data_lake_sources(self, region: str, account_id: str = None) -> List[Dict[str, Any]]:
320
+ """
321
+ Get Security Lake data lake sources with caching.
322
+
323
+ Args:
324
+ region: AWS region name
325
+ account_id: Optional account ID. If None, gets all accounts.
326
+
327
+ Returns:
328
+ List of data lake sources
329
+ """
330
+ cache_key = f"{self.account_id}:{region}:data_lake_sources:{account_id or 'all'}"
331
+ if cache_key in self.__class__._log_sources_cache:
332
+ logger.debug(f"Using cached data lake sources for {cache_key}")
333
+ return self.__class__._log_sources_cache[cache_key]
334
+
335
+ client = self.get_client(region)
336
+ if not client:
337
+ logger.debug(f"No client available for region {region}")
338
+ self.__class__._log_sources_cache[cache_key] = []
339
+ return []
340
+
341
+ try:
342
+ # Call with or without account_id based on parameter
343
+ data_lake_sources = client.get_data_lake_sources(account_id)
344
+ self.__class__._log_sources_cache[cache_key] = data_lake_sources
345
+ logger.debug(f"Cached {len(data_lake_sources)} data lake sources for {cache_key}")
346
+ return data_lake_sources
347
+ except Exception as e:
348
+ # Use debug level for UnauthorizedException as it's expected when Security Lake isn't enabled
349
+ if "UnauthorizedException" in str(e) or "Unauthorized" in str(e):
350
+ logger.debug(f"Security Lake not enabled in {region}: {e}")
351
+ else:
352
+ logger.error(f"Error getting data lake sources in {region}: {e}")
353
+ self.__class__._log_sources_cache[cache_key] = []
354
+ return []
355
+
356
+ def get_enabled_regions(self) -> List[str]:
357
+ """
358
+ Get list of regions where Security Lake is enabled.
359
+
360
+ Returns:
361
+ List of region names where Security Lake is enabled
362
+ """
363
+ enabled_regions = []
364
+
365
+ for region in self.regions:
366
+ if self.is_security_lake_enabled(region):
367
+ logger.debug(f"Security Lake is enabled in {region}")
368
+ enabled_regions.append(region)
369
+ else:
370
+ logger.debug(f"Security Lake is not enabled in {region}")
371
+
372
+ return enabled_regions
373
+
374
+ def get_account_log_source_status(self, region: str, source_name: str) -> bool:
375
+ """
376
+ Check if a specific log source is enabled for the current account in a region.
377
+ Uses get_data_lake_sources API for account-specific status.
378
+
379
+ Args:
380
+ region: AWS region name
381
+ source_name: Name of the log source to check (e.g., 'ROUTE53', 'VPC_FLOW')
382
+
383
+ Returns:
384
+ True if the log source is enabled for this account, False otherwise
385
+ """
386
+ if not self.account_id:
387
+ logger.debug("Could not determine account ID")
388
+ return False
389
+
390
+ # Check cache first
391
+ cache_key = f"account_sources:{self.account_id}:{region}"
392
+ if cache_key not in self.__class__._log_sources_cache:
393
+ client = self.get_client(region)
394
+ if not client:
395
+ return False
396
+
397
+ # Get account-specific data lake sources and cache them
398
+ # Pass the account ID as a string (not the full account object)
399
+ data_lake_sources = client.get_data_lake_sources(self.account_id)
400
+ self.__class__._log_sources_cache[cache_key] = data_lake_sources
401
+ logger.debug(f"Cached {len(data_lake_sources)} account data lake sources for {cache_key}")
402
+ else:
403
+ data_lake_sources = self.__class__._log_sources_cache[cache_key]
404
+ logger.debug(f"Using cached account data lake sources for {cache_key}")
405
+
406
+ # Check if the source is enabled for this account
407
+ for source_entry in data_lake_sources:
408
+ if source_entry.get("account") == self.account_id and source_entry.get("sourceName") == source_name:
409
+ return True
410
+
411
+ return False
412
+
413
+ def check_log_source_configured(self, region: str, source_name: str, account_id: str = None,
414
+ required_version: str = "2.0") -> bool:
415
+ """
416
+ Check if a log source is configured using list-log-sources API.
417
+ This checks configuration, not collection status.
418
+
419
+ Args:
420
+ region: AWS region name
421
+ source_name: Name of the log source (e.g., 'ROUTE53', 'VPC_FLOW')
422
+ account_id: Account ID to check (defaults to current account)
423
+ required_version: Required source version (default: "2.0")
424
+
425
+ Returns:
426
+ True if source is configured with correct version, False otherwise
427
+ """
428
+ target_account = account_id or self.account_id
429
+ if not target_account:
430
+ logger.debug("Could not determine account ID")
431
+ return False
432
+
433
+ # Check cache first
434
+ cache_key = f"list_sources:{target_account}:{region}"
435
+ if cache_key not in self.__class__._log_sources_cache:
436
+ client = self.get_client(region)
437
+ if not client:
438
+ return False
439
+
440
+ # Get configured log sources and cache them
441
+ try:
442
+ log_sources = client.list_log_sources(regions=[region], accounts=[target_account])
443
+ self.__class__._log_sources_cache[cache_key] = log_sources
444
+ logger.debug(f"Cached log sources for {cache_key}")
445
+ except Exception as e:
446
+ logger.error(f"Error listing log sources: {e}")
447
+ return False
448
+ else:
449
+ log_sources = self.__class__._log_sources_cache[cache_key]
450
+ logger.debug(f"Using cached log sources for {cache_key}")
451
+
452
+ # Check if the source is configured with correct version
453
+ for source_entry in log_sources:
454
+ if source_entry.get("account") == target_account and source_entry.get("region") == region:
455
+ for source in source_entry.get("sources", []):
456
+ aws_log_source = source.get("awsLogSource", {})
457
+ if (aws_log_source.get("sourceName") == source_name and
458
+ aws_log_source.get("sourceVersion") == required_version):
459
+ return True
460
+
461
+ return False
@@ -0,0 +1 @@
1
+ """Security Lake checks."""
@@ -0,0 +1,98 @@
1
+ """Check if Amazon Security Lake is enabled."""
2
+
3
+ from typing import List, Dict, Any
4
+ from sraverify.services.securitylake.base import SecurityLakeCheck
5
+ from sraverify.core.logging import logger
6
+
7
+
8
+ class SRA_SECURITYLAKE_01(SecurityLakeCheck):
9
+ """Check if Amazon Security Lake is enabled."""
10
+
11
+ def __init__(self):
12
+ """Initialize check."""
13
+ super().__init__()
14
+ self.account_type = "log-archive" # Check all org accounts from delegated admin
15
+ self.check_id = "SRA-SECURITYLAKE-01"
16
+ self.check_name = "Security Lake is enabled for all organization accounts"
17
+ self.severity = "HIGH"
18
+ self.description = (
19
+ "This check verifies whether Amazon Security Lake is enabled for all active accounts in the organization. "
20
+ "Amazon Security Lake is a fully managed security data lake service that you "
21
+ "can use to automatically centralize security data from AWS environments, "
22
+ "SaaS providers, on premises, cloud sources, and third-party sources into a "
23
+ "purpose-built data lake that's stored in your AWS account. The data lake is "
24
+ "backed by Amazon S3 buckets, and you retain ownership over your data. You "
25
+ "must enable security lake in every AWS account and AWS region to collect "
26
+ "security logs and event from your entire AWS environment. "
27
+ "This check runs from the delegated administrator account "
28
+ "and validates configuration across all organization member accounts."
29
+ )
30
+ self.check_logic = (
31
+ "Checks if Security Lake is enabled for all active organization accounts by calling get_data_lake_sources API. "
32
+ "The check passes if Security Lake is enabled for all active accounts in the organization. "
33
+ "The check fails if any active account does not have Security Lake enabled."
34
+ )
35
+
36
+ def execute(self) -> List[Dict[str, Any]]:
37
+ """
38
+ Execute the check.
39
+
40
+ Returns:
41
+ List of findings
42
+ """
43
+
44
+ for region in self.regions:
45
+ logger.debug(f"Checking if Security Lake is enabled for all organization accounts in {region}")
46
+
47
+ # Get all organization accounts
48
+ org_accounts = self.get_organization_accounts(region)
49
+ if not org_accounts:
50
+ logger.debug("No organization accounts found, checking current account only")
51
+ org_accounts = [{'Id': self.account_id, 'Status': 'ACTIVE'}]
52
+
53
+ # Create sets of active account IDs
54
+ active_org_account_ids = set()
55
+ for account in org_accounts:
56
+ if account.get('Status') == 'ACTIVE':
57
+ active_org_account_ids.add(account.get('Id'))
58
+
59
+ # Get accounts with Security Lake enabled
60
+ enabled_accounts = set()
61
+ sources_data = self.get_data_lake_sources(region) # No account_id = get all accounts
62
+ for source in sources_data:
63
+ account_id = source.get('account')
64
+ if account_id:
65
+ enabled_accounts.add(account_id)
66
+
67
+ # Check each account in the organization
68
+ for account_id in active_org_account_ids:
69
+ resource_id = f"arn:aws:securitylake:{region}:{account_id}:datalake/default"
70
+
71
+ if account_id not in enabled_accounts:
72
+ self.findings.append(
73
+ self.create_finding(
74
+ status="FAIL",
75
+ region=region,
76
+ resource_id=resource_id,
77
+ checked_value="Security Lake enabled",
78
+ actual_value=f"Security Lake is not enabled for account {account_id}",
79
+ remediation=(
80
+ f"Enable Security Lake for account {account_id}. In the Security Lake console, "
81
+ "navigate to Settings and enable Security Lake. Alternatively, use the AWS CLI command: "
82
+ f"aws securitylake create-data-lake --region {region}"
83
+ )
84
+ )
85
+ )
86
+ else:
87
+ self.findings.append(
88
+ self.create_finding(
89
+ status="PASS",
90
+ region=region,
91
+ resource_id=resource_id,
92
+ checked_value="Security Lake enabled",
93
+ actual_value=f"Security Lake is enabled for account {account_id}",
94
+ remediation="No remediation needed"
95
+ )
96
+ )
97
+
98
+ return self.findings