complio 0.1.1__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.
- CHANGELOG.md +208 -0
- README.md +343 -0
- complio/__init__.py +48 -0
- complio/cli/__init__.py +0 -0
- complio/cli/banner.py +87 -0
- complio/cli/commands/__init__.py +0 -0
- complio/cli/commands/history.py +439 -0
- complio/cli/commands/scan.py +700 -0
- complio/cli/main.py +115 -0
- complio/cli/output.py +338 -0
- complio/config/__init__.py +17 -0
- complio/config/settings.py +333 -0
- complio/connectors/__init__.py +9 -0
- complio/connectors/aws/__init__.py +0 -0
- complio/connectors/aws/client.py +342 -0
- complio/connectors/base.py +135 -0
- complio/core/__init__.py +10 -0
- complio/core/registry.py +228 -0
- complio/core/runner.py +351 -0
- complio/py.typed +0 -0
- complio/reporters/__init__.py +7 -0
- complio/reporters/generator.py +417 -0
- complio/tests_library/__init__.py +0 -0
- complio/tests_library/base.py +492 -0
- complio/tests_library/identity/__init__.py +0 -0
- complio/tests_library/identity/access_key_rotation.py +302 -0
- complio/tests_library/identity/mfa_enforcement.py +327 -0
- complio/tests_library/identity/root_account_protection.py +470 -0
- complio/tests_library/infrastructure/__init__.py +0 -0
- complio/tests_library/infrastructure/cloudtrail_encryption.py +286 -0
- complio/tests_library/infrastructure/cloudtrail_log_validation.py +274 -0
- complio/tests_library/infrastructure/cloudtrail_logging.py +400 -0
- complio/tests_library/infrastructure/ebs_encryption.py +244 -0
- complio/tests_library/infrastructure/ec2_security_groups.py +321 -0
- complio/tests_library/infrastructure/iam_password_policy.py +460 -0
- complio/tests_library/infrastructure/nacl_security.py +356 -0
- complio/tests_library/infrastructure/rds_encryption.py +252 -0
- complio/tests_library/infrastructure/s3_encryption.py +301 -0
- complio/tests_library/infrastructure/s3_public_access.py +369 -0
- complio/tests_library/infrastructure/secrets_manager_encryption.py +248 -0
- complio/tests_library/infrastructure/vpc_flow_logs.py +287 -0
- complio/tests_library/logging/__init__.py +0 -0
- complio/tests_library/logging/cloudwatch_alarms.py +354 -0
- complio/tests_library/logging/cloudwatch_logs_encryption.py +281 -0
- complio/tests_library/logging/cloudwatch_retention.py +252 -0
- complio/tests_library/logging/config_enabled.py +393 -0
- complio/tests_library/logging/eventbridge_rules.py +460 -0
- complio/tests_library/logging/guardduty_enabled.py +436 -0
- complio/tests_library/logging/security_hub_enabled.py +416 -0
- complio/tests_library/logging/sns_encryption.py +273 -0
- complio/tests_library/network/__init__.py +0 -0
- complio/tests_library/network/alb_nlb_security.py +421 -0
- complio/tests_library/network/api_gateway_security.py +452 -0
- complio/tests_library/network/cloudfront_https.py +332 -0
- complio/tests_library/network/direct_connect_security.py +343 -0
- complio/tests_library/network/nacl_configuration.py +367 -0
- complio/tests_library/network/network_firewall.py +355 -0
- complio/tests_library/network/transit_gateway_security.py +318 -0
- complio/tests_library/network/vpc_endpoints_security.py +339 -0
- complio/tests_library/network/vpn_security.py +333 -0
- complio/tests_library/network/waf_configuration.py +428 -0
- complio/tests_library/security/__init__.py +0 -0
- complio/tests_library/security/kms_key_rotation.py +314 -0
- complio/tests_library/storage/__init__.py +0 -0
- complio/tests_library/storage/backup_encryption.py +288 -0
- complio/tests_library/storage/dynamodb_encryption.py +280 -0
- complio/tests_library/storage/efs_encryption.py +257 -0
- complio/tests_library/storage/elasticache_encryption.py +370 -0
- complio/tests_library/storage/redshift_encryption.py +252 -0
- complio/tests_library/storage/s3_versioning.py +264 -0
- complio/utils/__init__.py +26 -0
- complio/utils/errors.py +179 -0
- complio/utils/exceptions.py +151 -0
- complio/utils/history.py +243 -0
- complio/utils/logger.py +391 -0
- complio-0.1.1.dist-info/METADATA +385 -0
- complio-0.1.1.dist-info/RECORD +79 -0
- complio-0.1.1.dist-info/WHEEL +4 -0
- complio-0.1.1.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AWS WAF (Web Application Firewall) configuration compliance test.
|
|
3
|
+
|
|
4
|
+
Checks that AWS WAF is properly configured for web application protection.
|
|
5
|
+
|
|
6
|
+
ISO 27001 Control: A.8.20 - Networks security
|
|
7
|
+
Requirement: Web applications should be protected by WAF with proper rules
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
>>> from complio.connectors.aws.client import AWSConnector
|
|
11
|
+
>>> from complio.tests_library.network.waf_configuration import WAFConfigurationTest
|
|
12
|
+
>>>
|
|
13
|
+
>>> connector = AWSConnector("production", "us-east-1")
|
|
14
|
+
>>> connector.connect()
|
|
15
|
+
>>>
|
|
16
|
+
>>> test = WAFConfigurationTest(connector)
|
|
17
|
+
>>> result = test.run()
|
|
18
|
+
>>> print(f"Passed: {result.passed}, Score: {result.score}")
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from typing import Any, Dict, List
|
|
22
|
+
|
|
23
|
+
from botocore.exceptions import ClientError
|
|
24
|
+
|
|
25
|
+
from complio.connectors.aws.client import AWSConnector
|
|
26
|
+
from complio.tests_library.base import (
|
|
27
|
+
ComplianceTest,
|
|
28
|
+
Severity,
|
|
29
|
+
TestResult,
|
|
30
|
+
TestStatus,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class WAFConfigurationTest(ComplianceTest):
|
|
35
|
+
"""Test for AWS WAF configuration compliance.
|
|
36
|
+
|
|
37
|
+
Verifies that AWS WAF (Web Application Firewall) is properly configured:
|
|
38
|
+
- WAF WebACLs should be associated with resources (ALB, CloudFront, API Gateway)
|
|
39
|
+
- WebACLs should have rules configured (not empty)
|
|
40
|
+
- Logging should be enabled
|
|
41
|
+
- Should use AWS managed rules or custom rules for protection
|
|
42
|
+
|
|
43
|
+
Compliance Requirements:
|
|
44
|
+
- WAF deployed for public-facing web applications
|
|
45
|
+
- WebACLs have active rules (managed or custom)
|
|
46
|
+
- Logging enabled for security monitoring
|
|
47
|
+
- Rules cover common vulnerabilities (OWASP Top 10)
|
|
48
|
+
|
|
49
|
+
Scoring:
|
|
50
|
+
- Based on WebACL configuration and associations
|
|
51
|
+
- Checks rule counts and logging status
|
|
52
|
+
- Validates resource protection
|
|
53
|
+
|
|
54
|
+
Example:
|
|
55
|
+
>>> test = WAFConfigurationTest(connector)
|
|
56
|
+
>>> result = test.execute()
|
|
57
|
+
>>> for finding in result.findings:
|
|
58
|
+
... print(f"{finding.resource_id}: {finding.title}")
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(self, connector: AWSConnector) -> None:
|
|
62
|
+
"""Initialize WAF configuration test.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
connector: AWS connector instance
|
|
66
|
+
"""
|
|
67
|
+
super().__init__(
|
|
68
|
+
test_id="waf_configuration",
|
|
69
|
+
test_name="AWS WAF Configuration Check",
|
|
70
|
+
description="Verify AWS WAF is properly configured for web application protection",
|
|
71
|
+
control_id="A.8.20",
|
|
72
|
+
connector=connector,
|
|
73
|
+
scope="regional", # WAFv2 is regional, but also has CLOUDFRONT scope
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def _check_webacl_configuration(self, webacl: Dict, wafv2_client: Any, scope: str) -> tuple:
|
|
77
|
+
"""Check WebACL configuration for issues.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
webacl: WebACL summary dict
|
|
81
|
+
wafv2_client: WAFv2 client
|
|
82
|
+
scope: REGIONAL or CLOUDFRONT
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Tuple of (has_issues, issues_list, severity, rule_count)
|
|
86
|
+
"""
|
|
87
|
+
issues = []
|
|
88
|
+
severity = Severity.MEDIUM
|
|
89
|
+
|
|
90
|
+
webacl_id = webacl.get("Id")
|
|
91
|
+
webacl_name = webacl.get("Name")
|
|
92
|
+
webacl_arn = webacl.get("ARN")
|
|
93
|
+
|
|
94
|
+
# Get detailed WebACL configuration
|
|
95
|
+
try:
|
|
96
|
+
webacl_response = wafv2_client.get_web_acl(
|
|
97
|
+
Name=webacl_name,
|
|
98
|
+
Scope=scope,
|
|
99
|
+
Id=webacl_id
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
webacl_details = webacl_response.get("WebACL", {})
|
|
103
|
+
rules = webacl_details.get("Rules", [])
|
|
104
|
+
rule_count = len(rules)
|
|
105
|
+
|
|
106
|
+
# Check if WebACL has rules
|
|
107
|
+
if rule_count == 0:
|
|
108
|
+
issues.append("No rules configured (WebACL provides no protection)")
|
|
109
|
+
severity = Severity.HIGH
|
|
110
|
+
|
|
111
|
+
# Check if WebACL is associated with resources
|
|
112
|
+
# Note: Need to check associations separately for ALB/CloudFront/API Gateway
|
|
113
|
+
# This requires additional API calls which we'll do in the main execute method
|
|
114
|
+
|
|
115
|
+
# Check for logging
|
|
116
|
+
try:
|
|
117
|
+
logging_config = wafv2_client.get_logging_configuration(
|
|
118
|
+
ResourceArn=webacl_arn
|
|
119
|
+
)
|
|
120
|
+
logging_enabled = True
|
|
121
|
+
except ClientError as e:
|
|
122
|
+
if e.response.get("Error", {}).get("Code") == "WAFNonexistentItemException":
|
|
123
|
+
logging_enabled = False
|
|
124
|
+
issues.append("WAF logging not enabled")
|
|
125
|
+
else:
|
|
126
|
+
raise
|
|
127
|
+
|
|
128
|
+
return len(issues) > 0, issues, severity, rule_count, logging_enabled
|
|
129
|
+
|
|
130
|
+
except ClientError as e:
|
|
131
|
+
error_code = e.response.get("Error", {}).get("Code")
|
|
132
|
+
issues.append(f"Error retrieving WebACL details: {error_code}")
|
|
133
|
+
return True, issues, Severity.MEDIUM, 0, False
|
|
134
|
+
|
|
135
|
+
def execute(self) -> TestResult:
|
|
136
|
+
"""Execute AWS WAF configuration compliance test.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
TestResult with findings for missing or misconfigured WAF
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
>>> test = WAFConfigurationTest(connector)
|
|
143
|
+
>>> result = test.execute()
|
|
144
|
+
>>> print(result.score)
|
|
145
|
+
85.0
|
|
146
|
+
"""
|
|
147
|
+
result = TestResult(
|
|
148
|
+
test_id=self.test_id,
|
|
149
|
+
test_name=self.test_name,
|
|
150
|
+
status=TestStatus.PASSED,
|
|
151
|
+
passed=True,
|
|
152
|
+
score=100.0,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
# Get WAFv2 client
|
|
157
|
+
wafv2_client = self.connector.get_client("wafv2")
|
|
158
|
+
|
|
159
|
+
# Check both REGIONAL and CLOUDFRONT scopes
|
|
160
|
+
all_webacls = []
|
|
161
|
+
|
|
162
|
+
# Regional WebACLs (for ALB, API Gateway)
|
|
163
|
+
self.logger.info("listing_regional_webacls")
|
|
164
|
+
try:
|
|
165
|
+
regional_response = wafv2_client.list_web_acls(Scope="REGIONAL")
|
|
166
|
+
regional_webacls = regional_response.get("WebACLs", [])
|
|
167
|
+
for webacl in regional_webacls:
|
|
168
|
+
webacl["_scope"] = "REGIONAL"
|
|
169
|
+
all_webacls.append(webacl)
|
|
170
|
+
self.logger.info("regional_webacls_found", count=len(regional_webacls))
|
|
171
|
+
except ClientError as e:
|
|
172
|
+
self.logger.warning("error_listing_regional_webacls", error=str(e))
|
|
173
|
+
|
|
174
|
+
# CloudFront WebACLs (global)
|
|
175
|
+
self.logger.info("listing_cloudfront_webacls")
|
|
176
|
+
try:
|
|
177
|
+
# CloudFront WAF must be in us-east-1
|
|
178
|
+
cloudfront_wafv2_client = self.connector.session.client("wafv2", region_name="us-east-1")
|
|
179
|
+
cloudfront_response = cloudfront_wafv2_client.list_web_acls(Scope="CLOUDFRONT")
|
|
180
|
+
cloudfront_webacls = cloudfront_response.get("WebACLs", [])
|
|
181
|
+
for webacl in cloudfront_webacls:
|
|
182
|
+
webacl["_scope"] = "CLOUDFRONT"
|
|
183
|
+
all_webacls.append(webacl)
|
|
184
|
+
self.logger.info("cloudfront_webacls_found", count=len(cloudfront_webacls))
|
|
185
|
+
except ClientError as e:
|
|
186
|
+
self.logger.warning("error_listing_cloudfront_webacls", error=str(e))
|
|
187
|
+
|
|
188
|
+
if not all_webacls:
|
|
189
|
+
self.logger.info("no_waf_webacls_found")
|
|
190
|
+
result.metadata["message"] = "No AWS WAF WebACLs found (consider deploying WAF for web applications)"
|
|
191
|
+
result.metadata["recommendation"] = "Deploy WAF for ALB, CloudFront, and API Gateway"
|
|
192
|
+
# Not a hard failure, but should be noted
|
|
193
|
+
result.score = 50.0
|
|
194
|
+
result.passed = False
|
|
195
|
+
result.status = TestStatus.FAILED
|
|
196
|
+
return result
|
|
197
|
+
|
|
198
|
+
self.logger.info("waf_webacls_found", total_count=len(all_webacls))
|
|
199
|
+
|
|
200
|
+
# Check configuration for each WebACL
|
|
201
|
+
properly_configured_count = 0
|
|
202
|
+
|
|
203
|
+
for webacl in all_webacls:
|
|
204
|
+
webacl_name = webacl.get("Name", "")
|
|
205
|
+
webacl_arn = webacl.get("ARN", "")
|
|
206
|
+
webacl_id = webacl.get("Id", "")
|
|
207
|
+
scope = webacl.get("_scope", "REGIONAL")
|
|
208
|
+
|
|
209
|
+
result.resources_scanned += 1
|
|
210
|
+
|
|
211
|
+
# Use appropriate client based on scope
|
|
212
|
+
if scope == "CLOUDFRONT":
|
|
213
|
+
check_client = self.connector.session.client("wafv2", region_name="us-east-1")
|
|
214
|
+
else:
|
|
215
|
+
check_client = wafv2_client
|
|
216
|
+
|
|
217
|
+
# Check WebACL configuration
|
|
218
|
+
has_issues, issues_list, severity, rule_count, logging_enabled = \
|
|
219
|
+
self._check_webacl_configuration(webacl, check_client, scope)
|
|
220
|
+
|
|
221
|
+
# Create evidence
|
|
222
|
+
evidence = self.create_evidence(
|
|
223
|
+
resource_id=webacl_arn,
|
|
224
|
+
resource_type="waf_webacl",
|
|
225
|
+
data={
|
|
226
|
+
"webacl_name": webacl_name,
|
|
227
|
+
"webacl_arn": webacl_arn,
|
|
228
|
+
"webacl_id": webacl_id,
|
|
229
|
+
"scope": scope,
|
|
230
|
+
"rule_count": rule_count,
|
|
231
|
+
"logging_enabled": logging_enabled,
|
|
232
|
+
"has_issues": has_issues,
|
|
233
|
+
"issues": issues_list,
|
|
234
|
+
}
|
|
235
|
+
)
|
|
236
|
+
result.add_evidence(evidence)
|
|
237
|
+
|
|
238
|
+
if not has_issues:
|
|
239
|
+
properly_configured_count += 1
|
|
240
|
+
self.logger.debug(
|
|
241
|
+
"webacl_properly_configured",
|
|
242
|
+
webacl_name=webacl_name,
|
|
243
|
+
scope=scope
|
|
244
|
+
)
|
|
245
|
+
else:
|
|
246
|
+
# Create finding for misconfigured WebACL
|
|
247
|
+
finding = self.create_finding(
|
|
248
|
+
resource_id=webacl_arn,
|
|
249
|
+
resource_type="waf_webacl",
|
|
250
|
+
severity=severity,
|
|
251
|
+
title=f"WAF WebACL has configuration issues ({scope})",
|
|
252
|
+
description=f"AWS WAF WebACL '{webacl_name}' (scope: {scope}) has configuration issues: "
|
|
253
|
+
f"{'; '.join(issues_list)}. WAF WebACLs should have active rules configured "
|
|
254
|
+
f"and logging enabled to protect web applications from common attacks and "
|
|
255
|
+
"monitor security events. ISO 27001 A.8.20 requires proper network security "
|
|
256
|
+
"controls for web applications.",
|
|
257
|
+
remediation=(
|
|
258
|
+
f"Improve WAF WebACL '{webacl_name}' configuration:\n\n"
|
|
259
|
+
"1. Add AWS Managed Rule Groups (recommended):\n"
|
|
260
|
+
f"aws wafv2 update-web-acl \\\n"
|
|
261
|
+
f" --name {webacl_name} \\\n"
|
|
262
|
+
f" --scope {scope} \\\n"
|
|
263
|
+
f" --id {webacl_id} \\\n"
|
|
264
|
+
" --cli-input-json file://webacl-config.json\n\n"
|
|
265
|
+
"Recommended AWS Managed Rule Groups:\n"
|
|
266
|
+
"- AWSManagedRulesCommonRuleSet (OWASP Top 10)\n"
|
|
267
|
+
"- AWSManagedRulesKnownBadInputsRuleSet (bad inputs)\n"
|
|
268
|
+
"- AWSManagedRulesSQLiRuleSet (SQL injection)\n"
|
|
269
|
+
"- AWSManagedRulesLinuxRuleSet (Linux exploits)\n"
|
|
270
|
+
"- AWSManagedRulesAmazonIpReputationList (IP reputation)\n"
|
|
271
|
+
"- AWSManagedRulesAnonymousIpList (Tor, VPN, proxies)\n\n"
|
|
272
|
+
"2. Enable WAF logging:\n"
|
|
273
|
+
"# Create S3 bucket or Kinesis stream first\n"
|
|
274
|
+
"# Bucket name must start with 'aws-waf-logs-'\n"
|
|
275
|
+
"aws s3 mb s3://aws-waf-logs-myapp\n\n"
|
|
276
|
+
f"aws wafv2 put-logging-configuration \\\n"
|
|
277
|
+
" --logging-configuration '{\n"
|
|
278
|
+
f' "ResourceArn": "{webacl_arn}",\n'
|
|
279
|
+
' "LogDestinationConfigs": [\n'
|
|
280
|
+
' "arn:aws:s3:::aws-waf-logs-myapp"\n'
|
|
281
|
+
' ]\n'
|
|
282
|
+
" }'\n\n"
|
|
283
|
+
"3. Associate WebACL with resources:\n"
|
|
284
|
+
"For Application Load Balancer:\n"
|
|
285
|
+
f"aws wafv2 associate-web-acl \\\n"
|
|
286
|
+
f" --web-acl-arn {webacl_arn} \\\n"
|
|
287
|
+
" --resource-arn <ALB-ARN>\n\n"
|
|
288
|
+
"For API Gateway (REST):\n"
|
|
289
|
+
f"aws wafv2 associate-web-acl \\\n"
|
|
290
|
+
f" --web-acl-arn {webacl_arn} \\\n"
|
|
291
|
+
" --resource-arn <API-GATEWAY-STAGE-ARN>\n\n"
|
|
292
|
+
"For CloudFront (must use CLOUDFRONT scope WebACL):\n"
|
|
293
|
+
"aws cloudfront update-distribution \\\n"
|
|
294
|
+
" --id <DISTRIBUTION-ID> \\\n"
|
|
295
|
+
" --distribution-config file://dist-config.json\n"
|
|
296
|
+
"# Add WebACLId in distribution config\n\n"
|
|
297
|
+
"Or use AWS Console:\n"
|
|
298
|
+
"1. Go to AWS WAF console\n"
|
|
299
|
+
f"2. Select WebACL '{webacl_name}'\n"
|
|
300
|
+
"3. Add rules:\n"
|
|
301
|
+
" - Click 'Rules' tab → 'Add rules'\n"
|
|
302
|
+
" - Add managed rule groups (AWS or Marketplace)\n"
|
|
303
|
+
" - Configure custom rules if needed\n"
|
|
304
|
+
" - Set rule priorities\n"
|
|
305
|
+
"4. Enable logging:\n"
|
|
306
|
+
" - Go to 'Logging' tab\n"
|
|
307
|
+
" - Enable logging\n"
|
|
308
|
+
" - Select S3 bucket or Kinesis stream\n"
|
|
309
|
+
"5. Associate with resources:\n"
|
|
310
|
+
" - Go to 'Associated AWS resources' tab\n"
|
|
311
|
+
" - Add ALB, CloudFront, or API Gateway\n\n"
|
|
312
|
+
"Security best practices:\n"
|
|
313
|
+
"- Use AWS Managed Rules as baseline\n"
|
|
314
|
+
"- Add rate limiting rules to prevent DDoS\n"
|
|
315
|
+
"- Configure geo-blocking for specific countries\n"
|
|
316
|
+
"- Use IP sets for allow/deny lists\n"
|
|
317
|
+
"- Enable bot control for automated traffic\n"
|
|
318
|
+
"- Set up CloudWatch alarms for blocked requests\n"
|
|
319
|
+
"- Use AWS Firewall Manager for centralized management\n"
|
|
320
|
+
"- Regularly review WAF logs and metrics\n"
|
|
321
|
+
"- Test rules in COUNT mode before BLOCK\n"
|
|
322
|
+
"- Use custom rules for application-specific logic\n\n"
|
|
323
|
+
"Common rule examples:\n"
|
|
324
|
+
"Rate limiting:\n"
|
|
325
|
+
"- Limit: 2000 requests per 5 minutes per IP\n"
|
|
326
|
+
"- Action: Block for 10 minutes\n\n"
|
|
327
|
+
"Geo-blocking:\n"
|
|
328
|
+
"- Block requests from high-risk countries\n"
|
|
329
|
+
"- Allow only specific countries if applicable\n\n"
|
|
330
|
+
"IP reputation:\n"
|
|
331
|
+
"- Block known malicious IPs\n"
|
|
332
|
+
"- Block Tor exit nodes\n"
|
|
333
|
+
"- Block proxies and VPNs if needed\n\n"
|
|
334
|
+
"OWASP Top 10 protection:\n"
|
|
335
|
+
"- SQL injection\n"
|
|
336
|
+
"- Cross-site scripting (XSS)\n"
|
|
337
|
+
"- Path traversal\n"
|
|
338
|
+
"- Command injection\n"
|
|
339
|
+
"- Session fixation\n\n"
|
|
340
|
+
"Monitoring and alerting:\n"
|
|
341
|
+
"- Set CloudWatch alarms for:\n"
|
|
342
|
+
" • Blocked requests > threshold\n"
|
|
343
|
+
" • Counted requests (testing rules)\n"
|
|
344
|
+
" • Rule evaluation errors\n"
|
|
345
|
+
"- Integrate with Security Hub\n"
|
|
346
|
+
"- Send logs to SIEM for analysis\n"
|
|
347
|
+
"- Use Athena to query WAF logs in S3"
|
|
348
|
+
),
|
|
349
|
+
evidence=evidence
|
|
350
|
+
)
|
|
351
|
+
result.add_finding(finding)
|
|
352
|
+
|
|
353
|
+
self.logger.warning(
|
|
354
|
+
"webacl_misconfigured",
|
|
355
|
+
webacl_name=webacl_name,
|
|
356
|
+
scope=scope,
|
|
357
|
+
issues=issues_list
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
# Calculate compliance score
|
|
361
|
+
if len(all_webacls) > 0:
|
|
362
|
+
result.score = (properly_configured_count / len(all_webacls)) * 100
|
|
363
|
+
result.passed = properly_configured_count == len(all_webacls)
|
|
364
|
+
else:
|
|
365
|
+
result.score = 50.0
|
|
366
|
+
result.passed = False
|
|
367
|
+
|
|
368
|
+
result.status = TestStatus.PASSED if result.passed else TestStatus.FAILED
|
|
369
|
+
|
|
370
|
+
# Add metadata
|
|
371
|
+
result.metadata = {
|
|
372
|
+
"total_webacls": len(all_webacls),
|
|
373
|
+
"properly_configured": properly_configured_count,
|
|
374
|
+
"misconfigured": len(all_webacls) - properly_configured_count,
|
|
375
|
+
"compliance_percentage": result.score,
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
self.logger.info(
|
|
379
|
+
"waf_configuration_test_completed",
|
|
380
|
+
total_webacls=len(all_webacls),
|
|
381
|
+
properly_configured=properly_configured_count,
|
|
382
|
+
score=result.score,
|
|
383
|
+
passed=result.passed
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
except ClientError as e:
|
|
387
|
+
error_code = e.response.get("Error", {}).get("Code")
|
|
388
|
+
self.logger.error("waf_configuration_test_error", error_code=error_code, error=str(e))
|
|
389
|
+
result.status = TestStatus.ERROR
|
|
390
|
+
result.passed = False
|
|
391
|
+
result.score = 0.0
|
|
392
|
+
result.error_message = f"AWS API Error: {error_code} - {str(e)}"
|
|
393
|
+
|
|
394
|
+
except Exception as e:
|
|
395
|
+
self.logger.error("waf_configuration_test_error", error=str(e))
|
|
396
|
+
result.status = TestStatus.ERROR
|
|
397
|
+
result.passed = False
|
|
398
|
+
result.score = 0.0
|
|
399
|
+
result.error_message = str(e)
|
|
400
|
+
|
|
401
|
+
return result
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
# ============================================================================
|
|
405
|
+
# CONVENIENCE FUNCTION
|
|
406
|
+
# ============================================================================
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def run_waf_configuration_test(connector: AWSConnector) -> TestResult:
|
|
410
|
+
"""Run AWS WAF configuration compliance test.
|
|
411
|
+
|
|
412
|
+
Convenience function for running the test.
|
|
413
|
+
|
|
414
|
+
Args:
|
|
415
|
+
connector: AWS connector
|
|
416
|
+
|
|
417
|
+
Returns:
|
|
418
|
+
TestResult
|
|
419
|
+
|
|
420
|
+
Example:
|
|
421
|
+
>>> from complio.connectors.aws.client import AWSConnector
|
|
422
|
+
>>> connector = AWSConnector("production", "us-east-1")
|
|
423
|
+
>>> connector.connect()
|
|
424
|
+
>>> result = run_waf_configuration_test(connector)
|
|
425
|
+
>>> print(f"Score: {result.score}%")
|
|
426
|
+
"""
|
|
427
|
+
test = WAFConfigurationTest(connector)
|
|
428
|
+
return test.execute()
|
|
File without changes
|