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,220 @@
1
+ from typing import Dict, List, Any
2
+ from sraverify.checks import SecurityCheck
3
+ from botocore.exceptions import ClientError
4
+
5
+ class SRACT1(SecurityCheck):
6
+ """SRA-CT-1: Organization CloudTrail Configuration"""
7
+
8
+ def __init__(self, check_type="organization"):
9
+ super().__init__(check_type=check_type)
10
+ self.check_id = "SRA-CT-1"
11
+ self.check_name = "Organization CloudTrail Configuration"
12
+ self.severity = "HIGH"
13
+ self.description = ('This check verifies that an organization trail is configured for your AWS Organization. '
14
+ 'It is important to have uniform logging strategy for your AWS environment. Organization '
15
+ 'trail logs all events for all AWS accounts in that organization and delivers logs to a '
16
+ 'single S3 bucket, CloudWatch Logs and Event Bridge. Organization trails are automatically '
17
+ 'applied to all member accounts in the organization. Member accounts can see the '
18
+ 'organization trail, but can\'t modify or delete it. Organization trail should be '
19
+ 'configured for all AWS regions even if you are not operating out of any region.')
20
+ self.check_logic = ('1. Verify execution from Organization Management account | ' # Step 1: Check account permissions
21
+ '2. List CloudTrail trails in current region | ' # Step 2: Get all trails
22
+ '3. Check for organization trail with IsOrganizationTrail=true and IsMultiRegionTrail=true | ' # Step 3: Verify organization trail exists
23
+ '4. Verify trail configuration (S3 bucket, CloudWatch Logs, EventBridge) | ' # Step 4: Check trail settings
24
+ '5. Check passes if organization trail is properly configured') # Step 5: All checks must pass
25
+ self.service = 'CloudTrail'
26
+
27
+ def get_findings(self):
28
+ """Return the findings"""
29
+ return self.findings
30
+
31
+ def run(self, session):
32
+ """Run the security check"""
33
+ try:
34
+ region = session.region_name
35
+ account_id = session.client('sts').get_caller_identity()['Account']
36
+
37
+ # Initialize clients
38
+ org_client = session.client('organizations')
39
+ cloudtrail_client = session.client('cloudtrail')
40
+
41
+ # Step 1: Verify we're in management account using org_mgmt_checker
42
+ is_management, error_message = self.org_checker.verify_org_management()
43
+ if not is_management:
44
+ finding = {
45
+ 'CheckId': self.check_id,
46
+ 'Status': 'ERROR',
47
+ 'Region': region,
48
+ "Severity": self.severity,
49
+ 'Title': f"{self.check_id} {self.check_name}",
50
+ 'Description': self.description,
51
+ 'ResourceId': account_id,
52
+ 'ResourceType': 'AWS::Organizations::Account',
53
+ 'AccountId': account_id,
54
+ 'CheckedValue': 'Management Account Access',
55
+ 'ActualValue': error_message if error_message else 'Not running from management account',
56
+ 'Remediation': 'Run this check from the Organization Management Account',
57
+ 'Service': self.service,
58
+ 'CheckLogic': self.check_logic,
59
+ 'CheckType': self.check_type
60
+ }
61
+ self.findings.append(finding)
62
+ return self.findings
63
+
64
+ # Steps 2 & 3: Check for organization trail
65
+ try:
66
+ trails = cloudtrail_client.list_trails()['Trails']
67
+ org_trail = None
68
+ non_org_trails = []
69
+
70
+ for trail in trails:
71
+ trail_info = cloudtrail_client.get_trail(Name=trail['Name'])['Trail']
72
+ if (trail_info.get('IsOrganizationTrail', False) and
73
+ trail_info.get('IsMultiRegionTrail', False)):
74
+ org_trail = trail_info
75
+ break
76
+ else:
77
+ non_org_trails.append(trail['Name'])
78
+
79
+ if not org_trail:
80
+ failure_details = []
81
+ if not trails:
82
+ failure_details.append("No trails found")
83
+ if non_org_trails:
84
+ failure_details.append(f"Found non-organization trails: {', '.join(non_org_trails)}")
85
+
86
+ self.findings.append({
87
+ 'CheckId': self.check_id,
88
+ 'Status': 'FAIL',
89
+ 'Region': region,
90
+ "Severity": self.severity,
91
+ 'Title': f"{self.check_id} {self.check_name}",
92
+ 'Description': self.description,
93
+ 'ResourceId': account_id,
94
+ 'ResourceType': 'AWS::CloudTrail::Trail',
95
+ 'AccountId': account_id,
96
+ 'CheckedValue': 'Organization Trail Configuration',
97
+ 'ActualValue': ' | '.join(failure_details) if failure_details else 'No organization-wide trail found',
98
+ 'Remediation': 'Create an organization trail that logs all regions',
99
+ 'Service': 'CloudTrail',
100
+ 'CheckLogic': self.check_logic,
101
+ 'CheckType': self.check_type
102
+ })
103
+ return self.findings
104
+
105
+ # Step 4: Verify trail configuration
106
+ try:
107
+ trail_status = cloudtrail_client.get_trail_status(Name=org_trail['Name'])
108
+
109
+ config_issues = []
110
+
111
+ if not org_trail.get('CloudWatchLogsLogGroupArn'):
112
+ config_issues.append("CloudWatch Logs integration not configured")
113
+
114
+ if not org_trail.get('S3BucketName'):
115
+ config_issues.append("S3 bucket not configured")
116
+
117
+ if not trail_status.get('IsLogging', False):
118
+ config_issues.append("Trail logging is not enabled")
119
+
120
+ if config_issues:
121
+ self.findings.append({
122
+ 'CheckId': self.check_id,
123
+ 'Status': 'FAIL',
124
+ 'Region': region,
125
+ "Severity": self.severity,
126
+ 'Title': f"{self.check_id} {self.check_name}",
127
+ 'Description': self.description,
128
+ 'ResourceId': org_trail['TrailARN'],
129
+ 'ResourceType': 'AWS::CloudTrail::Trail',
130
+ 'AccountId': account_id,
131
+ 'CheckedValue': 'Trail Configuration Details',
132
+ 'ActualValue': f"Configuration issues found: {' | '.join(config_issues)}",
133
+ 'Remediation': 'Configure CloudWatch Logs, S3 bucket, and enable logging for the organization trail',
134
+ 'Service': 'CloudTrail',
135
+ 'CheckLogic': self.check_logic,
136
+ 'CheckType': self.check_type
137
+ })
138
+ return self.findings
139
+
140
+ # All checks passed
141
+ self.findings.append({
142
+ 'CheckId': self.check_id,
143
+ 'Status': 'PASS',
144
+ 'Region': region,
145
+ "Severity": self.severity,
146
+ 'Title': f"{self.check_id} {self.check_name}",
147
+ 'Description': self.description,
148
+ 'ResourceId': org_trail['TrailARN'],
149
+ 'ResourceType': 'AWS::CloudTrail::Trail',
150
+ 'AccountId': account_id,
151
+ 'CheckedValue': 'Organization Trail Configuration',
152
+ 'ActualValue': (f"Organization trail: {org_trail['Name']} | "
153
+ f"Multi-region: {org_trail['IsMultiRegionTrail']} | "
154
+ f"Logging Enabled: {trail_status['IsLogging']} | "
155
+ f"S3 Bucket: {org_trail['S3BucketName']} | "
156
+ f"CloudWatch Logs Configured: {'Yes' if org_trail.get('CloudWatchLogsLogGroupArn') else 'No'}"),
157
+ 'Remediation': 'None required',
158
+ 'Service': 'CloudTrail',
159
+ 'CheckLogic': self.check_logic,
160
+ 'CheckType': self.check_type
161
+ })
162
+
163
+ except ClientError as e:
164
+ self.findings.append({
165
+ 'CheckId': self.check_id,
166
+ 'Status': 'ERROR',
167
+ 'Region': region,
168
+ "Severity": self.severity,
169
+ 'Title': f"{self.check_id} {self.check_name}",
170
+ 'Description': self.description,
171
+ 'ResourceId': org_trail['TrailARN'],
172
+ 'ResourceType': 'AWS::CloudTrail::Trail',
173
+ 'AccountId': account_id,
174
+ 'CheckedValue': 'Trail Status',
175
+ 'ActualValue': f'Error checking trail status: {str(e)} | Error Code: {e.response["Error"]["Code"]}',
176
+ 'Remediation': 'Verify CloudTrail permissions and trail configuration',
177
+ 'Service': 'CloudTrail',
178
+ 'CheckLogic': self.check_logic,
179
+ 'CheckType': self.check_type
180
+ })
181
+
182
+ except ClientError as e:
183
+ self.findings.append({
184
+ 'CheckId': self.check_id,
185
+ 'Status': 'ERROR',
186
+ 'Region': region,
187
+ "Severity": self.severity,
188
+ 'Title': f"{self.check_id} {self.check_name}",
189
+ 'Description': self.description,
190
+ 'ResourceId': account_id,
191
+ 'ResourceType': 'AWS::CloudTrail::Trail',
192
+ 'AccountId': account_id,
193
+ 'CheckedValue': 'CloudTrail Access',
194
+ 'ActualValue': f'Error accessing CloudTrail: {str(e)} | Error Code: {e.response["Error"]["Code"]}',
195
+ 'Remediation': 'Verify CloudTrail permissions and service availability',
196
+ 'Service': 'CloudTrail',
197
+ 'CheckLogic': self.check_logic,
198
+ 'CheckType': self.check_type
199
+ })
200
+
201
+ except Exception as e:
202
+ self.findings.append({
203
+ 'CheckId': self.check_id,
204
+ 'Status': 'ERROR',
205
+ 'Region': region,
206
+ "Severity": self.severity,
207
+ 'Title': f"{self.check_id} {self.check_name}",
208
+ 'Description': self.description,
209
+ 'ResourceId': account_id,
210
+ 'ResourceType': 'AWS::CloudTrail::Trail',
211
+ 'AccountId': account_id,
212
+ 'CheckedValue': 'Check Execution',
213
+ 'ActualValue': f'Unexpected error during check execution: {str(e)}',
214
+ 'Remediation': 'Review error logs and verify AWS credentials and permissions',
215
+ 'Service': 'CloudTrail',
216
+ 'CheckLogic': self.check_logic,
217
+ 'CheckType': self.check_type
218
+ })
219
+
220
+ return self.findings
@@ -0,0 +1,229 @@
1
+ from typing import Dict, List, Any
2
+ from sraverify.checks import SecurityCheck
3
+ from botocore.exceptions import ClientError
4
+ import logging
5
+ from datetime import datetime, timezone
6
+
7
+ class SRACT10(SecurityCheck):
8
+ """SRA-CT-10: Organization Trail Log File Validation Status"""
9
+
10
+ def __init__(self, check_type="organization"):
11
+ """Initialize the check with organization type"""
12
+ super().__init__(check_type=check_type)
13
+ self.check_id = "SRA-CT-10"
14
+ self.check_name = "Organization trail is configured to deliver Log file validation digest files to destination bucket"
15
+ self.description = ('This check verifies that log file validation digest files are being successfully delivered to a S3 bucket. '
16
+ 'Log file validation helps ensure the integrity and authenticity of CloudTrail logs by creating digitally '
17
+ 'signed digest files containing hashes of log files.')
18
+ self.service = "CloudTrail"
19
+ self.severity = "HIGH"
20
+ self.check_type = check_type
21
+ self.check_logic = ('1. Verify execution from Organization Management Account | '
22
+ '2. List CloudTrail trails in current region | '
23
+ '3. Check for organization trail with IsOrganizationTrail=true | '
24
+ '4. Verify log file validation configuration and successful delivery by checking: '
25
+ 'a) EnableLogFileValidation is true, b) S3 bucket is configured, '
26
+ 'c) No digest delivery errors exist, d) Latest digest delivery was successful within 24 hours')
27
+ self.logger = logging.getLogger(self.__class__.__name__)
28
+ self.findings = []
29
+
30
+ def get_findings(self) -> List[Dict[str, Any]]:
31
+ """Return the findings"""
32
+ return self.findings
33
+
34
+ def check_digest_delivery_status(self, cloudtrail_client, trail_name: str) -> tuple:
35
+ """Check digest file delivery status and return status details"""
36
+ try:
37
+ status = cloudtrail_client.get_trail_status(Name=trail_name)
38
+ latest_delivery_time = status.get('LatestDigestDeliveryTime')
39
+ latest_delivery_error = status.get('LatestDigestDeliveryError', '')
40
+ is_logging = status.get('IsLogging', False)
41
+
42
+
43
+ # Step 1: Verify we're in management account using org_mgmt_checker
44
+ is_management, error_message = self.org_checker.verify_org_management()
45
+ if not is_management:
46
+ finding = {
47
+ 'CheckId': self.check_id,
48
+ 'Status': 'ERROR',
49
+ 'Region': region,
50
+ "Severity": self.severity,
51
+ 'Title': f"{self.check_id} {self.check_name}",
52
+ 'Description': self.description,
53
+ 'ResourceId': account_id,
54
+ 'ResourceType': 'AWS::Organizations::Account',
55
+ 'AccountId': account_id,
56
+ 'CheckedValue': 'Management Account Access',
57
+ 'ActualValue': error_message if error_message else 'Not running from management account',
58
+ 'Remediation': 'Run this check from the Organization Management Account',
59
+ 'Service': self.service,
60
+ 'CheckLogic': self.check_logic,
61
+ 'CheckType': self.check_type
62
+ }
63
+ self.findings.append(finding)
64
+ return self.findings
65
+
66
+
67
+ # Check if delivery is recent (within 24 hours)
68
+ current_time = datetime.now(timezone.utc)
69
+ is_recent = False
70
+ if latest_delivery_time:
71
+ time_difference = current_time - latest_delivery_time
72
+ is_recent = time_difference.total_seconds() < 86400 # 24 hours
73
+
74
+ return is_logging, is_recent, latest_delivery_error
75
+
76
+ except ClientError as e:
77
+ self.logger.error(f"Error getting trail status for {trail_name}: {str(e)}")
78
+ return False, False, str(e)
79
+
80
+ def run(self, session) -> None:
81
+ """Run the security check"""
82
+ try:
83
+ # Get account information
84
+ sts_client = session.client('sts')
85
+ account_id = sts_client.get_caller_identity()['Account']
86
+ region = session.region_name
87
+ self.logger.debug(f"Running check for account: {account_id} in region: {region}")
88
+
89
+ # Initialize CloudTrail client
90
+ cloudtrail_client = session.client('cloudtrail')
91
+
92
+ try:
93
+ # List trails and find organization trails
94
+ trails = cloudtrail_client.describe_trails(includeShadowTrails=True)
95
+ org_trails = [t for t in trails['trailList'] if t.get('IsOrganizationTrail')]
96
+ self.logger.debug(f"Found {len(org_trails)} organization trails")
97
+
98
+ if not org_trails:
99
+ self.findings.append({
100
+ "CheckId": self.check_id,
101
+ "Status": "FAIL",
102
+ "Region": region,
103
+ "Severity": self.severity,
104
+ "Title": f"{self.check_id} {self.check_name}",
105
+ "Description": self.description,
106
+ "ResourceId": "organization-trail",
107
+ "ResourceType": "AWS::CloudTrail::Trail",
108
+ "AccountId": account_id,
109
+ "CheckedValue": "Organization Trail Configuration",
110
+ "ActualValue": "No organization trail found",
111
+ "Remediation": "Create an organization trail with log file validation enabled",
112
+ "Service": self.service,
113
+ "CheckLogic": self.check_logic,
114
+ "CheckType": self.check_type
115
+ })
116
+ return
117
+
118
+ # Check each organization trail for log file validation and digest delivery
119
+ valid_trail = None
120
+ for trail in org_trails:
121
+ trail_name = trail['Name']
122
+ log_validation_enabled = trail.get('LogFileValidationEnabled', False)
123
+ s3_bucket = trail.get('S3BucketName')
124
+
125
+ # Skip if log validation is not enabled or S3 bucket is not configured
126
+ if not (log_validation_enabled and s3_bucket):
127
+ continue
128
+
129
+ is_logging, is_recent, delivery_error = self.check_digest_delivery_status(cloudtrail_client, trail_name)
130
+
131
+ if is_logging and is_recent and not delivery_error:
132
+ valid_trail = trail
133
+ self.logger.debug(f"Found valid trail with successful digest delivery: {trail_name}")
134
+ break
135
+
136
+ # Create finding based on log validation and digest delivery status
137
+ if valid_trail:
138
+ self.findings.append({
139
+ "CheckId": self.check_id,
140
+ "Status": "PASS",
141
+ "Region": region,
142
+ "Severity": self.severity,
143
+ "Title": f"{self.check_id} {self.check_name}",
144
+ "Description": self.description,
145
+ "ResourceId": valid_trail['TrailARN'],
146
+ "ResourceType": "AWS::CloudTrail::Trail",
147
+ "AccountId": account_id,
148
+ "CheckedValue": "Log File Validation Status",
149
+ "ActualValue": f"Organization trail {valid_trail['Name']} is successfully delivering digest files to S3 bucket {valid_trail['S3BucketName']}",
150
+ "Remediation": "None required",
151
+ "Service": self.service,
152
+ "CheckLogic": self.check_logic,
153
+ "CheckType": self.check_type
154
+ })
155
+ else:
156
+ actual_value = "No organization trail found with successful recent digest file delivery"
157
+ remediation = "Verify log file validation configuration and S3 permissions"
158
+ if org_trails:
159
+ trail = org_trails[0]
160
+ if not trail.get('LogFileValidationEnabled'):
161
+ actual_value = f"Trail {trail['Name']} does not have log file validation enabled"
162
+ remediation = "Enable log file validation for the organization trail"
163
+ elif not trail.get('S3BucketName'):
164
+ actual_value = f"Trail {trail['Name']} has no S3 bucket configured"
165
+ remediation = "Configure S3 bucket for the organization trail"
166
+ elif delivery_error:
167
+ actual_value = f"Trail {trail['Name']} has digest delivery error: {delivery_error}"
168
+ remediation = "Resolve digest delivery errors (check S3 bucket permissions)"
169
+ elif not is_recent:
170
+ actual_value = f"Trail {trail['Name']} has no recent digest file delivery"
171
+ remediation = "Verify trail logging is enabled and check CloudWatch logs for errors"
172
+
173
+ self.findings.append({
174
+ "CheckId": self.check_id,
175
+ "Status": "FAIL",
176
+ "Region": region,
177
+ "Severity": self.severity,
178
+ "Title": f"{self.check_id} {self.check_name}",
179
+ "Description": self.description,
180
+ "ResourceId": (valid_trail or org_trails[0])['TrailARN'],
181
+ "ResourceType": "AWS::CloudTrail::Trail",
182
+ "AccountId": account_id,
183
+ "CheckedValue": "Log File Validation Status",
184
+ "ActualValue": actual_value,
185
+ "Remediation": remediation,
186
+ "Service": self.service,
187
+ "CheckLogic": self.check_logic,
188
+ "CheckType": self.check_type
189
+ })
190
+
191
+ except ClientError as e:
192
+ self.logger.error(f"Error accessing CloudTrail: {str(e)}")
193
+ self.findings.append({
194
+ "CheckId": self.check_id,
195
+ "Status": "ERROR",
196
+ "Region": region,
197
+ "Severity": self.severity,
198
+ "Title": f"{self.check_id} {self.check_name}",
199
+ "Description": self.description,
200
+ "ResourceId": "cloudtrail",
201
+ "ResourceType": "AWS::CloudTrail::Trail",
202
+ "AccountId": account_id,
203
+ "CheckedValue": "CloudTrail API Access",
204
+ "ActualValue": f"Error accessing CloudTrail: {str(e)}",
205
+ "Remediation": "Verify CloudTrail permissions",
206
+ "Service": self.service,
207
+ "CheckLogic": self.check_logic,
208
+ "CheckType": self.check_type
209
+ })
210
+
211
+ except Exception as e:
212
+ self.logger.error(f"Unexpected error in check: {str(e)}")
213
+ self.findings.append({
214
+ "CheckId": self.check_id,
215
+ "Status": "ERROR",
216
+ "Region": region if 'region' in locals() else session.region_name,
217
+ "Severity": self.severity,
218
+ "Title": f"{self.check_id} {self.check_name}",
219
+ "Description": self.description,
220
+ "ResourceId": "check-execution",
221
+ "ResourceType": "AWS::CloudTrail::Trail",
222
+ "AccountId": account_id if 'account_id' in locals() else "unknown",
223
+ "CheckedValue": "Check Execution",
224
+ "ActualValue": f"Unexpected error: {str(e)}",
225
+ "Remediation": "Contact support team",
226
+ "Service": self.service,
227
+ "CheckLogic": self.check_logic,
228
+ "CheckType": self.check_type
229
+ })