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,355 @@
1
+ """
2
+ AWS Network Firewall compliance test.
3
+
4
+ Checks that Network Firewalls are deployed and properly configured.
5
+
6
+ ISO 27001 Control: A.8.20 - Networks security
7
+ Requirement: VPCs handling sensitive workloads should use Network Firewall
8
+
9
+ Example:
10
+ >>> from complio.connectors.aws.client import AWSConnector
11
+ >>> from complio.tests_library.network.network_firewall import NetworkFirewallTest
12
+ >>>
13
+ >>> connector = AWSConnector("production", "us-east-1")
14
+ >>> connector.connect()
15
+ >>>
16
+ >>> test = NetworkFirewallTest(connector)
17
+ >>> result = test.run()
18
+ >>> print(f"Passed: {result.passed}, Score: {result.score}")
19
+ """
20
+
21
+ from typing import Any, Dict
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 NetworkFirewallTest(ComplianceTest):
35
+ """Test for AWS Network Firewall compliance.
36
+
37
+ Verifies that Network Firewalls are deployed for VPC protection:
38
+ - Firewalls should be in READY state
39
+ - Should have logging configured
40
+ - Should have stateful and/or stateless rules configured
41
+
42
+ Compliance Requirements:
43
+ - Network Firewalls deployed for production VPCs
44
+ - Logging enabled (flow logs and/or alert logs)
45
+ - Active firewall rules configured
46
+
47
+ Scoring:
48
+ - Based on deployment and configuration status
49
+ - 100% if Network Firewalls are properly configured
50
+ - Note: This is an informational test about firewall presence
51
+
52
+ Example:
53
+ >>> test = NetworkFirewallTest(connector)
54
+ >>> result = test.execute()
55
+ >>> for finding in result.findings:
56
+ ... print(f"{finding.resource_id}: {finding.title}")
57
+ """
58
+
59
+ def __init__(self, connector: AWSConnector) -> None:
60
+ """Initialize Network Firewall test.
61
+
62
+ Args:
63
+ connector: AWS connector instance
64
+ """
65
+ super().__init__(
66
+ test_id="network_firewall",
67
+ test_name="AWS Network Firewall Check",
68
+ description="Verify Network Firewalls are deployed and properly configured",
69
+ control_id="A.8.20",
70
+ connector=connector,
71
+ scope="regional",
72
+ )
73
+
74
+ def execute(self) -> TestResult:
75
+ """Execute Network Firewall compliance test.
76
+
77
+ Returns:
78
+ TestResult with findings for missing or misconfigured firewalls
79
+
80
+ Example:
81
+ >>> test = NetworkFirewallTest(connector)
82
+ >>> result = test.execute()
83
+ >>> print(result.score)
84
+ 100.0
85
+ """
86
+ result = TestResult(
87
+ test_id=self.test_id,
88
+ test_name=self.test_name,
89
+ status=TestStatus.PASSED,
90
+ passed=True,
91
+ score=100.0,
92
+ )
93
+
94
+ try:
95
+ # Get Network Firewall client
96
+ network_firewall_client = self.connector.get_client("network-firewall")
97
+
98
+ # List all Network Firewalls
99
+ self.logger.info("listing_network_firewalls")
100
+ firewalls_response = network_firewall_client.list_firewalls()
101
+ firewalls = firewalls_response.get("Firewalls", [])
102
+
103
+ if not firewalls:
104
+ self.logger.info("no_network_firewalls_found")
105
+ result.metadata["message"] = "No Network Firewalls found in region (consider deploying for production VPCs)"
106
+ result.metadata["recommendation"] = "Deploy Network Firewall for VPCs handling sensitive workloads"
107
+ # Not a failure, just informational
108
+ return result
109
+
110
+ self.logger.info("network_firewalls_found", count=len(firewalls))
111
+
112
+ # Check configuration for each firewall
113
+ properly_configured_count = 0
114
+
115
+ for firewall_summary in firewalls:
116
+ firewall_name = firewall_summary.get("FirewallName", "")
117
+ firewall_arn = firewall_summary.get("FirewallArn", "")
118
+
119
+ result.resources_scanned += 1
120
+
121
+ # Get detailed firewall configuration
122
+ firewall_response = network_firewall_client.describe_firewall(
123
+ FirewallName=firewall_name
124
+ )
125
+
126
+ firewall = firewall_response.get("Firewall", {})
127
+ firewall_status = firewall_response.get("FirewallStatus", {})
128
+
129
+ status = firewall_status.get("Status", "")
130
+ vpc_id = firewall.get("VpcId", "")
131
+ firewall_policy_arn = firewall.get("FirewallPolicyArn", "")
132
+
133
+ # Get logging configuration
134
+ logging_response = network_firewall_client.describe_logging_configuration(
135
+ FirewallName=firewall_name
136
+ )
137
+ logging_config = logging_response.get("LoggingConfiguration", {})
138
+ log_destination_configs = logging_config.get("LogDestinationConfigs", [])
139
+
140
+ # Determine security issues
141
+ issues = []
142
+ severity = Severity.MEDIUM
143
+
144
+ # Check firewall status
145
+ if status != "READY":
146
+ issues.append(f"Firewall not in READY state (current: {status})")
147
+ severity = Severity.HIGH
148
+
149
+ # Check logging configuration
150
+ if not log_destination_configs:
151
+ issues.append("No logging configured (no alert or flow logs)")
152
+ severity = Severity.MEDIUM
153
+
154
+ # Check if firewall policy exists
155
+ if not firewall_policy_arn:
156
+ issues.append("No firewall policy configured")
157
+ severity = Severity.HIGH
158
+
159
+ # Create evidence
160
+ evidence = self.create_evidence(
161
+ resource_id=firewall_arn,
162
+ resource_type="network_firewall",
163
+ data={
164
+ "firewall_name": firewall_name,
165
+ "firewall_arn": firewall_arn,
166
+ "vpc_id": vpc_id,
167
+ "status": status,
168
+ "firewall_policy_arn": firewall_policy_arn,
169
+ "logging_configured": len(log_destination_configs) > 0,
170
+ "log_destinations_count": len(log_destination_configs),
171
+ "has_issues": len(issues) > 0,
172
+ "issues": issues,
173
+ }
174
+ )
175
+ result.add_evidence(evidence)
176
+
177
+ if len(issues) == 0:
178
+ properly_configured_count += 1
179
+ self.logger.debug(
180
+ "network_firewall_configured",
181
+ firewall_name=firewall_name
182
+ )
183
+ else:
184
+ # Create finding for misconfigured firewall
185
+ finding = self.create_finding(
186
+ resource_id=firewall_arn,
187
+ resource_type="network_firewall",
188
+ severity=severity,
189
+ title="Network Firewall has configuration issues",
190
+ description=f"Network Firewall '{firewall_name}' in VPC '{vpc_id}' has configuration issues: "
191
+ f"{'; '.join(issues)}. Network Firewalls should be in READY state, have active "
192
+ "firewall policies, and enable logging for security monitoring. ISO 27001 A.8.20 "
193
+ "requires proper network security controls.",
194
+ remediation=(
195
+ f"Improve Network Firewall '{firewall_name}' configuration:\n\n"
196
+ "1. Ensure firewall is in READY state:\n"
197
+ f"aws network-firewall describe-firewall \\\n"
198
+ f" --firewall-name {firewall_name}\n\n"
199
+ "2. Enable logging (alert and flow logs):\n"
200
+ f"aws network-firewall update-logging-configuration \\\n"
201
+ f" --firewall-name {firewall_name} \\\n"
202
+ " --logging-configuration '{\n"
203
+ ' "LogDestinationConfigs": [\n'
204
+ ' {\n'
205
+ ' "LogType": "ALERT",\n'
206
+ ' "LogDestinationType": "CloudWatchLogs",\n'
207
+ ' "LogDestination": {\n'
208
+ ' "logGroup": "/aws/network-firewall/alerts"\n'
209
+ ' }\n'
210
+ ' },\n'
211
+ ' {\n'
212
+ ' "LogType": "FLOW",\n'
213
+ ' "LogDestinationType": "S3",\n'
214
+ ' "LogDestination": {\n'
215
+ ' "bucketName": "my-firewall-logs",\n'
216
+ ' "prefix": "network-firewall/flow"\n'
217
+ ' }\n'
218
+ ' }\n'
219
+ ' ]\n'
220
+ " }'\n\n"
221
+ "3. Create and attach firewall policy with rules:\n"
222
+ "# Create stateful rule group\n"
223
+ "aws network-firewall create-rule-group \\\n"
224
+ " --rule-group-name block-malicious-domains \\\n"
225
+ " --type STATEFUL \\\n"
226
+ " --capacity 100 \\\n"
227
+ " --rule-group file://stateful-rules.json\n\n"
228
+ "# Create firewall policy\n"
229
+ "aws network-firewall create-firewall-policy \\\n"
230
+ " --firewall-policy-name my-firewall-policy \\\n"
231
+ " --firewall-policy file://policy.json\n\n"
232
+ "# Associate policy with firewall\n"
233
+ f"aws network-firewall associate-firewall-policy \\\n"
234
+ f" --firewall-name {firewall_name} \\\n"
235
+ " --firewall-policy-arn <POLICY-ARN>\n\n"
236
+ "Or use AWS Console:\n"
237
+ "1. Go to VPC console → Network Firewall → Firewalls\n"
238
+ f"2. Select firewall '{firewall_name}'\n"
239
+ "3. Verify status is READY\n"
240
+ "4. Edit firewall:\n"
241
+ " - Associate firewall policy\n"
242
+ " - Enable logging:\n"
243
+ " • Alert logs → CloudWatch Logs\n"
244
+ " • Flow logs → S3 or CloudWatch\n"
245
+ "5. Create firewall policy:\n"
246
+ " - Add stateful rule groups (domain lists, IPS rules)\n"
247
+ " - Add stateless rule groups (basic filtering)\n"
248
+ " - Set default actions\n\n"
249
+ "Security best practices:\n"
250
+ "- Deploy Network Firewall in dedicated inspection subnets\n"
251
+ "- Use stateful inspection for deep packet inspection\n"
252
+ "- Enable IPS/IDS with AWS managed rule groups\n"
253
+ "- Block known malicious domains and IPs\n"
254
+ "- Log all traffic for security analysis\n"
255
+ "- Use separate firewall policies for different zones\n"
256
+ "- Regularly update rule groups\n"
257
+ "- Monitor firewall metrics in CloudWatch\n"
258
+ "- Integrate with AWS Firewall Manager for centralized management\n"
259
+ "- Use with Transit Gateway for centralized inspection\n\n"
260
+ "Example stateful rules:\n"
261
+ "- Block malicious domains (using domain list)\n"
262
+ "- Block known malware signatures (using Suricata rules)\n"
263
+ "- Allow only specific protocols and ports\n"
264
+ "- Log and alert on suspicious patterns\n\n"
265
+ "Example deployment architectures:\n"
266
+ "1. Inspection VPC with Transit Gateway\n"
267
+ "2. Distributed firewalls in each VPC\n"
268
+ "3. Centralized egress filtering\n"
269
+ "4. East-West traffic inspection"
270
+ ),
271
+ evidence=evidence
272
+ )
273
+ result.add_finding(finding)
274
+
275
+ self.logger.warning(
276
+ "network_firewall_misconfigured",
277
+ firewall_name=firewall_name,
278
+ issues=issues
279
+ )
280
+
281
+ # Calculate compliance score
282
+ if len(firewalls) > 0:
283
+ result.score = (properly_configured_count / len(firewalls)) * 100
284
+ result.passed = properly_configured_count == len(firewalls)
285
+ else:
286
+ result.score = 100.0
287
+ result.passed = True
288
+
289
+ result.status = TestStatus.PASSED if result.passed else TestStatus.FAILED
290
+
291
+ # Add metadata
292
+ result.metadata = {
293
+ "total_firewalls": len(firewalls),
294
+ "properly_configured": properly_configured_count,
295
+ "misconfigured": len(firewalls) - properly_configured_count,
296
+ "compliance_percentage": result.score,
297
+ }
298
+
299
+ self.logger.info(
300
+ "network_firewall_test_completed",
301
+ total_firewalls=len(firewalls),
302
+ properly_configured=properly_configured_count,
303
+ score=result.score,
304
+ passed=result.passed
305
+ )
306
+
307
+ except ClientError as e:
308
+ error_code = e.response.get("Error", {}).get("Code")
309
+ # Network Firewall may not be available in all regions
310
+ if error_code in ["UnknownOperationException", "InvalidAction"]:
311
+ self.logger.info("network_firewall_not_available_in_region")
312
+ result.metadata["message"] = "AWS Network Firewall not available in this region"
313
+ return result
314
+
315
+ self.logger.error("network_firewall_test_error", error_code=error_code, error=str(e))
316
+ result.status = TestStatus.ERROR
317
+ result.passed = False
318
+ result.score = 0.0
319
+ result.error_message = f"AWS API Error: {error_code} - {str(e)}"
320
+
321
+ except Exception as e:
322
+ self.logger.error("network_firewall_test_error", error=str(e))
323
+ result.status = TestStatus.ERROR
324
+ result.passed = False
325
+ result.score = 0.0
326
+ result.error_message = str(e)
327
+
328
+ return result
329
+
330
+
331
+ # ============================================================================
332
+ # CONVENIENCE FUNCTION
333
+ # ============================================================================
334
+
335
+
336
+ def run_network_firewall_test(connector: AWSConnector) -> TestResult:
337
+ """Run AWS Network Firewall compliance test.
338
+
339
+ Convenience function for running the test.
340
+
341
+ Args:
342
+ connector: AWS connector
343
+
344
+ Returns:
345
+ TestResult
346
+
347
+ Example:
348
+ >>> from complio.connectors.aws.client import AWSConnector
349
+ >>> connector = AWSConnector("production", "us-east-1")
350
+ >>> connector.connect()
351
+ >>> result = run_network_firewall_test(connector)
352
+ >>> print(f"Score: {result.score}%")
353
+ """
354
+ test = NetworkFirewallTest(connector)
355
+ return test.execute()
@@ -0,0 +1,318 @@
1
+ """
2
+ Transit Gateway security compliance test.
3
+
4
+ Checks that Transit Gateways use secure configurations.
5
+
6
+ ISO 27001 Control: A.8.22 - Network segregation
7
+ Requirement: Transit Gateways must have proper route tables and security controls
8
+
9
+ Example:
10
+ >>> from complio.connectors.aws.client import AWSConnector
11
+ >>> from complio.tests_library.network.transit_gateway_security import TransitGatewaySecurityTest
12
+ >>>
13
+ >>> connector = AWSConnector("production", "us-east-1")
14
+ >>> connector.connect()
15
+ >>>
16
+ >>> test = TransitGatewaySecurityTest(connector)
17
+ >>> result = test.run()
18
+ >>> print(f"Passed: {result.passed}, Score: {result.score}")
19
+ """
20
+
21
+ from typing import Any, Dict
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 TransitGatewaySecurityTest(ComplianceTest):
35
+ """Test for Transit Gateway security compliance.
36
+
37
+ Verifies that Transit Gateways use secure configurations:
38
+ - Default route table association should be disabled (explicit control)
39
+ - Default route table propagation should be disabled
40
+ - Auto-accept shared attachments should be disabled
41
+ - DNS support and VPN ECMP support configured appropriately
42
+
43
+ Compliance Requirements:
44
+ - DefaultRouteTableAssociation should be 'disable'
45
+ - DefaultRouteTablePropagation should be 'disable'
46
+ - AutoAcceptSharedAttachments should be 'disable'
47
+ - Proper isolation between VPC attachments
48
+
49
+ Scoring:
50
+ - 100% if all Transit Gateways follow security best practices
51
+ - Proportional score based on compliant/total ratio
52
+ - 100% if no Transit Gateways exist
53
+
54
+ Example:
55
+ >>> test = TransitGatewaySecurityTest(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 Transit Gateway security test.
63
+
64
+ Args:
65
+ connector: AWS connector instance
66
+ """
67
+ super().__init__(
68
+ test_id="transit_gateway_security",
69
+ test_name="Transit Gateway Security Check",
70
+ description="Verify Transit Gateways use secure configurations",
71
+ control_id="A.8.22",
72
+ connector=connector,
73
+ scope="regional",
74
+ )
75
+
76
+ def execute(self) -> TestResult:
77
+ """Execute Transit Gateway security compliance test.
78
+
79
+ Returns:
80
+ TestResult with findings for insecure Transit Gateways
81
+
82
+ Example:
83
+ >>> test = TransitGatewaySecurityTest(connector)
84
+ >>> result = test.execute()
85
+ >>> print(result.score)
86
+ 100.0
87
+ """
88
+ result = TestResult(
89
+ test_id=self.test_id,
90
+ test_name=self.test_name,
91
+ status=TestStatus.PASSED,
92
+ passed=True,
93
+ score=100.0,
94
+ )
95
+
96
+ try:
97
+ # Get EC2 client
98
+ ec2_client = self.connector.get_client("ec2")
99
+
100
+ # List all Transit Gateways
101
+ self.logger.info("listing_transit_gateways")
102
+ tgw_response = ec2_client.describe_transit_gateways()
103
+ transit_gateways = tgw_response.get("TransitGateways", [])
104
+
105
+ if not transit_gateways:
106
+ self.logger.info("no_transit_gateways_found")
107
+ result.metadata["message"] = "No Transit Gateways found in region"
108
+ return result
109
+
110
+ self.logger.info("transit_gateways_found", count=len(transit_gateways))
111
+
112
+ # Check security for each Transit Gateway
113
+ secure_tgw_count = 0
114
+
115
+ for tgw in transit_gateways:
116
+ tgw_id = tgw["TransitGatewayId"]
117
+ tgw_state = tgw.get("State", "")
118
+ tgw_arn = tgw.get("TransitGatewayArn", "")
119
+
120
+ # Skip deleted/deleting transit gateways
121
+ if tgw_state in ["deleted", "deleting"]:
122
+ continue
123
+
124
+ result.resources_scanned += 1
125
+
126
+ # Get Transit Gateway options
127
+ options = tgw.get("Options", {})
128
+ default_route_table_association = options.get("DefaultRouteTableAssociation", "enable")
129
+ default_route_table_propagation = options.get("DefaultRouteTablePropagation", "enable")
130
+ auto_accept_shared_attachments = options.get("AutoAcceptSharedAttachments", "enable")
131
+ dns_support = options.get("DnsSupport", "enable")
132
+
133
+ # Determine security issues
134
+ issues = []
135
+ severity = Severity.MEDIUM
136
+
137
+ # Check default route table association (should be disabled for security)
138
+ if default_route_table_association == "enable":
139
+ issues.append("Default route table association enabled (should be disabled for explicit control)")
140
+ severity = Severity.MEDIUM
141
+
142
+ # Check default route table propagation
143
+ if default_route_table_propagation == "enable":
144
+ issues.append("Default route table propagation enabled (should be disabled for explicit control)")
145
+ severity = Severity.MEDIUM
146
+
147
+ # Check auto-accept shared attachments (security risk)
148
+ if auto_accept_shared_attachments == "enable":
149
+ issues.append("Auto-accept shared attachments enabled (security risk)")
150
+ severity = Severity.HIGH
151
+
152
+ # Create evidence
153
+ evidence = self.create_evidence(
154
+ resource_id=tgw_id,
155
+ resource_type="transit_gateway",
156
+ data={
157
+ "transit_gateway_id": tgw_id,
158
+ "transit_gateway_arn": tgw_arn,
159
+ "state": tgw_state,
160
+ "default_route_table_association": default_route_table_association,
161
+ "default_route_table_propagation": default_route_table_propagation,
162
+ "auto_accept_shared_attachments": auto_accept_shared_attachments,
163
+ "dns_support": dns_support,
164
+ "has_issues": len(issues) > 0,
165
+ "issues": issues,
166
+ }
167
+ )
168
+ result.add_evidence(evidence)
169
+
170
+ if len(issues) == 0:
171
+ secure_tgw_count += 1
172
+ self.logger.debug(
173
+ "transit_gateway_secure",
174
+ tgw_id=tgw_id
175
+ )
176
+ else:
177
+ # Create finding for insecure Transit Gateway
178
+ finding = self.create_finding(
179
+ resource_id=tgw_id,
180
+ resource_type="transit_gateway",
181
+ severity=severity,
182
+ title="Transit Gateway has security configuration issues",
183
+ description=f"Transit Gateway '{tgw_id}' has security configuration issues: "
184
+ f"{'; '.join(issues)}. Transit Gateways should use explicit route table "
185
+ "associations and propagations for better security control, and should not "
186
+ "auto-accept shared attachments to prevent unauthorized network access. "
187
+ "ISO 27001 A.8.22 requires proper network segregation and security controls.",
188
+ remediation=(
189
+ f"Improve Transit Gateway '{tgw_id}' security configuration:\n\n"
190
+ "1. Disable default route table association:\n"
191
+ f"aws ec2 modify-transit-gateway \\\n"
192
+ f" --transit-gateway-id {tgw_id} \\\n"
193
+ " --options DefaultRouteTableAssociation=disable\n\n"
194
+ "2. Disable default route table propagation:\n"
195
+ f"aws ec2 modify-transit-gateway \\\n"
196
+ f" --transit-gateway-id {tgw_id} \\\n"
197
+ " --options DefaultRouteTablePropagation=disable\n\n"
198
+ "3. Disable auto-accept shared attachments:\n"
199
+ f"aws ec2 modify-transit-gateway \\\n"
200
+ f" --transit-gateway-id {tgw_id} \\\n"
201
+ " --options AutoAcceptSharedAttachments=disable\n\n"
202
+ "4. Explicitly manage route tables:\n"
203
+ "# Create dedicated route tables for different environments\n"
204
+ f"aws ec2 create-transit-gateway-route-table \\\n"
205
+ f" --transit-gateway-id {tgw_id} \\\n"
206
+ " --tag-specifications 'ResourceType=transit-gateway-route-table,Tags=[{Key=Name,Value=Production}]'\n\n"
207
+ "# Associate VPC attachments explicitly\n"
208
+ "aws ec2 associate-transit-gateway-route-table \\\n"
209
+ " --transit-gateway-route-table-id <TGW-RT-ID> \\\n"
210
+ " --transit-gateway-attachment-id <TGW-ATTACHMENT-ID>\n\n"
211
+ "Or use AWS Console:\n"
212
+ "1. Go to VPC console → Transit Gateways\n"
213
+ f"2. Select Transit Gateway '{tgw_id}'\n"
214
+ "3. Actions → Modify Transit Gateway\n"
215
+ "4. Disable:\n"
216
+ " - Default route table association\n"
217
+ " - Default route table propagation\n"
218
+ " - Auto accept shared attachments\n"
219
+ "5. Click 'Modify'\n"
220
+ "6. Go to 'Route Tables' tab\n"
221
+ "7. Create dedicated route tables for:\n"
222
+ " - Production VPCs\n"
223
+ " - Development VPCs\n"
224
+ " - Shared services VPCs\n"
225
+ "8. Manually associate each VPC attachment to appropriate route table\n\n"
226
+ "Security best practices:\n"
227
+ "- Use separate route tables for different security zones\n"
228
+ "- Implement hub-and-spoke topology for centralized control\n"
229
+ "- Use blackhole routes to block unwanted traffic\n"
230
+ "- Enable VPC Flow Logs on attached VPCs\n"
231
+ "- Monitor Transit Gateway metrics in CloudWatch\n"
232
+ "- Use AWS Resource Access Manager (RAM) for controlled sharing\n"
233
+ "- Implement least privilege routing (only allow necessary routes)\n"
234
+ "- Tag route tables and attachments for easy management\n"
235
+ "- Regularly audit route table configurations\n"
236
+ "- Use AWS Network Firewall with Transit Gateway for inspection\n\n"
237
+ "Network isolation strategies:\n"
238
+ "- Production-to-production: Allow\n"
239
+ "- Production-to-dev: Deny (use separate route tables)\n"
240
+ "- Shared-services-to-all: Allow (DNS, AD, etc.)\n"
241
+ "- Internet-egress: Centralize through inspection VPC"
242
+ ),
243
+ evidence=evidence
244
+ )
245
+ result.add_finding(finding)
246
+
247
+ self.logger.warning(
248
+ "transit_gateway_insecure",
249
+ tgw_id=tgw_id,
250
+ issues=issues
251
+ )
252
+
253
+ # Calculate compliance score
254
+ result.score = (secure_tgw_count / len(transit_gateways)) * 100
255
+
256
+ # Determine pass/fail
257
+ result.passed = secure_tgw_count == len(transit_gateways)
258
+ result.status = TestStatus.PASSED if result.passed else TestStatus.FAILED
259
+
260
+ # Add metadata
261
+ result.metadata = {
262
+ "total_transit_gateways": len(transit_gateways),
263
+ "secure_transit_gateways": secure_tgw_count,
264
+ "insecure_transit_gateways": len(transit_gateways) - secure_tgw_count,
265
+ "compliance_percentage": result.score,
266
+ }
267
+
268
+ self.logger.info(
269
+ "transit_gateway_security_test_completed",
270
+ total_transit_gateways=len(transit_gateways),
271
+ secure=secure_tgw_count,
272
+ score=result.score,
273
+ passed=result.passed
274
+ )
275
+
276
+ except ClientError as e:
277
+ error_code = e.response.get("Error", {}).get("Code")
278
+ self.logger.error("transit_gateway_security_test_error", error_code=error_code, error=str(e))
279
+ result.status = TestStatus.ERROR
280
+ result.passed = False
281
+ result.score = 0.0
282
+ result.error_message = f"AWS API Error: {error_code} - {str(e)}"
283
+
284
+ except Exception as e:
285
+ self.logger.error("transit_gateway_security_test_error", error=str(e))
286
+ result.status = TestStatus.ERROR
287
+ result.passed = False
288
+ result.score = 0.0
289
+ result.error_message = str(e)
290
+
291
+ return result
292
+
293
+
294
+ # ============================================================================
295
+ # CONVENIENCE FUNCTION
296
+ # ============================================================================
297
+
298
+
299
+ def run_transit_gateway_security_test(connector: AWSConnector) -> TestResult:
300
+ """Run Transit Gateway security compliance test.
301
+
302
+ Convenience function for running the test.
303
+
304
+ Args:
305
+ connector: AWS connector
306
+
307
+ Returns:
308
+ TestResult
309
+
310
+ Example:
311
+ >>> from complio.connectors.aws.client import AWSConnector
312
+ >>> connector = AWSConnector("production", "us-east-1")
313
+ >>> connector.connect()
314
+ >>> result = run_transit_gateway_security_test(connector)
315
+ >>> print(f"Score: {result.score}%")
316
+ """
317
+ test = TransitGatewaySecurityTest(connector)
318
+ return test.execute()