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,112 @@
1
+ """
2
+ Check if CloudWatch alarms exist for Shield Advanced protected CloudFront and Route53 resources.
3
+ """
4
+ from typing import Dict, List, Any
5
+ from sraverify.services.shield.base import ShieldCheck
6
+
7
+
8
+ class SRA_SHIELD_13(ShieldCheck):
9
+ """Check if CloudWatch alarms exist for Shield Advanced protected CloudFront and Route53 resources."""
10
+
11
+ def __init__(self):
12
+ """Initialize Shield Advanced CloudWatch alarms check."""
13
+ super().__init__()
14
+ self.check_id = "SRA-SHIELD-13"
15
+ self.check_name = "CloudWatch alarms exist for Shield Advanced protected CloudFront and Route53 resources"
16
+ self.description = ("This check verifies that CloudWatch alarms are configured for "
17
+ "Shield Advanced protected CloudFront distributions and Route53 hosted zones "
18
+ "to monitor DDoS detection metrics (DDoSDetected).")
19
+ self.severity = "MEDIUM"
20
+ self.check_logic = ("List Shield protections for CloudFront and Route53 resources, "
21
+ "then check if CloudWatch alarms exist for DDoSDetected metric.")
22
+
23
+ def execute(self) -> List[Dict[str, Any]]:
24
+ """
25
+ Execute the check.
26
+
27
+ Returns:
28
+ List of findings
29
+ """
30
+ # Shield metrics for CloudFront and Route53 are reported in us-east-1
31
+ region = "us-east-1"
32
+ protections = self.list_protections(region)
33
+
34
+ if "Error" in protections:
35
+ error_code = protections["Error"].get("Code", "")
36
+ if error_code == "ResourceNotFoundException":
37
+ self.findings.append(self.create_finding(
38
+ status="FAIL",
39
+ region=region,
40
+ resource_id=None,
41
+ actual_value="Shield Advanced subscription not found",
42
+ remediation="Enable Shield Advanced subscription to protect resources"
43
+ ))
44
+ else:
45
+ self.findings.append(self.create_finding(
46
+ status="ERROR",
47
+ region=region,
48
+ resource_id=None,
49
+ actual_value=protections["Error"].get("Message", "Unknown error"),
50
+ remediation="Check IAM permissions for Shield API access"
51
+ ))
52
+ elif protections.get("Protections"):
53
+ # Filter for CloudFront and Route53 resources
54
+ cf_r53_protections = [
55
+ p for p in protections["Protections"]
56
+ if ("cloudfront" in p.get("ResourceArn", "").lower() or
57
+ "route53" in p.get("ResourceArn", "").lower())
58
+ ]
59
+
60
+ if not cf_r53_protections:
61
+ self.findings.append(self.create_finding(
62
+ status="PASS",
63
+ region=region,
64
+ resource_id="shield:cloudwatch-alarms",
65
+ actual_value="No CloudFront or Route53 protected resources found",
66
+ remediation=""
67
+ ))
68
+ return self.findings
69
+
70
+ # Check each resource for CloudWatch alarms
71
+ for protection in cf_r53_protections:
72
+ resource_arn = protection.get("ResourceArn", "")
73
+ protection_name = protection.get("Name", "Unknown")
74
+
75
+ # Check for DDoSDetected alarm
76
+ alarms = self.get_cloudwatch_alarms_for_resource(region, resource_arn)
77
+
78
+ if "Error" in alarms:
79
+ self.findings.append(self.create_finding(
80
+ status="ERROR",
81
+ region=region,
82
+ resource_id=resource_arn,
83
+ actual_value=alarms["Error"].get("Message", "Unknown error"),
84
+ remediation="Check IAM permissions for CloudWatch API access"
85
+ ))
86
+ elif alarms.get("DDoSDetectedAlarms"):
87
+ alarm_names = [alarm["AlarmName"] for alarm in alarms["DDoSDetectedAlarms"]]
88
+ self.findings.append(self.create_finding(
89
+ status="PASS",
90
+ region=region,
91
+ resource_id=resource_arn,
92
+ actual_value=f"DDoSDetected alarms configured: {', '.join(alarm_names)}",
93
+ remediation=""
94
+ ))
95
+ else:
96
+ self.findings.append(self.create_finding(
97
+ status="FAIL",
98
+ region=region,
99
+ resource_id=resource_arn,
100
+ actual_value="No DDoSDetected CloudWatch alarms configured",
101
+ remediation="Create CloudWatch alarm for DDoSDetected metric to monitor DDoS events"
102
+ ))
103
+ else:
104
+ self.findings.append(self.create_finding(
105
+ status="PASS",
106
+ region=region,
107
+ resource_id=None,
108
+ actual_value="No Shield Advanced protections found",
109
+ remediation=""
110
+ ))
111
+
112
+ return self.findings
@@ -0,0 +1,111 @@
1
+ """
2
+ Check if Shield Advanced protected resources have automatic application layer DDoS mitigation enabled.
3
+ """
4
+ from typing import Dict, List, Any
5
+ from sraverify.services.shield.base import ShieldCheck
6
+
7
+
8
+ class SRA_SHIELD_14(ShieldCheck):
9
+ """Check if Shield Advanced protected resources have automatic application layer DDoS mitigation enabled."""
10
+
11
+ def __init__(self):
12
+ """Initialize Shield Advanced automatic mitigation check."""
13
+ super().__init__()
14
+ self.check_id = "SRA-SHIELD-14"
15
+ self.check_name = "Shield Advanced protected resources have automatic application layer DDoS mitigation enabled"
16
+ self.description = ("This check verifies that Shield Advanced protected application layer resources "
17
+ "(CloudFront distributions and Application Load Balancers) have automatic "
18
+ "application layer DDoS mitigation enabled with Block action for effective protection.")
19
+ self.severity = "HIGH"
20
+ self.check_logic = ("List Shield protections and check ApplicationLayerAutomaticResponseConfiguration "
21
+ "status and action for application layer resources.")
22
+
23
+ def execute(self) -> List[Dict[str, Any]]:
24
+ """
25
+ Execute the check.
26
+
27
+ Returns:
28
+ List of findings
29
+ """
30
+ # Shield is a global service, check only in us-east-1
31
+ region = "us-east-1"
32
+ protections = self.list_protections(region)
33
+
34
+ if "Error" in protections:
35
+ error_code = protections["Error"].get("Code", "")
36
+ if error_code == "ResourceNotFoundException":
37
+ self.findings.append(self.create_finding(
38
+ status="FAIL",
39
+ region=region,
40
+ resource_id=None,
41
+ actual_value="Shield Advanced subscription not found",
42
+ remediation="Enable Shield Advanced subscription to protect resources"
43
+ ))
44
+ else:
45
+ self.findings.append(self.create_finding(
46
+ status="ERROR",
47
+ region=region,
48
+ resource_id=None,
49
+ actual_value=protections["Error"].get("Message", "Unknown error"),
50
+ remediation="Check IAM permissions for Shield API access"
51
+ ))
52
+ elif protections.get("Protections"):
53
+ # Filter for application layer resources (CloudFront and ALB)
54
+ app_layer_protections = [
55
+ p for p in protections["Protections"]
56
+ if ("cloudfront" in p.get("ResourceArn", "").lower() or
57
+ "elasticloadbalancing" in p.get("ResourceArn", "").lower())
58
+ ]
59
+
60
+ if not app_layer_protections:
61
+ self.findings.append(self.create_finding(
62
+ status="PASS",
63
+ region=region,
64
+ resource_id="shield:automatic-mitigation",
65
+ actual_value="No application layer protected resources found",
66
+ remediation=""
67
+ ))
68
+ return self.findings
69
+
70
+ # Check each application layer resource for automatic mitigation
71
+ for protection in app_layer_protections:
72
+ resource_arn = protection.get("ResourceArn", "")
73
+
74
+ # Check if ApplicationLayerAutomaticResponseConfiguration exists in the protection
75
+ auto_response_config = protection.get("ApplicationLayerAutomaticResponseConfiguration")
76
+
77
+ if auto_response_config and auto_response_config.get("Status") == "ENABLED":
78
+ if "Block" in auto_response_config.get("Action", {}):
79
+ self.findings.append(self.create_finding(
80
+ status="PASS",
81
+ region=region,
82
+ resource_id=resource_arn,
83
+ actual_value="Automatic mitigation enabled with Block action",
84
+ remediation=""
85
+ ))
86
+ else:
87
+ self.findings.append(self.create_finding(
88
+ status="FAIL",
89
+ region=region,
90
+ resource_id=resource_arn,
91
+ actual_value="Automatic mitigation enabled but using Count action",
92
+ remediation="Change automatic mitigation action from Count to Block for effective protection"
93
+ ))
94
+ else:
95
+ self.findings.append(self.create_finding(
96
+ status="FAIL",
97
+ region=region,
98
+ resource_id=resource_arn,
99
+ actual_value="Automatic application layer DDoS mitigation not enabled",
100
+ remediation="Enable automatic application layer DDoS mitigation with Block action in Shield Advanced console"
101
+ ))
102
+ else:
103
+ self.findings.append(self.create_finding(
104
+ status="PASS",
105
+ region=region,
106
+ resource_id=None,
107
+ actual_value="No Shield Advanced protections found",
108
+ remediation=""
109
+ ))
110
+
111
+ return self.findings
@@ -0,0 +1,214 @@
1
+ """
2
+ Shield client for interacting with AWS Shield service.
3
+ """
4
+ from typing import Dict, Optional, Any
5
+ import boto3
6
+ from botocore.exceptions import ClientError
7
+ from sraverify.core.logging import logger
8
+
9
+
10
+ class ShieldClient:
11
+ """Client for interacting with AWS Shield service."""
12
+
13
+ def __init__(self, region: str, session: Optional[boto3.Session] = None):
14
+ """
15
+ Initialize Shield client for a specific region.
16
+
17
+ Args:
18
+ region: AWS region name
19
+ session: AWS session to use (if None, a new session will be created)
20
+ """
21
+ self.region = region
22
+ self.session = session or boto3.Session()
23
+ self.client = self.session.client('shield', region_name=region)
24
+
25
+ def get_subscription_state(self) -> Dict[str, Any]:
26
+ """
27
+ Get Shield Advanced subscription state.
28
+
29
+ Returns:
30
+ Dictionary containing subscription details or error information
31
+ """
32
+ try:
33
+ return self.client.describe_subscription()
34
+ except ClientError as e:
35
+ error_code = e.response.get('Error', {}).get('Code', '')
36
+ error_message = str(e)
37
+ logger.debug(f"Error getting Shield subscription in {self.region}: {error_message}")
38
+ return {
39
+ "Error": {
40
+ "Code": error_code,
41
+ "Message": error_message
42
+ }
43
+ }
44
+
45
+ def get_subscription_status(self) -> Dict[str, Any]:
46
+ """
47
+ Get Shield Advanced subscription status (ACTIVE/INACTIVE).
48
+
49
+ Returns:
50
+ Dictionary containing subscription state or error information
51
+ """
52
+ try:
53
+ return self.client.get_subscription_state()
54
+ except ClientError as e:
55
+ error_code = e.response.get('Error', {}).get('Code', '')
56
+ error_message = str(e)
57
+ logger.debug(f"Error getting Shield subscription state in {self.region}: {error_message}")
58
+ return {
59
+ "Error": {
60
+ "Code": error_code,
61
+ "Message": error_message
62
+ }
63
+ }
64
+
65
+ def list_protections(self, resource_type: Optional[str] = None) -> Dict[str, Any]:
66
+ """
67
+ List Shield Advanced protections.
68
+
69
+ Args:
70
+ resource_type: Optional resource type filter
71
+
72
+ Returns:
73
+ Dictionary containing protections list or error information
74
+ """
75
+ try:
76
+ params = {}
77
+ if resource_type:
78
+ params['InclusionFilters'] = {'ResourceTypes': [resource_type]}
79
+
80
+ return self.client.list_protections(**params)
81
+ except ClientError as e:
82
+ error_code = e.response.get('Error', {}).get('Code', '')
83
+ error_message = str(e)
84
+ logger.debug(f"Error listing Shield protections in {self.region}: {error_message}")
85
+ return {
86
+ "Error": {
87
+ "Code": error_code,
88
+ "Message": error_message
89
+ }
90
+ }
91
+
92
+ def describe_drt_access(self) -> Dict[str, Any]:
93
+ """
94
+ Describe Shield Response Team (SRT) access configuration.
95
+
96
+ Returns:
97
+ Dictionary containing DRT access details or error information
98
+ """
99
+ try:
100
+ return self.client.describe_drt_access()
101
+ except ClientError as e:
102
+ error_code = e.response.get('Error', {}).get('Code', '')
103
+ error_message = str(e)
104
+ logger.debug(f"Error describing DRT access in {self.region}: {error_message}")
105
+ return {
106
+ "Error": {
107
+ "Code": error_code,
108
+ "Message": error_message
109
+ }
110
+ }
111
+
112
+ def get_lambda_function(self, function_name: str) -> Dict[str, Any]:
113
+ """
114
+ Get Lambda function details.
115
+
116
+ Args:
117
+ function_name: Name of the Lambda function
118
+
119
+ Returns:
120
+ Dictionary containing function details or error information
121
+ """
122
+ try:
123
+ lambda_client = self.session.client('lambda', region_name=self.region)
124
+ return lambda_client.get_function(FunctionName=function_name)
125
+ except ClientError as e:
126
+ error_code = e.response.get('Error', {}).get('Code', '')
127
+ error_message = str(e)
128
+ logger.debug(f"Error getting Lambda function {function_name} in {self.region}: {error_message}")
129
+ return {
130
+ "Error": {
131
+ "Code": error_code,
132
+ "Message": error_message
133
+ }
134
+ }
135
+
136
+ def get_web_acl_for_resource(self, resource_arn: str) -> Dict[str, Any]:
137
+ """
138
+ Get WAF web ACL associated with a resource.
139
+
140
+ Args:
141
+ resource_arn: ARN of the resource
142
+
143
+ Returns:
144
+ Dictionary containing web ACL details or error information
145
+ """
146
+ try:
147
+ # For CloudFront distributions, use CloudFront API
148
+ if "cloudfront" in resource_arn.lower():
149
+ # Extract distribution ID from ARN: arn:aws:cloudfront::account:distribution/ID
150
+ distribution_id = resource_arn.split("/")[-1]
151
+ cloudfront_client = self.session.client('cloudfront', region_name='us-east-1')
152
+ response = cloudfront_client.get_distribution_config(Id=distribution_id)
153
+ web_acl_id = response.get('DistributionConfig', {}).get('WebACLId', '')
154
+
155
+ if web_acl_id:
156
+ return {"WebACL": {"Id": web_acl_id, "Name": f"WebACL-{web_acl_id}"}}
157
+ else:
158
+ return {"Error": {"Code": "WAFNonexistentItemException", "Message": "No web ACL associated"}}
159
+ else:
160
+ # For other resources, use WAFv2 API
161
+ wafv2_client = self.session.client('wafv2', region_name=self.region)
162
+ return wafv2_client.get_web_acl_for_resource(ResourceArn=resource_arn)
163
+ except ClientError as e:
164
+ error_code = e.response.get('Error', {}).get('Code', '')
165
+ error_message = str(e)
166
+ logger.debug(f"Error getting web ACL for resource {resource_arn} in {self.region}: {error_message}")
167
+ return {
168
+ "Error": {
169
+ "Code": error_code,
170
+ "Message": error_message
171
+ }
172
+ }
173
+
174
+ def get_cloudwatch_alarms_for_resource(self, resource_arn: str) -> Dict[str, Any]:
175
+ """
176
+ Get CloudWatch alarms for Shield Advanced DDoS metrics for a resource.
177
+
178
+ Args:
179
+ resource_arn: ARN of the resource
180
+
181
+ Returns:
182
+ Dictionary containing alarm details or error information
183
+ """
184
+ try:
185
+ cloudwatch_client = self.session.client('cloudwatch', region_name=self.region)
186
+
187
+ # Look for alarms on DDoSDetected metric for this resource
188
+ response = cloudwatch_client.describe_alarms_for_metric(
189
+ MetricName='DDoSDetected',
190
+ Namespace='AWS/DDoSProtection',
191
+ Dimensions=[
192
+ {
193
+ 'Name': 'ResourceArn',
194
+ 'Value': resource_arn
195
+ }
196
+ ]
197
+ )
198
+
199
+ ddos_alarms = response.get('MetricAlarms', [])
200
+
201
+ return {
202
+ "DDoSDetectedAlarms": ddos_alarms
203
+ }
204
+
205
+ except ClientError as e:
206
+ error_code = e.response.get('Error', {}).get('Code', '')
207
+ error_message = str(e)
208
+ logger.debug(f"Error getting CloudWatch alarms for resource {resource_arn} in {self.region}: {error_message}")
209
+ return {
210
+ "Error": {
211
+ "Code": error_code,
212
+ "Message": error_message
213
+ }
214
+ }
@@ -0,0 +1,21 @@
1
+ from sraverify.services.waf.checks.sra_waf_01 import SRA_WAF_01
2
+ from sraverify.services.waf.checks.sra_waf_02 import SRA_WAF_02
3
+ from sraverify.services.waf.checks.sra_waf_03 import SRA_WAF_03
4
+ from sraverify.services.waf.checks.sra_waf_04 import SRA_WAF_04
5
+ from sraverify.services.waf.checks.sra_waf_05 import SRA_WAF_05
6
+ from sraverify.services.waf.checks.sra_waf_06 import SRA_WAF_06
7
+ from sraverify.services.waf.checks.sra_waf_07 import SRA_WAF_07
8
+ from sraverify.services.waf.checks.sra_waf_08 import SRA_WAF_08
9
+ from sraverify.services.waf.checks.sra_waf_09 import SRA_WAF_09
10
+
11
+ CHECKS = {
12
+ "SRA-WAF-01": SRA_WAF_01,
13
+ "SRA-WAF-02": SRA_WAF_02,
14
+ "SRA-WAF-03": SRA_WAF_03,
15
+ "SRA-WAF-04": SRA_WAF_04,
16
+ "SRA-WAF-05": SRA_WAF_05,
17
+ "SRA-WAF-06": SRA_WAF_06,
18
+ "SRA-WAF-07": SRA_WAF_07,
19
+ "SRA-WAF-08": SRA_WAF_08,
20
+ "SRA-WAF-09": SRA_WAF_09,
21
+ }
@@ -0,0 +1,100 @@
1
+ from typing import Dict, Any
2
+ from sraverify.core.check import SecurityCheck
3
+ from sraverify.services.waf.client import WAFClient
4
+
5
+ class WAFCheck(SecurityCheck):
6
+ def __init__(self):
7
+ super().__init__(
8
+ account_type="application",
9
+ service="WAF",
10
+ resource_type="AWS::ElasticLoadBalancingV2::LoadBalancer"
11
+ )
12
+ self._distributions_cache = {}
13
+ self._load_balancers_cache = {}
14
+ self._rest_apis_cache = {}
15
+ self._graphql_apis_cache = {}
16
+ self._user_pools_cache = {}
17
+ self._apprunner_services_cache = {}
18
+ self._verified_access_instances_cache = {}
19
+ self._amplify_apps_cache = {}
20
+ self._web_acls_cache = {}
21
+
22
+ def _setup_clients(self):
23
+ self._clients.clear()
24
+ # WAF for CloudFront is global, use us-east-1
25
+ self._clients['us-east-1'] = WAFClient('us-east-1', session=self.session)
26
+ # For ALB, API Gateway, AppSync, Cognito, App Runner, Verified Access, Amplify, and Web ACLs, create clients for all regions
27
+ if hasattr(self, 'regions') and self.regions:
28
+ for region in self.regions:
29
+ if region not in self._clients:
30
+ self._clients[region] = WAFClient(region, session=self.session)
31
+
32
+ def get_distributions(self) -> Dict[str, Any]:
33
+ if not self._distributions_cache:
34
+ client = self.get_client('us-east-1')
35
+ if client:
36
+ self._distributions_cache = client.list_distributions()
37
+ return self._distributions_cache
38
+
39
+ def get_load_balancers(self, region: str) -> Dict[str, Any]:
40
+ if region not in self._load_balancers_cache:
41
+ client = self.get_client(region)
42
+ if client:
43
+ self._load_balancers_cache[region] = client.describe_load_balancers()
44
+ return self._load_balancers_cache.get(region, {})
45
+
46
+ def get_rest_apis(self, region: str) -> Dict[str, Any]:
47
+ if region not in self._rest_apis_cache:
48
+ client = self.get_client(region)
49
+ if client:
50
+ self._rest_apis_cache[region] = client.get_rest_apis()
51
+ return self._rest_apis_cache.get(region, {})
52
+
53
+ def get_stages(self, region: str, rest_api_id: str) -> Dict[str, Any]:
54
+ client = self.get_client(region)
55
+ if client:
56
+ return client.get_stages(rest_api_id)
57
+ return {"Error": {"Message": "No client available"}}
58
+
59
+ def get_graphql_apis(self, region: str) -> Dict[str, Any]:
60
+ if region not in self._graphql_apis_cache:
61
+ client = self.get_client(region)
62
+ if client:
63
+ self._graphql_apis_cache[region] = client.list_graphql_apis()
64
+ return self._graphql_apis_cache.get(region, {})
65
+
66
+ def get_user_pools(self, region: str) -> Dict[str, Any]:
67
+ if region not in self._user_pools_cache:
68
+ client = self.get_client(region)
69
+ if client:
70
+ self._user_pools_cache[region] = client.list_user_pools()
71
+ return self._user_pools_cache.get(region, {})
72
+
73
+ def get_apprunner_services(self, region: str) -> Dict[str, Any]:
74
+ if region not in self._apprunner_services_cache:
75
+ client = self.get_client(region)
76
+ if client:
77
+ self._apprunner_services_cache[region] = client.list_services()
78
+ return self._apprunner_services_cache.get(region, {})
79
+
80
+ def get_verified_access_instances(self, region: str) -> Dict[str, Any]:
81
+ if region not in self._verified_access_instances_cache:
82
+ client = self.get_client(region)
83
+ if client:
84
+ self._verified_access_instances_cache[region] = client.describe_verified_access_instances()
85
+ return self._verified_access_instances_cache.get(region, {})
86
+
87
+ def get_amplify_apps(self, region: str) -> Dict[str, Any]:
88
+ if region not in self._amplify_apps_cache:
89
+ client = self.get_client(region)
90
+ if client:
91
+ self._amplify_apps_cache[region] = client.list_apps()
92
+ return self._amplify_apps_cache.get(region, {})
93
+
94
+ def get_web_acls(self, region: str, scope: str = "REGIONAL") -> Dict[str, Any]:
95
+ cache_key = f"{region}_{scope}"
96
+ if cache_key not in self._web_acls_cache:
97
+ client = self.get_client(region)
98
+ if client:
99
+ self._web_acls_cache[cache_key] = client.list_web_acls(scope)
100
+ return self._web_acls_cache.get(cache_key, {})
@@ -0,0 +1 @@
1
+ # WAF security checks
@@ -0,0 +1,63 @@
1
+ from typing import Dict, List, Any
2
+ from sraverify.services.waf.base import WAFCheck
3
+
4
+ class SRA_WAF_01(WAFCheck):
5
+ def __init__(self):
6
+ super().__init__()
7
+ self.resource_type = "AWS::CloudFront::Distribution"
8
+ self.check_id = "SRA-WAF-01"
9
+ self.check_name = "CloudFront distributions should be associated with AWS WAF"
10
+ self.description = "Ensures that all CloudFront distributions are protected by AWS WAF web ACLs to filter malicious traffic"
11
+ self.severity = "HIGH"
12
+ self.check_logic = "Lists all CloudFront distributions and verifies each has a WebACLId configured"
13
+
14
+ def execute(self) -> List[Dict[str, Any]]:
15
+ region = "us-east-1" # CloudFront is global service
16
+
17
+ distributions_response = self.get_distributions()
18
+
19
+ if "Error" in distributions_response:
20
+ self.findings.append(self.create_finding(
21
+ status="ERROR",
22
+ region=region,
23
+ resource_id=None,
24
+ actual_value=distributions_response["Error"].get("Message", "Unknown error"),
25
+ remediation="Check IAM permissions for CloudFront API access"
26
+ ))
27
+ return self.findings
28
+
29
+ distribution_list = distributions_response.get("DistributionList", {})
30
+ distributions = distribution_list.get("Items", [])
31
+
32
+ if not distributions:
33
+ self.findings.append(self.create_finding(
34
+ status="PASS",
35
+ region=region,
36
+ resource_id="No distributions",
37
+ actual_value="No CloudFront distributions found",
38
+ remediation="No action needed"
39
+ ))
40
+ return self.findings
41
+
42
+ for distribution in distributions:
43
+ distribution_id = distribution.get("Id")
44
+ web_acl_id = distribution.get("WebACLId")
45
+
46
+ if web_acl_id:
47
+ self.findings.append(self.create_finding(
48
+ status="PASS",
49
+ region=region,
50
+ resource_id=distribution_id,
51
+ actual_value=f"WAF Web ACL associated: {web_acl_id}",
52
+ remediation="No action needed"
53
+ ))
54
+ else:
55
+ self.findings.append(self.create_finding(
56
+ status="FAIL",
57
+ region=region,
58
+ resource_id=distribution_id,
59
+ actual_value="No WAF Web ACL associated",
60
+ remediation="Associate a WAF Web ACL with this CloudFront distribution using the AWS Console, CLI, or API"
61
+ ))
62
+
63
+ return self.findings