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,72 @@
1
+ """Check if Security Lake has a delegated administrator."""
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_14(SecurityLakeCheck):
9
+ """Check if Security Lake has a delegated administrator."""
10
+
11
+ def __init__(self):
12
+ """Initialize check."""
13
+ super().__init__()
14
+ self.account_type = "management" # Delegated admin check runs from management account
15
+ self.check_id = "SRA-SECURITYLAKE-14"
16
+ self.check_name = "Security Lake has delegated administrator"
17
+ self.severity = "CRITICAL"
18
+ self.description = (
19
+ "This check verifies whether Security Lake service administration for "
20
+ "the AWS Organization is delegated out from AWS Organization management "
21
+ "account to a member account."
22
+ )
23
+ self.check_logic = (
24
+ "Checks if Security Lake has a delegated administrator configured. "
25
+ "The check passes if at least one delegated administrator is found for the Security Lake service. "
26
+ "The check fails if no delegated administrator is configured."
27
+ )
28
+
29
+ def execute(self) -> List[Dict[str, Any]]:
30
+ """Run check."""
31
+ findings = []
32
+
33
+ # This is a global check, so we only need to run it once
34
+ # Use the first region just to make the API call
35
+ region = self.regions[0] if self.regions else "us-east-1"
36
+ resource_id = f"arn:aws:organizations::global:delegatedadministrator/securitylake"
37
+
38
+ # Get delegated administrators using the base class method
39
+ delegated_admin = self.get_delegated_administrators(region)
40
+
41
+ if not delegated_admin:
42
+ findings.append(
43
+ self.create_finding(
44
+ status="FAIL",
45
+ region="global",
46
+ resource_id=resource_id,
47
+ checked_value="Delegated administrator configured",
48
+ actual_value="No delegated administrator configured for Security Lake",
49
+ remediation=(
50
+ "Configure a delegated administrator for Security Lake. In the AWS Organizations console, "
51
+ "navigate to Services > Security Lake and delegate administration to a member account. "
52
+ "Alternatively, use the AWS CLI command: "
53
+ "aws organizations register-delegated-administrator --service-principal securitylake.amazonaws.com "
54
+ "--account-id ACCOUNT_ID"
55
+ )
56
+ )
57
+ )
58
+ else:
59
+ admin_info = delegated_admin[0] if delegated_admin else {} # Get first admin if exists
60
+ admin_id = admin_info.get('Id', 'Unknown')
61
+ findings.append(
62
+ self.create_finding(
63
+ status="PASS",
64
+ region="global",
65
+ resource_id=resource_id,
66
+ checked_value="Delegated administrator configured",
67
+ actual_value=f"Delegated administrator {admin_id} is configured for Security Lake",
68
+ remediation="No remediation needed"
69
+ )
70
+ )
71
+
72
+ return findings
@@ -0,0 +1,120 @@
1
+ """Check if Security Lake delegated admin is Log Archive account."""
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_15(SecurityLakeCheck):
9
+ """Check if Security Lake delegated admin is Log Archive account."""
10
+
11
+ def __init__(self):
12
+ """Initialize check."""
13
+ super().__init__()
14
+ self.account_type = "management" # Delegated admin check runs from management account
15
+ self.check_id = "SRA-SECURITYLAKE-15"
16
+ self.check_name = "Security Lake delegated admin is log archive account"
17
+ self.severity = "CRITICAL"
18
+ self.description = (
19
+ "This check verifies whether Security Lake delegated admin account "
20
+ "is the Log Archive account of your AWS organization. The Log Archive "
21
+ "account is dedicated to ingesting and archiving all security-related "
22
+ "logs and backups."
23
+ )
24
+ self.check_logic = (
25
+ "Checks if the Security Lake delegated administrator is the Log Archive account. "
26
+ "The check passes if the delegated administrator account ID matches the Log Archive account ID. "
27
+ "The check fails if there is no delegated administrator or if the delegated administrator "
28
+ "is not the Log Archive account."
29
+ )
30
+ # Initialize log archive account attribute
31
+ self._log_archive_accounts = None
32
+
33
+ def execute(self) -> List[Dict[str, Any]]:
34
+ """
35
+ Execute the check.
36
+
37
+ Returns:
38
+ List of findings
39
+ """
40
+
41
+ # This is a global check, so we only need to run it once
42
+ # Use the first region just to make the API call
43
+ region = self.regions[0] if self.regions else "us-east-1"
44
+ resource_id = f"arn:aws:organizations::global:delegatedadministrator/securitylake"
45
+
46
+ # Check if Log Archive account ID is provided
47
+ if not hasattr(self, '_log_archive_accounts') or not self._log_archive_accounts:
48
+ self.findings.append(
49
+ self.create_finding(
50
+ status="ERROR",
51
+ region="global",
52
+ resource_id=resource_id,
53
+ checked_value="Delegated administrator is Log Archive account",
54
+ actual_value="Log Archive Account ID not provided",
55
+ remediation="Provide the Log Archive account ID using the --log-archive-account parameter"
56
+ )
57
+ )
58
+ return self.findings
59
+
60
+ # Use the first log archive account if multiple are provided
61
+ log_archive_account = self._log_archive_accounts[0]
62
+ logger.debug(f"Using Log Archive account: {log_archive_account}")
63
+
64
+ # Get delegated administrators using the base class method
65
+ delegated_admin = self.get_delegated_administrators(region)
66
+
67
+ if not delegated_admin:
68
+ self.findings.append(
69
+ self.create_finding(
70
+ status="FAIL",
71
+ region="global",
72
+ resource_id=resource_id,
73
+ checked_value=f"Delegated administrator is Log Archive account {log_archive_account}",
74
+ actual_value="No delegated administrator configured for Security Lake",
75
+ remediation=(
76
+ f"Configure a delegated administrator for Security Lake and ensure it is the Log Archive account. "
77
+ f"In the AWS Organizations console, navigate to Services > Security Lake and delegate "
78
+ f"administration to the Log Archive account {log_archive_account}."
79
+ )
80
+ )
81
+ )
82
+ return self.findings
83
+
84
+ # Get the delegated admin account ID
85
+ admin_info = delegated_admin[0] if delegated_admin else {}
86
+ admin_id = admin_info.get('Id', 'Unknown')
87
+
88
+ # Check if the delegated admin is the Log Archive account
89
+ if admin_id == log_archive_account:
90
+ self.findings.append(
91
+ self.create_finding(
92
+ status="PASS",
93
+ region="global",
94
+ resource_id=resource_id,
95
+ checked_value=f"Delegated administrator is Log Archive account {log_archive_account}",
96
+ actual_value=f"Delegated administrator {admin_id} is the Log Archive account",
97
+ remediation="No remediation needed"
98
+ )
99
+ )
100
+ else:
101
+ self.findings.append(
102
+ self.create_finding(
103
+ status="FAIL",
104
+ region="global",
105
+ resource_id=resource_id,
106
+ checked_value=f"Delegated administrator is Log Archive account {log_archive_account}",
107
+ actual_value=f"Delegated administrator {admin_id} is not the Log Archive account {log_archive_account}",
108
+ remediation=(
109
+ f"Update the delegated administrator for Security Lake to be the Log Archive account. "
110
+ f"1. Deregister the current delegated administrator: "
111
+ f"aws organizations deregister-delegated-administrator --service-principal securitylake.amazonaws.com "
112
+ f"--account-id {admin_id} "
113
+ f"2. Register the Log Archive account: "
114
+ f"aws organizations register-delegated-administrator --service-principal securitylake.amazonaws.com "
115
+ f"--account-id {log_archive_account}"
116
+ )
117
+ )
118
+ )
119
+
120
+ return self.findings
@@ -0,0 +1,104 @@
1
+ """Check if Audit account has query access."""
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_16(SecurityLakeCheck):
9
+ """Check if Audit account has query access."""
10
+
11
+ def __init__(self):
12
+ """Initialize check."""
13
+ super().__init__()
14
+ self.check_id = "SRA-SECURITYLAKE-16"
15
+ self.check_name = "Security Lake audit account has query access"
16
+ self.severity = "HIGH"
17
+ self.description = (
18
+ "This check verifies whether the AWS Organization audit "
19
+ "account is set up as query access subscriber. These "
20
+ "subscribers directly query AWS Lake Formation tables in your S3 "
21
+ "bucket with services like Amazon Athena. Separation of log storage "
22
+ "(Log Archive account) and log access (audit account) "
23
+ "helps is separation of duties and helps in least privilege access."
24
+ )
25
+ self.check_logic = (
26
+ "Checks if the audit account is set up as a query access subscriber. "
27
+ "The check passes if there is at least one subscriber with type QUERY_ACCESS and "
28
+ "the account ID matches the audit account ID. "
29
+ "The check fails if no query access subscriber is found for the audit account."
30
+ )
31
+ self._audit_accounts = []
32
+
33
+ def execute(self) -> List[Dict[str, Any]]:
34
+ """Run check."""
35
+ findings = []
36
+
37
+ # Check if audit account ID is provided
38
+ if not self._audit_accounts:
39
+ logger.warning("Audit account ID not provided. Check cannot be completed.")
40
+ findings.append(
41
+ self.create_finding(
42
+ status="ERROR",
43
+ region="global",
44
+ resource_id=f"arn:aws:securitylake::global:subscriber/query-access",
45
+ checked_value="Security tooling account has query access",
46
+ actual_value="Audit account ID not provided",
47
+ remediation="Run sraverify with --audit-account parameter"
48
+ )
49
+ )
50
+ return findings
51
+
52
+ # Use the first audit account in the list
53
+ audit_account_id = self._audit_accounts[0]
54
+ logger.debug(f"Using audit account ID: {audit_account_id}")
55
+
56
+ # Check each region
57
+ for region in self.regions:
58
+ resource_id = f"arn:aws:securitylake:{region}:{self.account_id}:subscriber/query-access"
59
+
60
+ # Get subscribers using the base class method
61
+ subscribers = self.get_subscribers(region)
62
+
63
+ # Check if any subscriber is the audit account with query access
64
+ audit_subscriber = next(
65
+ (sub for sub in subscribers
66
+ if "LAKEFORMATION" in sub.get("accessTypes", []) and
67
+ sub.get("subscriberIdentity", {}).get("principal") == audit_account_id),
68
+ None
69
+ )
70
+
71
+ if not audit_subscriber:
72
+ logger.debug(f"Audit account is not set up as query access subscriber in {region}")
73
+ findings.append(
74
+ self.create_finding(
75
+ status="FAIL",
76
+ region=region,
77
+ resource_id=resource_id,
78
+ checked_value=f"Audit account {audit_account_id} has query access",
79
+ actual_value=f"Audit account {audit_account_id} is not set up as query access subscriber",
80
+ remediation=(
81
+ f"Set up the audit account {audit_account_id} as a query access subscriber in Security Lake. "
82
+ "In the Security Lake console, navigate to Subscribers > Create subscriber and select "
83
+ f"the audit account {audit_account_id} with Query access type."
84
+ )
85
+ )
86
+ )
87
+ else:
88
+ # Get subscriber ID for resource ID
89
+ subscriber_id = audit_subscriber.get("subscriberId", "default")
90
+ resource_id = f"arn:aws:securitylake:{region}:{self.account_id}:subscriber/{subscriber_id}"
91
+
92
+ logger.debug(f"Audit account is set up as query access subscriber in {region}")
93
+ findings.append(
94
+ self.create_finding(
95
+ status="PASS",
96
+ region=region,
97
+ resource_id=resource_id,
98
+ checked_value=f"Audit account {audit_account_id} has query access",
99
+ actual_value=f"Audit account {audit_account_id} is set up as query access subscriber",
100
+ remediation="No remediation needed"
101
+ )
102
+ )
103
+
104
+ return findings
@@ -0,0 +1,103 @@
1
+ """Check if Audit account has data access."""
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_17(SecurityLakeCheck):
9
+ """Check if Audit account has data access."""
10
+
11
+ def __init__(self):
12
+ """Initialize check."""
13
+ super().__init__()
14
+ self.check_id = "SRA-SECURITYLAKE-17"
15
+ self.check_name = "Security Lake audit account has data access"
16
+ self.severity = "HIGH"
17
+ self.description = (
18
+ "This check verifies whether the AWS Organization "
19
+ "Audit account is set up as data access subscriber. These "
20
+ "subscribers can directly access the S3 objects and receive "
21
+ "notifications of new objects through a subscription endpoint or "
22
+ "by polling an Amazon SQS queue."
23
+ )
24
+ self.check_logic = (
25
+ "Checks if the audit account is set up as a data access subscriber. "
26
+ "The check passes if there is at least one subscriber with type DATA_ACCESS and "
27
+ "the account ID matches the audit account ID. "
28
+ "The check fails if no data access subscriber is found for the audit account."
29
+ )
30
+ self._audit_accounts = []
31
+
32
+ def execute(self) -> List[Dict[str, Any]]:
33
+ """Run check."""
34
+ findings = []
35
+
36
+ # Check if audit account ID is provided
37
+ if not self._audit_accounts:
38
+ logger.warning("Audit account ID not provided. Check cannot be completed.")
39
+ findings.append(
40
+ self.create_finding(
41
+ status="ERROR",
42
+ region="global",
43
+ resource_id=f"arn:aws:securitylake::global:subscriber/data-access",
44
+ checked_value="Security tooling account has data access",
45
+ actual_value="Audit account ID not provided",
46
+ remediation="Run sraverify with --audit-account parameter"
47
+ )
48
+ )
49
+ return findings
50
+
51
+ # Use the first audit account in the list
52
+ audit_account_id = self._audit_accounts[0]
53
+ logger.debug(f"Using audit account ID: {audit_account_id}")
54
+
55
+ # Check each region
56
+ for region in self.regions:
57
+ resource_id = f"arn:aws:securitylake:{region}:{self.account_id}:subscriber/data-access"
58
+
59
+ # Get subscribers using the base class method
60
+ subscribers = self.get_subscribers(region)
61
+
62
+ # Check if any subscriber is the audit account with data access
63
+ audit_subscriber = next(
64
+ (sub for sub in subscribers
65
+ if "S3" in sub.get("accessTypes", []) and
66
+ sub.get("subscriberIdentity", {}).get("principal") == audit_account_id),
67
+ None
68
+ )
69
+
70
+ if not audit_subscriber:
71
+ logger.debug(f"Audit account is not set up as data access subscriber in {region}")
72
+ findings.append(
73
+ self.create_finding(
74
+ status="FAIL",
75
+ region=region,
76
+ resource_id=resource_id,
77
+ checked_value=f"Audit account {audit_account_id} has data access",
78
+ actual_value=f"Audit account {audit_account_id} is not set up as data access subscriber",
79
+ remediation=(
80
+ f"Set up the audit account {audit_account_id} as a data access subscriber in Security Lake. "
81
+ "In the Security Lake console, navigate to Subscribers > Create subscriber and select "
82
+ f"the audit account {audit_account_id} with Data access type."
83
+ )
84
+ )
85
+ )
86
+ else:
87
+ # Get subscriber ID for resource ID
88
+ subscriber_id = audit_subscriber.get("subscriberId", "default")
89
+ resource_id = f"arn:aws:securitylake:{region}:{self.account_id}:subscriber/{subscriber_id}"
90
+
91
+ logger.debug(f"Audit account is set up as data access subscriber in {region}")
92
+ findings.append(
93
+ self.create_finding(
94
+ status="PASS",
95
+ region=region,
96
+ resource_id=resource_id,
97
+ checked_value=f"Audit account {audit_account_id} has data access",
98
+ actual_value=f"Audit account {audit_account_id} is set up as data access subscriber",
99
+ remediation="No remediation needed"
100
+ )
101
+ )
102
+
103
+ return findings
@@ -0,0 +1,247 @@
1
+ """Security Lake client for interacting with AWS Security Lake service."""
2
+
3
+ from typing import Dict, List, Optional, Any
4
+ import boto3
5
+ from botocore.exceptions import ClientError
6
+ from sraverify.core.logging import logger
7
+
8
+
9
+ class SecurityLakeClient:
10
+ """Client for interacting with AWS Security Lake service."""
11
+
12
+ def __init__(self, region: str, session: Optional[boto3.Session] = None):
13
+ """
14
+ Initialize Security Lake client.
15
+
16
+ Args:
17
+ region: AWS region name
18
+ session: Optional boto3 session
19
+ """
20
+ self.region = region
21
+ self.session = session or boto3.Session()
22
+ self.client = self.session.client('securitylake', region_name=region)
23
+ self.org_client = self.session.client('organizations', region_name=region)
24
+
25
+ def is_security_lake_enabled(self):
26
+ """
27
+ Check if Security Lake is enabled in the region.
28
+
29
+ Returns:
30
+ True if enabled, False otherwise
31
+ """
32
+ try:
33
+ response = self.client.list_data_lakes(regions=[self.region])
34
+ data_lakes = response.get('dataLakes', [])
35
+ return len(data_lakes) > 0
36
+ except ClientError as e:
37
+ logger.debug(f"Error checking Security Lake status in {self.region}: {e}")
38
+ return False
39
+
40
+ def get_organization_configuration(self):
41
+ """
42
+ Get Security Lake organization configuration.
43
+
44
+ Returns:
45
+ Organization configuration or empty dict if error
46
+ """
47
+ try:
48
+ response = self.client.get_data_lake_organization_configuration()
49
+ return response
50
+ except self.client.exceptions.ResourceNotFoundException:
51
+ logger.debug(f"No organization configuration found in region {self.region}")
52
+ return {}
53
+ except ClientError as e:
54
+ logger.error(f"Error getting organization configuration in {self.region}: {e}")
55
+ return {}
56
+
57
+ def list_data_lakes(self):
58
+ """
59
+ List Security Lake data lakes.
60
+
61
+ Returns:
62
+ List of data lakes or empty list if error
63
+ """
64
+ try:
65
+ response = self.client.list_data_lakes(regions=[self.region])
66
+ return response.get("dataLakes", [])
67
+ except self.client.exceptions.ResourceNotFoundException:
68
+ logger.debug(f"No data lakes found in region {self.region}")
69
+ return []
70
+ except ClientError as e:
71
+ logger.error(f"Error listing data lakes in {self.region}: {e}")
72
+ return []
73
+
74
+ def list_log_sources(self, regions=None, accounts=None):
75
+ """
76
+ List enabled log sources with pagination support.
77
+
78
+ Args:
79
+ regions: List of regions to filter (optional)
80
+ accounts: List of account IDs to filter (optional)
81
+
82
+ Returns:
83
+ List of log sources or empty list if error
84
+ """
85
+ try:
86
+ params = {}
87
+ if regions:
88
+ params['regions'] = regions
89
+ if accounts:
90
+ params['accounts'] = accounts
91
+
92
+ response = self.client.list_log_sources(**params)
93
+ log_sources = response.get("sources", [])
94
+
95
+ # Handle pagination
96
+ while response.get('nextToken'):
97
+ params['nextToken'] = response['nextToken']
98
+ response = self.client.list_log_sources(**params)
99
+ log_sources.extend(response.get("sources", []))
100
+
101
+ return log_sources
102
+ except self.client.exceptions.ResourceNotFoundException:
103
+ logger.debug(f"No log sources found in region {self.region}")
104
+ return []
105
+ except ClientError as e:
106
+ logger.error(f"Error listing log sources in {self.region}: {e}")
107
+ return []
108
+
109
+ def list_subscribers(self):
110
+ """
111
+ List Security Lake subscribers with pagination support.
112
+
113
+ Returns:
114
+ List of subscribers or empty list if error
115
+ """
116
+ try:
117
+ response = self.client.list_subscribers()
118
+ subscribers = response.get("subscribers", [])
119
+
120
+ # Handle pagination
121
+ while response.get('nextToken'):
122
+ response = self.client.list_subscribers(nextToken=response['nextToken'])
123
+ subscribers.extend(response.get("subscribers", []))
124
+
125
+ return subscribers
126
+ except self.client.exceptions.ResourceNotFoundException:
127
+ logger.debug(f"No subscribers found in region {self.region}")
128
+ return []
129
+ except ClientError as e:
130
+ logger.error(f"Error listing subscribers in {self.region}: {e}")
131
+ return []
132
+
133
+ def get_delegated_admin(self):
134
+ """
135
+ Get Security Lake delegated admin account.
136
+
137
+ Returns:
138
+ Delegated admin info or None if error
139
+ """
140
+ try:
141
+ response = self.org_client.list_delegated_administrators(ServicePrincipal="securitylake.amazonaws.com")
142
+ admins = response.get("DelegatedAdministrators", [])
143
+ return admins[0] if admins else None
144
+ except ClientError as e:
145
+ logger.error(f"Error getting delegated admin: {e}")
146
+ return None
147
+
148
+ def list_delegated_administrators(self, service_principal: str = "securitylake.amazonaws.com") -> List[Dict[str, Any]]:
149
+ """
150
+ List delegated administrators for SecurityLake.
151
+
152
+ Args:
153
+ service_principal: Service principal to check for delegated administrators
154
+
155
+ Returns:
156
+ List of delegated administrators or empty list if error
157
+ """
158
+ try:
159
+ response = self.org_client.list_delegated_administrators(ServicePrincipal=service_principal)
160
+ delegated_admins = response.get("DelegatedAdministrators", [])
161
+
162
+ logger.debug(f"Found {len(delegated_admins)} delegated administrators for {service_principal}")
163
+ for admin in delegated_admins:
164
+ logger.debug(f"Delegated admin: {admin.get('Id')} - {admin.get('Name')}")
165
+ return delegated_admins
166
+ except ClientError as e:
167
+ logger.error(f"Error listing delegated administrators for {service_principal}: {e}")
168
+ return []
169
+
170
+ def get_sqs_queue_encryption(self, queue_url):
171
+ """
172
+ Get SQS queue encryption settings.
173
+
174
+ Args:
175
+ queue_url: SQS queue URL
176
+
177
+ Returns:
178
+ KMS key ID or None if error
179
+ """
180
+ try:
181
+ sqs = self.session.client('sqs', region_name=self.region)
182
+ response = sqs.get_queue_attributes(
183
+ QueueUrl=queue_url,
184
+ AttributeNames=["KmsMasterKeyId"]
185
+ )
186
+ return response.get("Attributes", {}).get("KmsMasterKeyId")
187
+ except ClientError as e:
188
+ logger.error(f"Error getting SQS queue encryption for {queue_url}: {e}")
189
+ return None
190
+
191
+ def list_organization_accounts(self) -> List[Dict[str, Any]]:
192
+ """
193
+ List all accounts in the organization.
194
+
195
+ Returns:
196
+ List of organization accounts or empty list if error
197
+ """
198
+ try:
199
+ response = self.org_client.list_accounts()
200
+ accounts = response.get('Accounts', [])
201
+
202
+ # Handle pagination
203
+ while response.get('NextToken'):
204
+ response = self.org_client.list_accounts(NextToken=response['NextToken'])
205
+ accounts.extend(response.get('Accounts', []))
206
+
207
+ logger.debug(f"Found {len(accounts)} organization accounts")
208
+ return accounts
209
+ except ClientError as e:
210
+ logger.error(f"Error listing organization accounts: {e}")
211
+ return []
212
+
213
+ def get_data_lake_sources(self, account_id: str = None):
214
+ """
215
+ Get data lake sources for a specific account.
216
+
217
+ Args:
218
+ account_id: AWS account ID string to check sources for
219
+
220
+ Returns:
221
+ List of data lake sources or empty list if error
222
+ """
223
+ try:
224
+ request_body = {}
225
+ if account_id:
226
+ # Ensure account_id is a string (extract ID if it's a dict like SecurityHub pattern)
227
+ if isinstance(account_id, dict):
228
+ if 'Id' in account_id:
229
+ account_id = account_id['Id']
230
+ else:
231
+ logger.error(f"Cannot extract account ID from dict: {account_id}")
232
+ return []
233
+
234
+ request_body["accounts"] = [account_id]
235
+
236
+ response = self.client.get_data_lake_sources(**request_body)
237
+ return response.get("dataLakeSources", [])
238
+ except self.client.exceptions.ResourceNotFoundException:
239
+ logger.debug(f"No data lake sources found in region {self.region}")
240
+ return []
241
+ except ClientError as e:
242
+ # Use debug level for UnauthorizedException as it's expected when Security Lake isn't enabled
243
+ if e.response.get('Error', {}).get('Code') == 'UnauthorizedException':
244
+ logger.debug(f"Security Lake not enabled in {self.region}: {e}")
245
+ else:
246
+ logger.error(f"Error getting data lake sources in {self.region}: {e}")
247
+ return []
@@ -0,0 +1,33 @@
1
+ """Shield service checks."""
2
+
3
+ from sraverify.services.shield.checks.sra_shield_01 import SRA_SHIELD_01
4
+ from sraverify.services.shield.checks.sra_shield_02 import SRA_SHIELD_02
5
+ from sraverify.services.shield.checks.sra_shield_03 import SRA_SHIELD_03
6
+ from sraverify.services.shield.checks.sra_shield_04 import SRA_SHIELD_04
7
+ from sraverify.services.shield.checks.sra_shield_05 import SRA_SHIELD_05
8
+ from sraverify.services.shield.checks.sra_shield_06 import SRA_SHIELD_06
9
+ from sraverify.services.shield.checks.sra_shield_07 import SRA_SHIELD_07
10
+ from sraverify.services.shield.checks.sra_shield_08 import SRA_SHIELD_08
11
+ from sraverify.services.shield.checks.sra_shield_09 import SRA_SHIELD_09
12
+ from sraverify.services.shield.checks.sra_shield_10 import SRA_SHIELD_10
13
+ from sraverify.services.shield.checks.sra_shield_11 import SRA_SHIELD_11
14
+ from sraverify.services.shield.checks.sra_shield_12 import SRA_SHIELD_12
15
+ from sraverify.services.shield.checks.sra_shield_13 import SRA_SHIELD_13
16
+ from sraverify.services.shield.checks.sra_shield_14 import SRA_SHIELD_14
17
+
18
+ CHECKS = {
19
+ "SRA-SHIELD-01": SRA_SHIELD_01,
20
+ "SRA-SHIELD-02": SRA_SHIELD_02,
21
+ "SRA-SHIELD-03": SRA_SHIELD_03,
22
+ "SRA-SHIELD-04": SRA_SHIELD_04,
23
+ "SRA-SHIELD-05": SRA_SHIELD_05,
24
+ "SRA-SHIELD-06": SRA_SHIELD_06,
25
+ "SRA-SHIELD-07": SRA_SHIELD_07,
26
+ "SRA-SHIELD-08": SRA_SHIELD_08,
27
+ "SRA-SHIELD-09": SRA_SHIELD_09,
28
+ "SRA-SHIELD-10": SRA_SHIELD_10,
29
+ "SRA-SHIELD-11": SRA_SHIELD_11,
30
+ "SRA-SHIELD-12": SRA_SHIELD_12,
31
+ "SRA-SHIELD-13": SRA_SHIELD_13,
32
+ "SRA-SHIELD-14": SRA_SHIELD_14,
33
+ }