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.
- sraverify/__init__.py +36 -0
- sraverify/checks/__init__.py +56 -0
- sraverify/checks/accessanalyzer/SRA_IAA_1.py +188 -0
- sraverify/checks/accessanalyzer/SRA_IAA_2.py +162 -0
- sraverify/checks/accessanalyzer/SRA_IAA_3.py +260 -0
- sraverify/checks/accessanalyzer/SRA_IAA_4.py +207 -0
- sraverify/checks/accessanalyzer/__init__.py +3 -0
- sraverify/checks/cloudtrail/SRA-CT-1.py +220 -0
- sraverify/checks/cloudtrail/SRA-CT-10.py +229 -0
- sraverify/checks/cloudtrail/SRA-CT-11.py +242 -0
- sraverify/checks/cloudtrail/SRA-CT-12.py +163 -0
- sraverify/checks/cloudtrail/SRA-CT-13.py +279 -0
- sraverify/checks/cloudtrail/SRA-CT-2.py +218 -0
- sraverify/checks/cloudtrail/SRA-CT-3.py +196 -0
- sraverify/checks/cloudtrail/SRA-CT-4.py +161 -0
- sraverify/checks/cloudtrail/SRA-CT-5.py +200 -0
- sraverify/checks/cloudtrail/SRA-CT-6.py +161 -0
- sraverify/checks/cloudtrail/SRA-CT-7.py +194 -0
- sraverify/checks/cloudtrail/SRA-CT-8.py +226 -0
- sraverify/checks/cloudtrail/SRA-CT-9.py +226 -0
- sraverify/checks/cloudtrail/__init__.py +3 -0
- sraverify/checks/config/SRA-CONFIG-1.py +197 -0
- sraverify/checks/config/__init__.py +3 -0
- sraverify/core/__init__.py +3 -0
- sraverify/core/check.py +227 -0
- sraverify/core/logging.py +37 -0
- sraverify/core/session.py +47 -0
- sraverify/lib/__init__.py +4 -0
- sraverify/lib/audit_info.py +37 -0
- sraverify/lib/banner.py +42 -0
- sraverify/lib/check_loader.py +80 -0
- sraverify/lib/org_mgmt_checker.py +86 -0
- sraverify/lib/outputs.py +46 -0
- sraverify/lib/progress.py +75 -0
- sraverify/lib/regions.py +27 -0
- sraverify/lib/session.py +23 -0
- sraverify/main.py +350 -0
- sraverify/services/__init__.py +3 -0
- sraverify/services/accessanalyzer/__init__.py +15 -0
- sraverify/services/accessanalyzer/base.py +123 -0
- sraverify/services/accessanalyzer/checks/__init__.py +3 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_01.py +82 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_02.py +82 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_03.py +103 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_04.py +139 -0
- sraverify/services/accessanalyzer/client.py +123 -0
- sraverify/services/account/__init__.py +9 -0
- sraverify/services/account/base.py +56 -0
- sraverify/services/account/checks/__init__.py +1 -0
- sraverify/services/account/checks/sra_account_01.py +65 -0
- sraverify/services/account/checks/sra_account_02.py +63 -0
- sraverify/services/account/checks/sra_account_03.py +63 -0
- sraverify/services/account/client.py +51 -0
- sraverify/services/auditmanager/__init__.py +10 -0
- sraverify/services/auditmanager/base.py +72 -0
- sraverify/services/auditmanager/checks/__init__.py +1 -0
- sraverify/services/auditmanager/checks/sra_auditmanager_01.py +58 -0
- sraverify/services/auditmanager/checks/sra_auditmanager_02.py +80 -0
- sraverify/services/auditmanager/client.py +58 -0
- sraverify/services/cloudtrail/__init__.py +33 -0
- sraverify/services/cloudtrail/base.py +167 -0
- sraverify/services/cloudtrail/checks/__init__.py +1 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_01.py +83 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_02.py +99 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_03.py +94 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_04.py +92 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_05.py +106 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_06.py +93 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_07.py +96 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_08.py +145 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_09.py +167 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_10.py +162 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_11.py +178 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_12.py +77 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_13.py +120 -0
- sraverify/services/cloudtrail/client.py +118 -0
- sraverify/services/config/__init__.py +25 -0
- sraverify/services/config/base.py +249 -0
- sraverify/services/config/checks/__init__.py +1 -0
- sraverify/services/config/checks/sra_config_01.py +123 -0
- sraverify/services/config/checks/sra_config_02.py +156 -0
- sraverify/services/config/checks/sra_config_03.py +149 -0
- sraverify/services/config/checks/sra_config_04.py +104 -0
- sraverify/services/config/checks/sra_config_05.py +104 -0
- sraverify/services/config/checks/sra_config_06.py +194 -0
- sraverify/services/config/checks/sra_config_07.py +162 -0
- sraverify/services/config/checks/sra_config_08.py +185 -0
- sraverify/services/config/checks/sra_config_09.py +177 -0
- sraverify/services/config/client.py +264 -0
- sraverify/services/ec2/__init__.py +8 -0
- sraverify/services/ec2/base.py +75 -0
- sraverify/services/ec2/checks/__init__.py +1 -0
- sraverify/services/ec2/checks/sra_ec2_01.py +83 -0
- sraverify/services/ec2/client.py +63 -0
- sraverify/services/firewallmanager/__init__.py +23 -0
- sraverify/services/firewallmanager/base.py +48 -0
- sraverify/services/firewallmanager/checks/__init__.py +1 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_01.py +75 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_02.py +57 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_03.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_04.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_05.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_06.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_07.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_08.py +61 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_09.py +61 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_10.py +71 -0
- sraverify/services/firewallmanager/client.py +40 -0
- sraverify/services/guardduty/__init__.py +58 -0
- sraverify/services/guardduty/base.py +207 -0
- sraverify/services/guardduty/checks/__init__.py +3 -0
- sraverify/services/guardduty/checks/sra_guardduty_01.py +51 -0
- sraverify/services/guardduty/checks/sra_guardduty_02.py +80 -0
- sraverify/services/guardduty/checks/sra_guardduty_03.py +77 -0
- sraverify/services/guardduty/checks/sra_guardduty_04.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_05.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_06.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_07.py +85 -0
- sraverify/services/guardduty/checks/sra_guardduty_08.py +83 -0
- sraverify/services/guardduty/checks/sra_guardduty_09.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_10.py +83 -0
- sraverify/services/guardduty/checks/sra_guardduty_11.py +93 -0
- sraverify/services/guardduty/checks/sra_guardduty_12.py +83 -0
- sraverify/services/guardduty/checks/sra_guardduty_13.py +90 -0
- sraverify/services/guardduty/checks/sra_guardduty_14.py +136 -0
- sraverify/services/guardduty/checks/sra_guardduty_15.py +94 -0
- sraverify/services/guardduty/checks/sra_guardduty_16.py +94 -0
- sraverify/services/guardduty/checks/sra_guardduty_17.py +91 -0
- sraverify/services/guardduty/checks/sra_guardduty_18.py +91 -0
- sraverify/services/guardduty/checks/sra_guardduty_19.py +91 -0
- sraverify/services/guardduty/checks/sra_guardduty_20.py +111 -0
- sraverify/services/guardduty/checks/sra_guardduty_21.py +112 -0
- sraverify/services/guardduty/checks/sra_guardduty_22.py +111 -0
- sraverify/services/guardduty/checks/sra_guardduty_23.py +154 -0
- sraverify/services/guardduty/checks/sra_guardduty_24.py +111 -0
- sraverify/services/guardduty/checks/sra_guardduty_25.py +111 -0
- sraverify/services/guardduty/client.py +107 -0
- sraverify/services/inspector/__init__.py +29 -0
- sraverify/services/inspector/base.py +233 -0
- sraverify/services/inspector/checks/__init__.py +3 -0
- sraverify/services/inspector/checks/sra_inspector_01.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_02.py +68 -0
- sraverify/services/inspector/checks/sra_inspector_03.py +68 -0
- sraverify/services/inspector/checks/sra_inspector_04.py +70 -0
- sraverify/services/inspector/checks/sra_inspector_05.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_06.py +115 -0
- sraverify/services/inspector/checks/sra_inspector_07.py +109 -0
- sraverify/services/inspector/checks/sra_inspector_08.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_09.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_10.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_11.py +69 -0
- sraverify/services/inspector/client.py +99 -0
- sraverify/services/macie/__init__.py +27 -0
- sraverify/services/macie/base.py +271 -0
- sraverify/services/macie/checks/__init__.py +1 -0
- sraverify/services/macie/checks/sra_macie_01.py +100 -0
- sraverify/services/macie/checks/sra_macie_02.py +102 -0
- sraverify/services/macie/checks/sra_macie_03.py +152 -0
- sraverify/services/macie/checks/sra_macie_04.py +120 -0
- sraverify/services/macie/checks/sra_macie_05.py +85 -0
- sraverify/services/macie/checks/sra_macie_06.py +124 -0
- sraverify/services/macie/checks/sra_macie_07.py +138 -0
- sraverify/services/macie/checks/sra_macie_08.py +82 -0
- sraverify/services/macie/checks/sra_macie_09.py +103 -0
- sraverify/services/macie/checks/sra_macie_10.py +81 -0
- sraverify/services/macie/client.py +220 -0
- sraverify/services/s3/__init__.py +16 -0
- sraverify/services/s3/base.py +69 -0
- sraverify/services/s3/checks/__init__.py +1 -0
- sraverify/services/s3/checks/sra_s3_01.py +89 -0
- sraverify/services/s3/checks/sra_s3_02.py +89 -0
- sraverify/services/s3/checks/sra_s3_03.py +88 -0
- sraverify/services/s3/checks/sra_s3_04.py +88 -0
- sraverify/services/s3/client.py +52 -0
- sraverify/services/securityhub/__init__.py +27 -0
- sraverify/services/securityhub/base.py +349 -0
- sraverify/services/securityhub/checks/__init__.py +1 -0
- sraverify/services/securityhub/checks/sra_securityhub_01.py +115 -0
- sraverify/services/securityhub/checks/sra_securityhub_02.py +114 -0
- sraverify/services/securityhub/checks/sra_securityhub_03.py +136 -0
- sraverify/services/securityhub/checks/sra_securityhub_04.py +75 -0
- sraverify/services/securityhub/checks/sra_securityhub_05.py +102 -0
- sraverify/services/securityhub/checks/sra_securityhub_06.py +113 -0
- sraverify/services/securityhub/checks/sra_securityhub_07.py +121 -0
- sraverify/services/securityhub/checks/sra_securityhub_08.py +113 -0
- sraverify/services/securityhub/checks/sra_securityhub_09.py +100 -0
- sraverify/services/securityhub/checks/sra_securityhub_10.py +94 -0
- sraverify/services/securityhub/checks/sra_securityhub_11.py +73 -0
- sraverify/services/securityhub/client.py +249 -0
- sraverify/services/securityincidentresponse/__init__.py +13 -0
- sraverify/services/securityincidentresponse/base.py +95 -0
- sraverify/services/securityincidentresponse/checks/__init__.py +1 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_01.py +77 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_02.py +72 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_03.py +86 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_04.py +117 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_05.py +55 -0
- sraverify/services/securityincidentresponse/client.py +71 -0
- sraverify/services/securitylake/__init__.py +39 -0
- sraverify/services/securitylake/base.py +461 -0
- sraverify/services/securitylake/checks/__init__.py +1 -0
- sraverify/services/securitylake/checks/sra_securitylake_01.py +98 -0
- sraverify/services/securitylake/checks/sra_securitylake_02.py +133 -0
- sraverify/services/securitylake/checks/sra_securitylake_03.py +116 -0
- sraverify/services/securitylake/checks/sra_securitylake_04.py +72 -0
- sraverify/services/securitylake/checks/sra_securitylake_05.py +116 -0
- sraverify/services/securitylake/checks/sra_securitylake_06.py +104 -0
- sraverify/services/securitylake/checks/sra_securitylake_07.py +108 -0
- sraverify/services/securitylake/checks/sra_securitylake_08.py +107 -0
- sraverify/services/securitylake/checks/sra_securitylake_09.py +107 -0
- sraverify/services/securitylake/checks/sra_securitylake_10.py +106 -0
- sraverify/services/securitylake/checks/sra_securitylake_11.py +109 -0
- sraverify/services/securitylake/checks/sra_securitylake_12.py +108 -0
- sraverify/services/securitylake/checks/sra_securitylake_13.py +108 -0
- sraverify/services/securitylake/checks/sra_securitylake_14.py +72 -0
- sraverify/services/securitylake/checks/sra_securitylake_15.py +120 -0
- sraverify/services/securitylake/checks/sra_securitylake_16.py +104 -0
- sraverify/services/securitylake/checks/sra_securitylake_17.py +103 -0
- sraverify/services/securitylake/client.py +247 -0
- sraverify/services/shield/__init__.py +33 -0
- sraverify/services/shield/base.py +199 -0
- sraverify/services/shield/checks/__init__.py +1 -0
- sraverify/services/shield/checks/sra_shield_01.py +68 -0
- sraverify/services/shield/checks/sra_shield_02.py +77 -0
- sraverify/services/shield/checks/sra_shield_03.py +84 -0
- sraverify/services/shield/checks/sra_shield_04.py +84 -0
- sraverify/services/shield/checks/sra_shield_05.py +84 -0
- sraverify/services/shield/checks/sra_shield_06.py +84 -0
- sraverify/services/shield/checks/sra_shield_07.py +84 -0
- sraverify/services/shield/checks/sra_shield_08.py +69 -0
- sraverify/services/shield/checks/sra_shield_09.py +86 -0
- sraverify/services/shield/checks/sra_shield_10.py +100 -0
- sraverify/services/shield/checks/sra_shield_11.py +71 -0
- sraverify/services/shield/checks/sra_shield_12.py +130 -0
- sraverify/services/shield/checks/sra_shield_13.py +112 -0
- sraverify/services/shield/checks/sra_shield_14.py +111 -0
- sraverify/services/shield/client.py +214 -0
- sraverify/services/waf/__init__.py +21 -0
- sraverify/services/waf/base.py +100 -0
- sraverify/services/waf/checks/__init__.py +1 -0
- sraverify/services/waf/checks/sra_waf_01.py +63 -0
- sraverify/services/waf/checks/sra_waf_02.py +82 -0
- sraverify/services/waf/checks/sra_waf_03.py +123 -0
- sraverify/services/waf/checks/sra_waf_04.py +94 -0
- sraverify/services/waf/checks/sra_waf_05.py +94 -0
- sraverify/services/waf/checks/sra_waf_06.py +91 -0
- sraverify/services/waf/checks/sra_waf_07.py +94 -0
- sraverify/services/waf/checks/sra_waf_08.py +66 -0
- sraverify/services/waf/checks/sra_waf_09.py +95 -0
- sraverify/services/waf/client.py +109 -0
- sraverify/utils/__init__.py +3 -0
- sraverify/utils/banner.py +65 -0
- sraverify/utils/outputs.py +57 -0
- sraverify/utils/progress.py +97 -0
- sraverify-0.1.0.dist-info/LICENSE +175 -0
- sraverify-0.1.0.dist-info/METADATA +516 -0
- sraverify-0.1.0.dist-info/NOTICE +1 -0
- sraverify-0.1.0.dist-info/RECORD +261 -0
- sraverify-0.1.0.dist-info/WHEEL +5 -0
- sraverify-0.1.0.dist-info/entry_points.txt +2 -0
- sraverify-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from typing import Dict, List, Any
|
|
2
|
+
from sraverify.services.waf.base import WAFCheck
|
|
3
|
+
|
|
4
|
+
class SRA_WAF_02(WAFCheck):
|
|
5
|
+
def __init__(self):
|
|
6
|
+
super().__init__()
|
|
7
|
+
self.check_id = "SRA-WAF-02"
|
|
8
|
+
self.check_name = "Application Load Balancers should be associated with AWS WAF"
|
|
9
|
+
self.description = "Ensures that all Application Load Balancers are protected by AWS WAF web ACLs to filter malicious traffic"
|
|
10
|
+
self.severity = "HIGH"
|
|
11
|
+
self.check_logic = "Lists all Application Load Balancers and verifies each has a WAF web ACL associated"
|
|
12
|
+
|
|
13
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
14
|
+
for region in self.regions:
|
|
15
|
+
load_balancers_response = self.get_load_balancers(region)
|
|
16
|
+
|
|
17
|
+
if "Error" in load_balancers_response:
|
|
18
|
+
self.findings.append(self.create_finding(
|
|
19
|
+
status="ERROR",
|
|
20
|
+
region=region,
|
|
21
|
+
resource_id=None,
|
|
22
|
+
actual_value=load_balancers_response["Error"].get("Message", "Unknown error"),
|
|
23
|
+
remediation="Check IAM permissions for ELB and WAF API access"
|
|
24
|
+
))
|
|
25
|
+
continue
|
|
26
|
+
|
|
27
|
+
load_balancers = load_balancers_response.get("LoadBalancers", [])
|
|
28
|
+
|
|
29
|
+
# Filter for Application Load Balancers only
|
|
30
|
+
albs = [lb for lb in load_balancers if lb.get("Type") == "application"]
|
|
31
|
+
|
|
32
|
+
if not albs:
|
|
33
|
+
self.findings.append(self.create_finding(
|
|
34
|
+
status="PASS",
|
|
35
|
+
region=region,
|
|
36
|
+
resource_id="No ALBs",
|
|
37
|
+
actual_value="No Application Load Balancers found",
|
|
38
|
+
remediation="No action needed"
|
|
39
|
+
))
|
|
40
|
+
continue
|
|
41
|
+
|
|
42
|
+
for alb in albs:
|
|
43
|
+
alb_arn = alb.get("LoadBalancerArn")
|
|
44
|
+
alb_name = alb.get("LoadBalancerName")
|
|
45
|
+
|
|
46
|
+
client = self.get_client(region)
|
|
47
|
+
if not client:
|
|
48
|
+
continue
|
|
49
|
+
|
|
50
|
+
web_acl_response = client.get_web_acl_for_resource(alb_arn)
|
|
51
|
+
|
|
52
|
+
if "Error" in web_acl_response:
|
|
53
|
+
self.findings.append(self.create_finding(
|
|
54
|
+
status="ERROR",
|
|
55
|
+
region=region,
|
|
56
|
+
resource_id=alb_name,
|
|
57
|
+
actual_value=web_acl_response["Error"].get("Message", "Unknown error"),
|
|
58
|
+
remediation="Check IAM permissions for WAF API access"
|
|
59
|
+
))
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
web_acl = web_acl_response.get("WebACL")
|
|
63
|
+
|
|
64
|
+
if web_acl:
|
|
65
|
+
web_acl_name = web_acl.get("Name", "Unknown")
|
|
66
|
+
self.findings.append(self.create_finding(
|
|
67
|
+
status="PASS",
|
|
68
|
+
region=region,
|
|
69
|
+
resource_id=alb_name,
|
|
70
|
+
actual_value=f"WAF Web ACL associated: {web_acl_name}",
|
|
71
|
+
remediation="No action needed"
|
|
72
|
+
))
|
|
73
|
+
else:
|
|
74
|
+
self.findings.append(self.create_finding(
|
|
75
|
+
status="FAIL",
|
|
76
|
+
region=region,
|
|
77
|
+
resource_id=alb_name,
|
|
78
|
+
actual_value="No WAF Web ACL associated",
|
|
79
|
+
remediation="Associate a WAF Web ACL with this Application Load Balancer using the AWS Console, CLI, or API"
|
|
80
|
+
))
|
|
81
|
+
|
|
82
|
+
return self.findings
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from typing import Dict, List, Any
|
|
2
|
+
from sraverify.services.waf.base import WAFCheck
|
|
3
|
+
|
|
4
|
+
class SRA_WAF_03(WAFCheck):
|
|
5
|
+
def __init__(self):
|
|
6
|
+
super().__init__()
|
|
7
|
+
self.resource_type = "AWS::ApiGateway::RestApi"
|
|
8
|
+
self.check_id = "SRA-WAF-03"
|
|
9
|
+
self.check_name = "API Gateway REST APIs should be associated with AWS WAF"
|
|
10
|
+
self.description = "Ensures that all API Gateway REST APIs are protected by AWS WAF web ACLs to filter malicious traffic"
|
|
11
|
+
self.severity = "HIGH"
|
|
12
|
+
self.check_logic = "Lists all API Gateway REST APIs and verifies each has a WAF web ACL associated"
|
|
13
|
+
|
|
14
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
15
|
+
for region in self.regions:
|
|
16
|
+
rest_apis_response = self.get_rest_apis(region)
|
|
17
|
+
|
|
18
|
+
if "Error" in rest_apis_response:
|
|
19
|
+
self.findings.append(self.create_finding(
|
|
20
|
+
status="ERROR",
|
|
21
|
+
region=region,
|
|
22
|
+
resource_id=None,
|
|
23
|
+
actual_value=rest_apis_response["Error"].get("Message", "Unknown error"),
|
|
24
|
+
remediation="Check IAM permissions for API Gateway and WAF API access"
|
|
25
|
+
))
|
|
26
|
+
continue
|
|
27
|
+
|
|
28
|
+
rest_apis = rest_apis_response.get("items", [])
|
|
29
|
+
|
|
30
|
+
if not rest_apis:
|
|
31
|
+
self.findings.append(self.create_finding(
|
|
32
|
+
status="PASS",
|
|
33
|
+
region=region,
|
|
34
|
+
resource_id="No REST APIs",
|
|
35
|
+
actual_value="No API Gateway REST APIs found",
|
|
36
|
+
remediation="No action needed"
|
|
37
|
+
))
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
for api in rest_apis:
|
|
41
|
+
api_id = api.get("id")
|
|
42
|
+
api_name = api.get("name")
|
|
43
|
+
|
|
44
|
+
# Get all stages for this API
|
|
45
|
+
stages_response = self.get_stages(region, api_id)
|
|
46
|
+
|
|
47
|
+
if "Error" in stages_response:
|
|
48
|
+
self.findings.append(self.create_finding(
|
|
49
|
+
status="ERROR",
|
|
50
|
+
region=region,
|
|
51
|
+
resource_id=api_name or api_id,
|
|
52
|
+
actual_value=stages_response["Error"].get("Message", "Unknown error"),
|
|
53
|
+
remediation="Check IAM permissions for API Gateway access"
|
|
54
|
+
))
|
|
55
|
+
continue
|
|
56
|
+
|
|
57
|
+
stages = stages_response.get("item", [])
|
|
58
|
+
|
|
59
|
+
if not stages:
|
|
60
|
+
self.findings.append(self.create_finding(
|
|
61
|
+
status="FAIL",
|
|
62
|
+
region=region,
|
|
63
|
+
resource_id=api_name or api_id,
|
|
64
|
+
actual_value="No stages deployed",
|
|
65
|
+
remediation="Deploy the API to a stage and associate a WAF Web ACL"
|
|
66
|
+
))
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
# Check each stage for WAF association
|
|
70
|
+
for stage in stages:
|
|
71
|
+
stage_name = stage.get("stageName")
|
|
72
|
+
resource_id = f"{api_name or api_id}/{stage_name}"
|
|
73
|
+
|
|
74
|
+
# Construct the API Gateway stage ARN for WAF association check
|
|
75
|
+
api_arn = f"arn:aws:apigateway:{region}::/restapis/{api_id}/stages/{stage_name}"
|
|
76
|
+
|
|
77
|
+
client = self.get_client(region)
|
|
78
|
+
if not client:
|
|
79
|
+
continue
|
|
80
|
+
|
|
81
|
+
web_acl_response = client.get_web_acl_for_resource(api_arn)
|
|
82
|
+
|
|
83
|
+
if "Error" in web_acl_response:
|
|
84
|
+
error_code = web_acl_response["Error"].get("Code")
|
|
85
|
+
if error_code == "AccessDeniedException":
|
|
86
|
+
self.findings.append(self.create_finding(
|
|
87
|
+
status="ERROR",
|
|
88
|
+
region=region,
|
|
89
|
+
resource_id=resource_id,
|
|
90
|
+
actual_value=web_acl_response["Error"].get("Message", "Access denied"),
|
|
91
|
+
remediation="Check IAM permissions for wafv2:GetWebACLForResource"
|
|
92
|
+
))
|
|
93
|
+
else:
|
|
94
|
+
self.findings.append(self.create_finding(
|
|
95
|
+
status="FAIL",
|
|
96
|
+
region=region,
|
|
97
|
+
resource_id=resource_id,
|
|
98
|
+
actual_value="No WAF Web ACL associated",
|
|
99
|
+
remediation="Associate a WAF Web ACL with this API Gateway stage using the AWS Console, CLI, or API"
|
|
100
|
+
))
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
web_acl = web_acl_response.get("WebACL")
|
|
104
|
+
|
|
105
|
+
if web_acl:
|
|
106
|
+
web_acl_name = web_acl.get("Name", "Unknown")
|
|
107
|
+
self.findings.append(self.create_finding(
|
|
108
|
+
status="PASS",
|
|
109
|
+
region=region,
|
|
110
|
+
resource_id=resource_id,
|
|
111
|
+
actual_value=f"WAF Web ACL associated: {web_acl_name}",
|
|
112
|
+
remediation="No action needed"
|
|
113
|
+
))
|
|
114
|
+
else:
|
|
115
|
+
self.findings.append(self.create_finding(
|
|
116
|
+
status="FAIL",
|
|
117
|
+
region=region,
|
|
118
|
+
resource_id=resource_id,
|
|
119
|
+
actual_value="No WAF Web ACL associated",
|
|
120
|
+
remediation="Associate a WAF Web ACL with this API Gateway stage using the AWS Console, CLI, or API"
|
|
121
|
+
))
|
|
122
|
+
|
|
123
|
+
return self.findings
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from typing import Dict, List, Any
|
|
2
|
+
from sraverify.services.waf.base import WAFCheck
|
|
3
|
+
|
|
4
|
+
class SRA_WAF_04(WAFCheck):
|
|
5
|
+
def __init__(self):
|
|
6
|
+
super().__init__()
|
|
7
|
+
self.resource_type = "AWS::AppSync::GraphQLApi"
|
|
8
|
+
self.check_id = "SRA-WAF-04"
|
|
9
|
+
self.check_name = "AppSync GraphQL APIs should be associated with AWS WAF"
|
|
10
|
+
self.description = "Ensures that all AppSync GraphQL APIs are protected by AWS WAF web ACLs to filter malicious traffic"
|
|
11
|
+
self.severity = "HIGH"
|
|
12
|
+
self.check_logic = "Lists all AppSync GraphQL APIs and verifies each has a WAF web ACL associated"
|
|
13
|
+
|
|
14
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
15
|
+
for region in self.regions:
|
|
16
|
+
graphql_apis_response = self.get_graphql_apis(region)
|
|
17
|
+
|
|
18
|
+
if "Error" in graphql_apis_response:
|
|
19
|
+
self.findings.append(self.create_finding(
|
|
20
|
+
status="ERROR",
|
|
21
|
+
region=region,
|
|
22
|
+
resource_id=None,
|
|
23
|
+
actual_value=graphql_apis_response["Error"].get("Message", "Unknown error"),
|
|
24
|
+
remediation="Check IAM permissions for AppSync and WAF API access"
|
|
25
|
+
))
|
|
26
|
+
continue
|
|
27
|
+
|
|
28
|
+
graphql_apis = graphql_apis_response.get("graphqlApis", [])
|
|
29
|
+
|
|
30
|
+
if not graphql_apis:
|
|
31
|
+
self.findings.append(self.create_finding(
|
|
32
|
+
status="PASS",
|
|
33
|
+
region=region,
|
|
34
|
+
resource_id="No GraphQL APIs",
|
|
35
|
+
actual_value="No AppSync GraphQL APIs found",
|
|
36
|
+
remediation="No action needed"
|
|
37
|
+
))
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
for api in graphql_apis:
|
|
41
|
+
api_arn = api.get("arn")
|
|
42
|
+
api_name = api.get("name")
|
|
43
|
+
api_id = api.get("apiId")
|
|
44
|
+
|
|
45
|
+
# Check if WAF is already associated (wafWebAclArn field)
|
|
46
|
+
waf_web_acl_arn = api.get("wafWebAclArn")
|
|
47
|
+
|
|
48
|
+
if waf_web_acl_arn:
|
|
49
|
+
self.findings.append(self.create_finding(
|
|
50
|
+
status="PASS",
|
|
51
|
+
region=region,
|
|
52
|
+
resource_id=api_name or api_id,
|
|
53
|
+
actual_value=f"WAF Web ACL associated: {waf_web_acl_arn}",
|
|
54
|
+
remediation="No action needed"
|
|
55
|
+
))
|
|
56
|
+
else:
|
|
57
|
+
# Double-check using WAF API
|
|
58
|
+
client = self.get_client(region)
|
|
59
|
+
if not client:
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
web_acl_response = client.get_web_acl_for_resource(api_arn)
|
|
63
|
+
|
|
64
|
+
if "Error" in web_acl_response:
|
|
65
|
+
self.findings.append(self.create_finding(
|
|
66
|
+
status="FAIL",
|
|
67
|
+
region=region,
|
|
68
|
+
resource_id=api_name or api_id,
|
|
69
|
+
actual_value="No WAF Web ACL associated",
|
|
70
|
+
remediation="Associate a WAF Web ACL with this AppSync GraphQL API using the AWS Console, CLI, or API"
|
|
71
|
+
))
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
web_acl = web_acl_response.get("WebACL")
|
|
75
|
+
|
|
76
|
+
if web_acl:
|
|
77
|
+
web_acl_name = web_acl.get("Name", "Unknown")
|
|
78
|
+
self.findings.append(self.create_finding(
|
|
79
|
+
status="PASS",
|
|
80
|
+
region=region,
|
|
81
|
+
resource_id=api_name or api_id,
|
|
82
|
+
actual_value=f"WAF Web ACL associated: {web_acl_name}",
|
|
83
|
+
remediation="No action needed"
|
|
84
|
+
))
|
|
85
|
+
else:
|
|
86
|
+
self.findings.append(self.create_finding(
|
|
87
|
+
status="FAIL",
|
|
88
|
+
region=region,
|
|
89
|
+
resource_id=api_name or api_id,
|
|
90
|
+
actual_value="No WAF Web ACL associated",
|
|
91
|
+
remediation="Associate a WAF Web ACL with this AppSync GraphQL API using the AWS Console, CLI, or API"
|
|
92
|
+
))
|
|
93
|
+
|
|
94
|
+
return self.findings
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from typing import Dict, List, Any
|
|
2
|
+
from sraverify.services.waf.base import WAFCheck
|
|
3
|
+
|
|
4
|
+
class SRA_WAF_05(WAFCheck):
|
|
5
|
+
def __init__(self):
|
|
6
|
+
super().__init__()
|
|
7
|
+
self.resource_type = "AWS::Cognito::UserPool"
|
|
8
|
+
self.check_id = "SRA-WAF-05"
|
|
9
|
+
self.check_name = "Cognito user pools should be associated with AWS WAF"
|
|
10
|
+
self.description = "Ensures that all Cognito user pools are protected by AWS WAF web ACLs to filter malicious traffic"
|
|
11
|
+
self.severity = "HIGH"
|
|
12
|
+
self.check_logic = "Lists all Cognito user pools and verifies each has a WAF web ACL associated"
|
|
13
|
+
|
|
14
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
15
|
+
for region in self.regions:
|
|
16
|
+
user_pools_response = self.get_user_pools(region)
|
|
17
|
+
|
|
18
|
+
if "Error" in user_pools_response:
|
|
19
|
+
self.findings.append(self.create_finding(
|
|
20
|
+
status="ERROR",
|
|
21
|
+
region=region,
|
|
22
|
+
resource_id=None,
|
|
23
|
+
actual_value=user_pools_response["Error"].get("Message", "Unknown error"),
|
|
24
|
+
remediation="Check IAM permissions for Cognito and WAF API access"
|
|
25
|
+
))
|
|
26
|
+
continue
|
|
27
|
+
|
|
28
|
+
user_pools = user_pools_response.get("UserPools", [])
|
|
29
|
+
|
|
30
|
+
if not user_pools:
|
|
31
|
+
self.findings.append(self.create_finding(
|
|
32
|
+
status="PASS",
|
|
33
|
+
region=region,
|
|
34
|
+
resource_id="No user pools",
|
|
35
|
+
actual_value="No Cognito user pools found",
|
|
36
|
+
remediation="No action needed"
|
|
37
|
+
))
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
for pool in user_pools:
|
|
41
|
+
pool_id = pool.get("Id")
|
|
42
|
+
pool_name = pool.get("Name")
|
|
43
|
+
|
|
44
|
+
# Construct the Cognito user pool ARN for WAF association check
|
|
45
|
+
# Format: arn:partition:cognito-idp:region:account-id:userpool/user-pool-id
|
|
46
|
+
pool_arn = f"arn:aws:cognito-idp:{region}:{self.account_id}:userpool/{pool_id}"
|
|
47
|
+
|
|
48
|
+
client = self.get_client(region)
|
|
49
|
+
if not client:
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
web_acl_response = client.get_web_acl_for_resource(pool_arn)
|
|
53
|
+
|
|
54
|
+
if "Error" in web_acl_response:
|
|
55
|
+
error_code = web_acl_response["Error"].get("Code")
|
|
56
|
+
if error_code == "AccessDeniedException":
|
|
57
|
+
self.findings.append(self.create_finding(
|
|
58
|
+
status="ERROR",
|
|
59
|
+
region=region,
|
|
60
|
+
resource_id=pool_name or pool_id,
|
|
61
|
+
actual_value=web_acl_response["Error"].get("Message", "Access denied"),
|
|
62
|
+
remediation="Check IAM permissions for wafv2:GetWebACLForResource"
|
|
63
|
+
))
|
|
64
|
+
else:
|
|
65
|
+
self.findings.append(self.create_finding(
|
|
66
|
+
status="FAIL",
|
|
67
|
+
region=region,
|
|
68
|
+
resource_id=pool_name or pool_id,
|
|
69
|
+
actual_value="No WAF Web ACL associated",
|
|
70
|
+
remediation="Associate a WAF Web ACL with this Cognito user pool using the AWS Console, CLI, or API"
|
|
71
|
+
))
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
web_acl = web_acl_response.get("WebACL")
|
|
75
|
+
|
|
76
|
+
if web_acl:
|
|
77
|
+
web_acl_name = web_acl.get("Name", "Unknown")
|
|
78
|
+
self.findings.append(self.create_finding(
|
|
79
|
+
status="PASS",
|
|
80
|
+
region=region,
|
|
81
|
+
resource_id=pool_name or pool_id,
|
|
82
|
+
actual_value=f"WAF Web ACL associated: {web_acl_name}",
|
|
83
|
+
remediation="No action needed"
|
|
84
|
+
))
|
|
85
|
+
else:
|
|
86
|
+
self.findings.append(self.create_finding(
|
|
87
|
+
status="FAIL",
|
|
88
|
+
region=region,
|
|
89
|
+
resource_id=pool_name or pool_id,
|
|
90
|
+
actual_value="No WAF Web ACL associated",
|
|
91
|
+
remediation="Associate a WAF Web ACL with this Cognito user pool using the AWS Console, CLI, or API"
|
|
92
|
+
))
|
|
93
|
+
|
|
94
|
+
return self.findings
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from typing import Dict, List, Any
|
|
2
|
+
from sraverify.services.waf.base import WAFCheck
|
|
3
|
+
|
|
4
|
+
class SRA_WAF_06(WAFCheck):
|
|
5
|
+
def __init__(self):
|
|
6
|
+
super().__init__()
|
|
7
|
+
self.resource_type = "AWS::AppRunner::Service"
|
|
8
|
+
self.check_id = "SRA-WAF-06"
|
|
9
|
+
self.check_name = "App Runner services should be associated with AWS WAF"
|
|
10
|
+
self.description = "Ensures that all App Runner services are protected by AWS WAF web ACLs to filter malicious traffic"
|
|
11
|
+
self.severity = "HIGH"
|
|
12
|
+
self.check_logic = "Lists all App Runner services and verifies each has a WAF web ACL associated"
|
|
13
|
+
|
|
14
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
15
|
+
for region in self.regions:
|
|
16
|
+
services_response = self.get_apprunner_services(region)
|
|
17
|
+
|
|
18
|
+
if "Error" in services_response:
|
|
19
|
+
self.findings.append(self.create_finding(
|
|
20
|
+
status="ERROR",
|
|
21
|
+
region=region,
|
|
22
|
+
resource_id=None,
|
|
23
|
+
actual_value=services_response["Error"].get("Message", "Unknown error"),
|
|
24
|
+
remediation="Check IAM permissions for App Runner and WAF API access"
|
|
25
|
+
))
|
|
26
|
+
continue
|
|
27
|
+
|
|
28
|
+
services = services_response.get("ServiceSummaryList", [])
|
|
29
|
+
|
|
30
|
+
if not services:
|
|
31
|
+
self.findings.append(self.create_finding(
|
|
32
|
+
status="PASS",
|
|
33
|
+
region=region,
|
|
34
|
+
resource_id="No App Runner services",
|
|
35
|
+
actual_value="No App Runner services found",
|
|
36
|
+
remediation="No action needed"
|
|
37
|
+
))
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
for service in services:
|
|
41
|
+
service_arn = service.get("ServiceArn")
|
|
42
|
+
service_name = service.get("ServiceName")
|
|
43
|
+
service_id = service.get("ServiceId")
|
|
44
|
+
|
|
45
|
+
client = self.get_client(region)
|
|
46
|
+
if not client:
|
|
47
|
+
continue
|
|
48
|
+
|
|
49
|
+
web_acl_response = client.get_web_acl_for_resource(service_arn)
|
|
50
|
+
|
|
51
|
+
if "Error" in web_acl_response:
|
|
52
|
+
error_code = web_acl_response["Error"].get("Code")
|
|
53
|
+
if error_code == "AccessDeniedException":
|
|
54
|
+
self.findings.append(self.create_finding(
|
|
55
|
+
status="ERROR",
|
|
56
|
+
region=region,
|
|
57
|
+
resource_id=service_name or service_id,
|
|
58
|
+
actual_value=web_acl_response["Error"].get("Message", "Access denied"),
|
|
59
|
+
remediation="Check IAM permissions for wafv2:GetWebACLForResource and apprunner:DescribeWebAclForService"
|
|
60
|
+
))
|
|
61
|
+
else:
|
|
62
|
+
self.findings.append(self.create_finding(
|
|
63
|
+
status="FAIL",
|
|
64
|
+
region=region,
|
|
65
|
+
resource_id=service_name or service_id,
|
|
66
|
+
actual_value="No WAF Web ACL associated",
|
|
67
|
+
remediation="Associate a WAF Web ACL with this App Runner service using the AWS Console, CLI, or API"
|
|
68
|
+
))
|
|
69
|
+
continue
|
|
70
|
+
|
|
71
|
+
web_acl = web_acl_response.get("WebACL")
|
|
72
|
+
|
|
73
|
+
if web_acl:
|
|
74
|
+
web_acl_name = web_acl.get("Name", "Unknown")
|
|
75
|
+
self.findings.append(self.create_finding(
|
|
76
|
+
status="PASS",
|
|
77
|
+
region=region,
|
|
78
|
+
resource_id=service_name or service_id,
|
|
79
|
+
actual_value=f"WAF Web ACL associated: {web_acl_name}",
|
|
80
|
+
remediation="No action needed"
|
|
81
|
+
))
|
|
82
|
+
else:
|
|
83
|
+
self.findings.append(self.create_finding(
|
|
84
|
+
status="FAIL",
|
|
85
|
+
region=region,
|
|
86
|
+
resource_id=service_name or service_id,
|
|
87
|
+
actual_value="No WAF Web ACL associated",
|
|
88
|
+
remediation="Associate a WAF Web ACL with this App Runner service using the AWS Console, CLI, or API"
|
|
89
|
+
))
|
|
90
|
+
|
|
91
|
+
return self.findings
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from typing import Dict, List, Any
|
|
2
|
+
from sraverify.services.waf.base import WAFCheck
|
|
3
|
+
|
|
4
|
+
class SRA_WAF_07(WAFCheck):
|
|
5
|
+
def __init__(self):
|
|
6
|
+
super().__init__()
|
|
7
|
+
self.resource_type = "AWS::EC2::VerifiedAccessInstance"
|
|
8
|
+
self.check_id = "SRA-WAF-07"
|
|
9
|
+
self.check_name = "Verified Access instances should be associated with AWS WAF"
|
|
10
|
+
self.description = "Ensures that all Verified Access instances are protected by AWS WAF web ACLs to filter malicious traffic"
|
|
11
|
+
self.severity = "HIGH"
|
|
12
|
+
self.check_logic = "Lists all Verified Access instances and verifies each has a WAF web ACL associated"
|
|
13
|
+
|
|
14
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
15
|
+
for region in self.regions:
|
|
16
|
+
instances_response = self.get_verified_access_instances(region)
|
|
17
|
+
|
|
18
|
+
if "Error" in instances_response:
|
|
19
|
+
self.findings.append(self.create_finding(
|
|
20
|
+
status="ERROR",
|
|
21
|
+
region=region,
|
|
22
|
+
resource_id=None,
|
|
23
|
+
actual_value=instances_response["Error"].get("Message", "Unknown error"),
|
|
24
|
+
remediation="Check IAM permissions for EC2 and WAF API access"
|
|
25
|
+
))
|
|
26
|
+
continue
|
|
27
|
+
|
|
28
|
+
instances = instances_response.get("VerifiedAccessInstances", [])
|
|
29
|
+
|
|
30
|
+
if not instances:
|
|
31
|
+
self.findings.append(self.create_finding(
|
|
32
|
+
status="PASS",
|
|
33
|
+
region=region,
|
|
34
|
+
resource_id="No Verified Access instances",
|
|
35
|
+
actual_value="No Verified Access instances found",
|
|
36
|
+
remediation="No action needed"
|
|
37
|
+
))
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
for instance in instances:
|
|
41
|
+
instance_id = instance.get("VerifiedAccessInstanceId")
|
|
42
|
+
description = instance.get("Description", "")
|
|
43
|
+
|
|
44
|
+
# Construct the Verified Access instance ARN for WAF association check
|
|
45
|
+
# Format: arn:partition:ec2:region:account-id:verified-access-instance/instance-id
|
|
46
|
+
instance_arn = f"arn:aws:ec2:{region}:{self.account_id}:verified-access-instance/{instance_id}"
|
|
47
|
+
|
|
48
|
+
client = self.get_client(region)
|
|
49
|
+
if not client:
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
web_acl_response = client.get_web_acl_for_resource(instance_arn)
|
|
53
|
+
|
|
54
|
+
if "Error" in web_acl_response:
|
|
55
|
+
error_code = web_acl_response["Error"].get("Code")
|
|
56
|
+
if error_code == "AccessDeniedException":
|
|
57
|
+
self.findings.append(self.create_finding(
|
|
58
|
+
status="ERROR",
|
|
59
|
+
region=region,
|
|
60
|
+
resource_id=instance_id,
|
|
61
|
+
actual_value=web_acl_response["Error"].get("Message", "Access denied"),
|
|
62
|
+
remediation="Check IAM permissions for wafv2:GetWebACLForResource and ec2:GetVerifiedAccessInstanceWebAcl"
|
|
63
|
+
))
|
|
64
|
+
else:
|
|
65
|
+
self.findings.append(self.create_finding(
|
|
66
|
+
status="FAIL",
|
|
67
|
+
region=region,
|
|
68
|
+
resource_id=instance_id,
|
|
69
|
+
actual_value="No WAF Web ACL associated",
|
|
70
|
+
remediation="Associate a WAF Web ACL with this Verified Access instance using the AWS Console, CLI, or API"
|
|
71
|
+
))
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
web_acl = web_acl_response.get("WebACL")
|
|
75
|
+
|
|
76
|
+
if web_acl:
|
|
77
|
+
web_acl_name = web_acl.get("Name", "Unknown")
|
|
78
|
+
self.findings.append(self.create_finding(
|
|
79
|
+
status="PASS",
|
|
80
|
+
region=region,
|
|
81
|
+
resource_id=instance_id,
|
|
82
|
+
actual_value=f"WAF Web ACL associated: {web_acl_name}",
|
|
83
|
+
remediation="No action needed"
|
|
84
|
+
))
|
|
85
|
+
else:
|
|
86
|
+
self.findings.append(self.create_finding(
|
|
87
|
+
status="FAIL",
|
|
88
|
+
region=region,
|
|
89
|
+
resource_id=instance_id,
|
|
90
|
+
actual_value="No WAF Web ACL associated",
|
|
91
|
+
remediation="Associate a WAF Web ACL with this Verified Access instance using the AWS Console, CLI, or API"
|
|
92
|
+
))
|
|
93
|
+
|
|
94
|
+
return self.findings
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from typing import Dict, List, Any
|
|
2
|
+
from sraverify.services.waf.base import WAFCheck
|
|
3
|
+
|
|
4
|
+
class SRA_WAF_08(WAFCheck):
|
|
5
|
+
def __init__(self):
|
|
6
|
+
super().__init__()
|
|
7
|
+
self.resource_type = "AWS::Amplify::App"
|
|
8
|
+
self.check_id = "SRA-WAF-08"
|
|
9
|
+
self.check_name = "Amplify applications should be associated with AWS WAF"
|
|
10
|
+
self.description = "Ensures that all Amplify applications are protected by AWS WAF web ACLs to filter malicious traffic"
|
|
11
|
+
self.severity = "HIGH"
|
|
12
|
+
self.check_logic = "Lists all Amplify applications and verifies each has a WAF web ACL associated"
|
|
13
|
+
|
|
14
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
15
|
+
for region in self.regions:
|
|
16
|
+
apps_response = self.get_amplify_apps(region)
|
|
17
|
+
|
|
18
|
+
if "Error" in apps_response:
|
|
19
|
+
self.findings.append(self.create_finding(
|
|
20
|
+
status="ERROR",
|
|
21
|
+
region=region,
|
|
22
|
+
resource_id=None,
|
|
23
|
+
actual_value=apps_response["Error"].get("Message", "Unknown error"),
|
|
24
|
+
remediation="Check IAM permissions for Amplify and WAF API access"
|
|
25
|
+
))
|
|
26
|
+
continue
|
|
27
|
+
|
|
28
|
+
apps = apps_response.get("apps", [])
|
|
29
|
+
|
|
30
|
+
if not apps:
|
|
31
|
+
self.findings.append(self.create_finding(
|
|
32
|
+
status="PASS",
|
|
33
|
+
region=region,
|
|
34
|
+
resource_id="No Amplify apps",
|
|
35
|
+
actual_value="No Amplify applications found",
|
|
36
|
+
remediation="No action needed"
|
|
37
|
+
))
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
for app in apps:
|
|
41
|
+
app_id = app.get("appId")
|
|
42
|
+
app_name = app.get("name")
|
|
43
|
+
waf_config = app.get("wafConfiguration", {})
|
|
44
|
+
|
|
45
|
+
# Check WAF configuration from the app response
|
|
46
|
+
waf_status = waf_config.get("wafStatus")
|
|
47
|
+
web_acl_arn = waf_config.get("webAclArn")
|
|
48
|
+
|
|
49
|
+
if waf_status == "ENABLED" and web_acl_arn:
|
|
50
|
+
self.findings.append(self.create_finding(
|
|
51
|
+
status="PASS",
|
|
52
|
+
region=region,
|
|
53
|
+
resource_id=app_name or app_id,
|
|
54
|
+
actual_value=f"WAF Web ACL associated: {web_acl_arn}",
|
|
55
|
+
remediation="No action needed"
|
|
56
|
+
))
|
|
57
|
+
else:
|
|
58
|
+
self.findings.append(self.create_finding(
|
|
59
|
+
status="FAIL",
|
|
60
|
+
region=region,
|
|
61
|
+
resource_id=app_name or app_id,
|
|
62
|
+
actual_value="No WAF Web ACL associated",
|
|
63
|
+
remediation="Associate a WAF Web ACL with this Amplify application using the AWS Console, CLI, or API"
|
|
64
|
+
))
|
|
65
|
+
|
|
66
|
+
return self.findings
|