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,161 @@
1
+ from typing import Dict, List, Any
2
+ from sraverify.checks import SecurityCheck
3
+ from botocore.exceptions import ClientError
4
+ import logging
5
+
6
+ class SRACT6(SecurityCheck):
7
+ """SRA-CT-6: Organization Trail Global Service Events Configuration"""
8
+
9
+ def __init__(self, check_type="organization"):
10
+ """Initialize the check with organization type"""
11
+ super().__init__(check_type=check_type)
12
+ self.check_id = "SRA-CT-6"
13
+ self.check_name = "Organization Trail Global Service Events Configuration"
14
+ self.description = ('This check verifies that your organization trail is configured to log global service events. '
15
+ 'Global service events are activities that occur in global services like IAM, STS, and CloudFront. '
16
+ 'Logging these events is crucial for maintaining a complete audit trail of activities in your AWS environment.')
17
+ self.service = "CloudTrail"
18
+ self.severity = "HIGH"
19
+ self.check_type = check_type
20
+ self.check_logic = ('1. Verify execution from Organization Management account | '
21
+ '2. List CloudTrail trails in current region and identify organization trails (IsOrganizationTrail=true) | '
22
+ '3. For each organization trail, verify IncludeGlobalServiceEvents=true | '
23
+ '4. Check passes if at least one organization trail is properly configured for global service events')
24
+ self.logger = logging.getLogger(self.__class__.__name__)
25
+ self.findings = []
26
+
27
+ def get_findings(self) -> List[Dict[str, Any]]:
28
+ """Return the findings"""
29
+ return self.findings
30
+
31
+ def run(self, session) -> None:
32
+ """Run the security check"""
33
+ try:
34
+ # Get account information
35
+ sts_client = session.client('sts')
36
+ account_id = sts_client.get_caller_identity()['Account']
37
+ region = session.region_name
38
+ self.logger.debug(f"Running check for account: {account_id} in region: {region}")
39
+
40
+ # Initialize CloudTrail client
41
+ cloudtrail_client = session.client('cloudtrail')
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
+ try:
67
+ # List trails and find organization trails
68
+ trails = cloudtrail_client.describe_trails(includeShadowTrails=True)
69
+ org_trails = [t for t in trails['trailList'] if t.get('IsOrganizationTrail')]
70
+ self.logger.debug(f"Found {len(org_trails)} organization trails")
71
+
72
+ if not org_trails:
73
+ self.findings.append({
74
+ "CheckId": self.check_id,
75
+ "Status": "FAIL",
76
+ "Region": region,
77
+ "Severity": self.severity,
78
+ "Title": f"{self.check_id} {self.check_name}",
79
+ "Description": self.description,
80
+ "ResourceId": "organization-trail",
81
+ "ResourceType": "AWS::CloudTrail::Trail",
82
+ "AccountId": account_id,
83
+ "CheckedValue": "Organization Trail Configuration",
84
+ "ActualValue": "No organization trail found",
85
+ "Remediation": "Create an organization trail with global service events enabled",
86
+ "Service": self.service,
87
+ "CheckLogic": self.check_logic,
88
+ "CheckType": self.check_type
89
+ })
90
+ return
91
+
92
+ # Check for global service events configuration
93
+ global_events_trail = None
94
+ for trail in org_trails:
95
+ if trail.get('IncludeGlobalServiceEvents'):
96
+ global_events_trail = trail
97
+ self.logger.debug(f"Found trail with global service events enabled: {trail['Name']}")
98
+ break
99
+
100
+ # Create finding based on global service events configuration
101
+ status = "PASS" if global_events_trail else "FAIL"
102
+ actual_value = (f"Organization trail {global_events_trail['Name'] if global_events_trail else org_trails[0]['Name']} "
103
+ f"{'has' if global_events_trail else 'does not have'} global service events enabled")
104
+
105
+ self.findings.append({
106
+ "CheckId": self.check_id,
107
+ "Status": status,
108
+ "Region": region,
109
+ "Severity": self.severity,
110
+ "Title": f"{self.check_id} {self.check_name}",
111
+ "Description": self.description,
112
+ "ResourceId": (global_events_trail or org_trails[0])['TrailARN'],
113
+ "ResourceType": "AWS::CloudTrail::Trail",
114
+ "AccountId": account_id,
115
+ "CheckedValue": "Global Service Events Configuration",
116
+ "ActualValue": actual_value,
117
+ "Remediation": "None required" if global_events_trail else "Enable global service events for the organization trail",
118
+ "Service": self.service,
119
+ "CheckLogic": self.check_logic,
120
+ "CheckType": self.check_type
121
+ })
122
+
123
+ except ClientError as e:
124
+ self.logger.error(f"Error accessing CloudTrail: {str(e)}")
125
+ self.findings.append({
126
+ "CheckId": self.check_id,
127
+ "Status": "ERROR",
128
+ "Region": region,
129
+ "Severity": self.severity,
130
+ "Title": f"{self.check_id} {self.check_name}",
131
+ "Description": self.description,
132
+ "ResourceId": "cloudtrail",
133
+ "ResourceType": "AWS::CloudTrail::Trail",
134
+ "AccountId": account_id,
135
+ "CheckedValue": "CloudTrail API Access",
136
+ "ActualValue": f"Error accessing CloudTrail: {str(e)}",
137
+ "Remediation": "Verify CloudTrail permissions",
138
+ "Service": self.service,
139
+ "CheckLogic": self.check_logic,
140
+ "CheckType": self.check_type
141
+ })
142
+
143
+ except Exception as e:
144
+ self.logger.error(f"Unexpected error in check: {str(e)}")
145
+ self.findings.append({
146
+ "CheckId": self.check_id,
147
+ "Status": "ERROR",
148
+ "Region": region if 'region' in locals() else session.region_name,
149
+ "Severity": self.severity,
150
+ "Title": f"{self.check_id} {self.check_name}",
151
+ "Description": self.description,
152
+ "ResourceId": "check-execution",
153
+ "ResourceType": "AWS::CloudTrail::Trail",
154
+ "AccountId": account_id if 'account_id' in locals() else "unknown",
155
+ "CheckedValue": "Check Execution",
156
+ "ActualValue": f"Unexpected error: {str(e)}",
157
+ "Remediation": "Contact support team",
158
+ "Service": self.service,
159
+ "CheckLogic": self.check_logic,
160
+ "CheckType": self.check_type
161
+ })
@@ -0,0 +1,194 @@
1
+ from typing import Dict, List, Any, Optional
2
+ from sraverify.checks import SecurityCheck
3
+ from botocore.exceptions import ClientError
4
+ import logging
5
+
6
+ class SRACT7(SecurityCheck):
7
+ """SRA-CT-7: Organization trail is actively publishing events"""
8
+
9
+ def __init__(self, check_type="account"):
10
+ """Initialize the check with account type"""
11
+ super().__init__(check_type=check_type)
12
+ self.check_id = "SRA-CT-7"
13
+ self.check_name = "Organization trail is actively publishing events from accounts"
14
+ self.description = ('This check verifies that your organization trail is running and actively '
15
+ 'logging events. If a trail is modified to stop logging, accidentally or by '
16
+ 'malicious user, you will not have visibility into API activity.')
17
+ self.service = "CloudTrail"
18
+ self.severity = "HIGH"
19
+ self.check_type = check_type
20
+ self.check_logic = ('1. List CloudTrail trails in current region | '
21
+ '2. Check for organization trail with IsOrganizationTrail=true | '
22
+ '3. Verify trail is actively logging')
23
+ self.logger = logging.getLogger(self.__class__.__name__)
24
+ self.findings = []
25
+ self._regions = None
26
+
27
+ def initialize(self, regions: Optional[List[str]] = None):
28
+ """Initialize check with optional regions"""
29
+ self._regions = regions
30
+
31
+ def get_findings(self) -> List[Dict[str, Any]]:
32
+ """Return the findings"""
33
+ return self.findings
34
+
35
+ def check_trail_status(self, cloudtrail_client, trail_name: str) -> bool:
36
+ """Check if trail is actively logging"""
37
+ try:
38
+ status = cloudtrail_client.get_trail_status(Name=trail_name)
39
+ return status.get('IsLogging', False)
40
+ except ClientError as e:
41
+ self.logger.error(f"Error getting trail status for {trail_name}: {str(e)}")
42
+ return False
43
+
44
+ def check_region(self, session, region: str, account_id: str):
45
+ """Check CloudTrail configuration in a specific region"""
46
+ try:
47
+ # Initialize CloudTrail client for this region
48
+ cloudtrail_client = session.client('cloudtrail', region_name=region)
49
+
50
+ try:
51
+ # List trails and find organization trails
52
+ trails = cloudtrail_client.describe_trails(includeShadowTrails=True)
53
+ org_trails = [t for t in trails['trailList'] if t.get('IsOrganizationTrail')]
54
+ self.logger.debug(f"Found {len(org_trails)} organization trails in {region}")
55
+
56
+ if not org_trails:
57
+ self.findings.append({
58
+ "CheckId": self.check_id,
59
+ "Status": "FAIL",
60
+ "Region": region,
61
+ "Severity": self.severity,
62
+ "Title": f"{self.check_id} {self.check_name}",
63
+ "Description": self.description,
64
+ "ResourceId": "organization-trail",
65
+ "ResourceType": "AWS::CloudTrail::Trail",
66
+ "AccountId": account_id,
67
+ "CheckedValue": "Organization Trail Configuration",
68
+ "ActualValue": "No organization trail found",
69
+ "Remediation": "Create an organization trail",
70
+ "Service": self.service,
71
+ "CheckLogic": self.check_logic,
72
+ "CheckType": self.check_type
73
+ })
74
+ return
75
+
76
+ # Check each organization trail for logging status
77
+ active_trail = None
78
+ for trail in org_trails:
79
+ trail_name = trail['Name']
80
+ if self.check_trail_status(cloudtrail_client, trail_name):
81
+ active_trail = trail
82
+ self.logger.debug(f"Found active trail: {trail_name} in {region}")
83
+ break
84
+
85
+ if active_trail:
86
+ self.findings.append({
87
+ "CheckId": self.check_id,
88
+ "Status": "PASS",
89
+ "Region": region,
90
+ "Severity": self.severity,
91
+ "Title": f"{self.check_id} {self.check_name}",
92
+ "Description": self.description,
93
+ "ResourceId": active_trail['TrailARN'],
94
+ "ResourceType": "AWS::CloudTrail::Trail",
95
+ "AccountId": account_id,
96
+ "CheckedValue": "Trail Logging Status",
97
+ "ActualValue": f"Organization trail {active_trail['Name']} is actively logging",
98
+ "Remediation": "None required",
99
+ "Service": self.service,
100
+ "CheckLogic": self.check_logic,
101
+ "CheckType": self.check_type
102
+ })
103
+ else:
104
+ trail = org_trails[0]
105
+ self.findings.append({
106
+ "CheckId": self.check_id,
107
+ "Status": "FAIL",
108
+ "Region": region,
109
+ "Severity": self.severity,
110
+ "Title": f"{self.check_id} {self.check_name}",
111
+ "Description": self.description,
112
+ "ResourceId": trail['TrailARN'],
113
+ "ResourceType": "AWS::CloudTrail::Trail",
114
+ "AccountId": account_id,
115
+ "CheckedValue": "Trail Logging Status",
116
+ "ActualValue": f"Organization trail {trail['Name']} is not actively logging",
117
+ "Remediation": "Start logging for the organization trail",
118
+ "Service": self.service,
119
+ "CheckLogic": self.check_logic,
120
+ "CheckType": self.check_type
121
+ })
122
+
123
+ except ClientError as e:
124
+ self.findings.append({
125
+ "CheckId": self.check_id,
126
+ "Status": "ERROR",
127
+ "Region": region,
128
+ "Severity": self.severity,
129
+ "Title": f"{self.check_id} {self.check_name}",
130
+ "Description": self.description,
131
+ "ResourceId": "cloudtrail",
132
+ "ResourceType": "AWS::CloudTrail::Trail",
133
+ "AccountId": account_id,
134
+ "CheckedValue": "CloudTrail API Access",
135
+ "ActualValue": f"Error accessing CloudTrail: {str(e)}",
136
+ "Remediation": "Verify CloudTrail permissions",
137
+ "Service": self.service,
138
+ "CheckLogic": self.check_logic,
139
+ "CheckType": self.check_type
140
+ })
141
+
142
+ except Exception as e:
143
+ self.findings.append({
144
+ "CheckId": self.check_id,
145
+ "Status": "ERROR",
146
+ "Region": region,
147
+ "Severity": self.severity,
148
+ "Title": f"{self.check_id} {self.check_name}",
149
+ "Description": self.description,
150
+ "ResourceId": "check-execution",
151
+ "ResourceType": "AWS::CloudTrail::Trail",
152
+ "AccountId": account_id,
153
+ "CheckedValue": "Check Execution",
154
+ "ActualValue": f"Error: {str(e)}",
155
+ "Remediation": "Check logs for more details",
156
+ "Service": self.service,
157
+ "CheckLogic": self.check_logic,
158
+ "CheckType": self.check_type
159
+ })
160
+
161
+ def run(self, session):
162
+ """Run the security check"""
163
+ try:
164
+ # Get account information
165
+ sts_client = session.client('sts')
166
+ account_id = sts_client.get_caller_identity()['Account']
167
+
168
+ # Get regions to check
169
+ regions_to_check = self._regions if self._regions else [session.region_name]
170
+ self.logger.debug(f"Checking regions: {regions_to_check}")
171
+
172
+ # Check each region
173
+ for region in regions_to_check:
174
+ self.check_region(session, region, account_id)
175
+
176
+ except Exception as e:
177
+ self.logger.error(f"Unexpected error in check: {str(e)}")
178
+ self.findings.append({
179
+ "CheckId": self.check_id,
180
+ "Status": "ERROR",
181
+ "Region": session.region_name,
182
+ "Severity": self.severity,
183
+ "Title": f"{self.check_id} {self.check_name}",
184
+ "Description": self.description,
185
+ "ResourceId": "check-execution",
186
+ "ResourceType": "AWS::CloudTrail::Trail",
187
+ "AccountId": account_id if 'account_id' in locals() else "unknown",
188
+ "CheckedValue": "Check Execution",
189
+ "ActualValue": f"Unexpected error: {str(e)}",
190
+ "Remediation": "Contact support team",
191
+ "Service": self.service,
192
+ "CheckLogic": self.check_logic,
193
+ "CheckType": self.check_type
194
+ })
@@ -0,0 +1,226 @@
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 SRACT8(SecurityCheck):
8
+ """SRA-CT-8: Organization Trail S3 Delivery 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-8"
14
+ self.check_name = "Organization trail is publishing logs to destination S3 bucket"
15
+ self.description = ('This check verifies that last attempt to send CloudTrail logs to S3 bucket was successful. '
16
+ 'CloudTrail log files are an audit log of actions taken by an IAM identity or an AWS service. '
17
+ 'The integrity, completeness and availability of these logs is crucial for forensic and auditing purposes. '
18
+ 'By logging to a dedicated and centralized Amazon S3 bucket, you can enforce strict security controls, '
19
+ 'access, and segregation of duties.')
20
+ self.service = "CloudTrail"
21
+ self.severity = "HIGH"
22
+ self.check_type = check_type
23
+ self.check_logic = ('1. Verify execution from Organization Management Account | '
24
+ '2. List CloudTrail trails in current region | '
25
+ '3. Check for organization trail with IsOrganizationTrail=true | '
26
+ '4. Verify S3 bucket configuration and successful log delivery by checking: '
27
+ 'a) S3 bucket is configured, b) No delivery errors exist, '
28
+ 'c) Latest delivery was successful within 24 hours')
29
+ self.logger = logging.getLogger(self.__class__.__name__)
30
+ self.findings = []
31
+
32
+ def get_findings(self) -> List[Dict[str, Any]]:
33
+ """Return the findings"""
34
+ return self.findings
35
+
36
+ def check_trail_delivery_status(self, cloudtrail_client, trail_name: str) -> tuple:
37
+ """Check trail delivery status and return status details"""
38
+ try:
39
+ status = cloudtrail_client.get_trail_status(Name=trail_name)
40
+ latest_delivery_time = status.get('LatestDeliveryTime')
41
+ latest_delivery_error = status.get('LatestDeliveryError', '')
42
+ is_logging = status.get('IsLogging', False)
43
+
44
+ # Step 1: Verify we're in management account using org_mgmt_checker
45
+ is_management, error_message = self.org_checker.verify_org_management()
46
+ if not is_management:
47
+ finding = {
48
+ 'CheckId': self.check_id,
49
+ 'Status': 'ERROR',
50
+ 'Region': region,
51
+ "Severity": self.severity,
52
+ 'Title': f"{self.check_id} {self.check_name}",
53
+ 'Description': self.description,
54
+ 'ResourceId': account_id,
55
+ 'ResourceType': 'AWS::Organizations::Account',
56
+ 'AccountId': account_id,
57
+ 'CheckedValue': 'Management Account Access',
58
+ 'ActualValue': error_message if error_message else 'Not running from management account',
59
+ 'Remediation': 'Run this check from the Organization Management Account',
60
+ 'Service': self.service,
61
+ 'CheckLogic': self.check_logic,
62
+ 'CheckType': self.check_type
63
+ }
64
+ self.findings.append(finding)
65
+ return self.findings
66
+
67
+
68
+
69
+ # Check if delivery is recent (within 24 hours)
70
+ current_time = datetime.now(timezone.utc)
71
+ is_recent = False
72
+ if latest_delivery_time:
73
+ time_difference = current_time - latest_delivery_time
74
+ is_recent = time_difference.total_seconds() < 86400 # 24 hours
75
+
76
+ return is_logging, is_recent, latest_delivery_error
77
+
78
+ except ClientError as e:
79
+ self.logger.error(f"Error getting trail status for {trail_name}: {str(e)}")
80
+ return False, False, str(e)
81
+
82
+ def run(self, session) -> None:
83
+ """Run the security check"""
84
+ try:
85
+ # Get account information
86
+ sts_client = session.client('sts')
87
+ account_id = sts_client.get_caller_identity()['Account']
88
+ region = session.region_name
89
+ self.logger.debug(f"Running check for account: {account_id} in region: {region}")
90
+
91
+ # Initialize CloudTrail client
92
+ cloudtrail_client = session.client('cloudtrail')
93
+
94
+ try:
95
+ # List trails and find organization trails
96
+ trails = cloudtrail_client.describe_trails(includeShadowTrails=True)
97
+ org_trails = [t for t in trails['trailList'] if t.get('IsOrganizationTrail')]
98
+ self.logger.debug(f"Found {len(org_trails)} organization trails")
99
+
100
+ if not org_trails:
101
+ self.findings.append({
102
+ "CheckId": self.check_id,
103
+ "Status": "FAIL",
104
+ "Region": region,
105
+ "Severity": self.severity,
106
+ "Title": f"{self.check_id} {self.check_name}",
107
+ "Description": self.description,
108
+ "ResourceId": "organization-trail",
109
+ "ResourceType": "AWS::CloudTrail::Trail",
110
+ "AccountId": account_id,
111
+ "CheckedValue": "Organization Trail Configuration",
112
+ "ActualValue": "No organization trail found",
113
+ "Remediation": "Create an organization trail with S3 bucket configuration",
114
+ "Service": self.service,
115
+ "CheckLogic": self.check_logic,
116
+ "CheckType": self.check_type
117
+ })
118
+ return
119
+
120
+ # Check each organization trail for S3 delivery status
121
+ valid_trail = None
122
+ for trail in org_trails:
123
+ trail_name = trail['Name']
124
+ s3_bucket = trail.get('S3BucketName')
125
+
126
+ if not s3_bucket:
127
+ continue
128
+
129
+ is_logging, is_recent, delivery_error = self.check_trail_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 S3 delivery: {trail_name}")
134
+ break
135
+
136
+ # Create finding based on S3 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": "S3 Delivery Status",
149
+ "ActualValue": f"Organization trail {valid_trail['Name']} is successfully delivering logs 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 S3 delivery"
157
+ remediation = "Verify S3 bucket configuration and CloudTrail permissions"
158
+ if org_trails:
159
+ trail = org_trails[0]
160
+ if not trail.get('S3BucketName'):
161
+ actual_value = f"Trail {trail['Name']} has no S3 bucket configured"
162
+ remediation = "Configure S3 bucket for the organization trail"
163
+ elif delivery_error:
164
+ actual_value = f"Trail {trail['Name']} has delivery error: {delivery_error}"
165
+ remediation = "Resolve S3 delivery errors (check S3 bucket permissions)"
166
+ elif not is_recent:
167
+ actual_value = f"Trail {trail['Name']} has no recent log delivery"
168
+ remediation = "Verify trail logging is enabled and check CloudWatch logs for errors"
169
+
170
+ self.findings.append({
171
+ "CheckId": self.check_id,
172
+ "Status": "FAIL",
173
+ "Region": region,
174
+ "Severity": self.severity,
175
+ "Title": f"{self.check_id} {self.check_name}",
176
+ "Description": self.description,
177
+ "ResourceId": (valid_trail or org_trails[0])['TrailARN'],
178
+ "ResourceType": "AWS::CloudTrail::Trail",
179
+ "AccountId": account_id,
180
+ "CheckedValue": "S3 Delivery Status",
181
+ "ActualValue": actual_value,
182
+ "Remediation": remediation,
183
+ "Service": self.service,
184
+ "CheckLogic": self.check_logic,
185
+ "CheckType": self.check_type
186
+ })
187
+
188
+ except ClientError as e:
189
+ self.logger.error(f"Error accessing CloudTrail: {str(e)}")
190
+ self.findings.append({
191
+ "CheckId": self.check_id,
192
+ "Status": "ERROR",
193
+ "Region": region,
194
+ "Severity": self.severity,
195
+ "Title": f"{self.check_id} {self.check_name}",
196
+ "Description": self.description,
197
+ "ResourceId": "cloudtrail",
198
+ "ResourceType": "AWS::CloudTrail::Trail",
199
+ "AccountId": account_id,
200
+ "CheckedValue": "CloudTrail API Access",
201
+ "ActualValue": f"Error accessing CloudTrail: {str(e)}",
202
+ "Remediation": "Verify CloudTrail permissions",
203
+ "Service": self.service,
204
+ "CheckLogic": self.check_logic,
205
+ "CheckType": self.check_type
206
+ })
207
+
208
+ except Exception as e:
209
+ self.logger.error(f"Unexpected error in check: {str(e)}")
210
+ self.findings.append({
211
+ "CheckId": self.check_id,
212
+ "Status": "ERROR",
213
+ "Region": region if 'region' in locals() else session.region_name,
214
+ "Severity": self.severity,
215
+ "Title": f"{self.check_id} {self.check_name}",
216
+ "Description": self.description,
217
+ "ResourceId": "check-execution",
218
+ "ResourceType": "AWS::CloudTrail::Trail",
219
+ "AccountId": account_id if 'account_id' in locals() else "unknown",
220
+ "CheckedValue": "Check Execution",
221
+ "ActualValue": f"Unexpected error: {str(e)}",
222
+ "Remediation": "Contact support team",
223
+ "Service": self.service,
224
+ "CheckLogic": self.check_logic,
225
+ "CheckType": self.check_type
226
+ })