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.
Files changed (79) hide show
  1. CHANGELOG.md +208 -0
  2. README.md +343 -0
  3. complio/__init__.py +48 -0
  4. complio/cli/__init__.py +0 -0
  5. complio/cli/banner.py +87 -0
  6. complio/cli/commands/__init__.py +0 -0
  7. complio/cli/commands/history.py +439 -0
  8. complio/cli/commands/scan.py +700 -0
  9. complio/cli/main.py +115 -0
  10. complio/cli/output.py +338 -0
  11. complio/config/__init__.py +17 -0
  12. complio/config/settings.py +333 -0
  13. complio/connectors/__init__.py +9 -0
  14. complio/connectors/aws/__init__.py +0 -0
  15. complio/connectors/aws/client.py +342 -0
  16. complio/connectors/base.py +135 -0
  17. complio/core/__init__.py +10 -0
  18. complio/core/registry.py +228 -0
  19. complio/core/runner.py +351 -0
  20. complio/py.typed +0 -0
  21. complio/reporters/__init__.py +7 -0
  22. complio/reporters/generator.py +417 -0
  23. complio/tests_library/__init__.py +0 -0
  24. complio/tests_library/base.py +492 -0
  25. complio/tests_library/identity/__init__.py +0 -0
  26. complio/tests_library/identity/access_key_rotation.py +302 -0
  27. complio/tests_library/identity/mfa_enforcement.py +327 -0
  28. complio/tests_library/identity/root_account_protection.py +470 -0
  29. complio/tests_library/infrastructure/__init__.py +0 -0
  30. complio/tests_library/infrastructure/cloudtrail_encryption.py +286 -0
  31. complio/tests_library/infrastructure/cloudtrail_log_validation.py +274 -0
  32. complio/tests_library/infrastructure/cloudtrail_logging.py +400 -0
  33. complio/tests_library/infrastructure/ebs_encryption.py +244 -0
  34. complio/tests_library/infrastructure/ec2_security_groups.py +321 -0
  35. complio/tests_library/infrastructure/iam_password_policy.py +460 -0
  36. complio/tests_library/infrastructure/nacl_security.py +356 -0
  37. complio/tests_library/infrastructure/rds_encryption.py +252 -0
  38. complio/tests_library/infrastructure/s3_encryption.py +301 -0
  39. complio/tests_library/infrastructure/s3_public_access.py +369 -0
  40. complio/tests_library/infrastructure/secrets_manager_encryption.py +248 -0
  41. complio/tests_library/infrastructure/vpc_flow_logs.py +287 -0
  42. complio/tests_library/logging/__init__.py +0 -0
  43. complio/tests_library/logging/cloudwatch_alarms.py +354 -0
  44. complio/tests_library/logging/cloudwatch_logs_encryption.py +281 -0
  45. complio/tests_library/logging/cloudwatch_retention.py +252 -0
  46. complio/tests_library/logging/config_enabled.py +393 -0
  47. complio/tests_library/logging/eventbridge_rules.py +460 -0
  48. complio/tests_library/logging/guardduty_enabled.py +436 -0
  49. complio/tests_library/logging/security_hub_enabled.py +416 -0
  50. complio/tests_library/logging/sns_encryption.py +273 -0
  51. complio/tests_library/network/__init__.py +0 -0
  52. complio/tests_library/network/alb_nlb_security.py +421 -0
  53. complio/tests_library/network/api_gateway_security.py +452 -0
  54. complio/tests_library/network/cloudfront_https.py +332 -0
  55. complio/tests_library/network/direct_connect_security.py +343 -0
  56. complio/tests_library/network/nacl_configuration.py +367 -0
  57. complio/tests_library/network/network_firewall.py +355 -0
  58. complio/tests_library/network/transit_gateway_security.py +318 -0
  59. complio/tests_library/network/vpc_endpoints_security.py +339 -0
  60. complio/tests_library/network/vpn_security.py +333 -0
  61. complio/tests_library/network/waf_configuration.py +428 -0
  62. complio/tests_library/security/__init__.py +0 -0
  63. complio/tests_library/security/kms_key_rotation.py +314 -0
  64. complio/tests_library/storage/__init__.py +0 -0
  65. complio/tests_library/storage/backup_encryption.py +288 -0
  66. complio/tests_library/storage/dynamodb_encryption.py +280 -0
  67. complio/tests_library/storage/efs_encryption.py +257 -0
  68. complio/tests_library/storage/elasticache_encryption.py +370 -0
  69. complio/tests_library/storage/redshift_encryption.py +252 -0
  70. complio/tests_library/storage/s3_versioning.py +264 -0
  71. complio/utils/__init__.py +26 -0
  72. complio/utils/errors.py +179 -0
  73. complio/utils/exceptions.py +151 -0
  74. complio/utils/history.py +243 -0
  75. complio/utils/logger.py +391 -0
  76. complio-0.1.1.dist-info/METADATA +385 -0
  77. complio-0.1.1.dist-info/RECORD +79 -0
  78. complio-0.1.1.dist-info/WHEEL +4 -0
  79. 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