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,248 @@
1
+ """
2
+ Secrets Manager encryption compliance test.
3
+
4
+ Checks that all secrets use customer-managed KMS keys for encryption.
5
+
6
+ ISO 27001 Control: A.8.24 - Use of cryptography
7
+ Requirement: Sensitive data must be encrypted with customer-controlled keys
8
+
9
+ Example:
10
+ >>> from complio.connectors.aws.client import AWSConnector
11
+ >>> from complio.tests_library.infrastructure.secrets_manager_encryption import SecretsManagerEncryptionTest
12
+ >>>
13
+ >>> connector = AWSConnector("production", "us-east-1")
14
+ >>> connector.connect()
15
+ >>>
16
+ >>> test = SecretsManagerEncryptionTest(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 SecretsManagerEncryptionTest(ComplianceTest):
35
+ """Test for Secrets Manager encryption compliance.
36
+
37
+ Verifies that all secrets use customer-managed KMS keys for encryption
38
+ rather than the default AWS-managed key.
39
+
40
+ Compliance Requirements:
41
+ - All secrets should use customer-managed KMS keys
42
+ - AWS-managed keys provide less control over rotation and access
43
+ - Secrets without custom KMS keys are flagged as medium severity
44
+
45
+ Scoring:
46
+ - 100% if all secrets use customer-managed keys
47
+ - Proportional score based on compliant/total ratio
48
+ - 0% if no secrets use customer-managed keys
49
+
50
+ Example:
51
+ >>> test = SecretsManagerEncryptionTest(connector)
52
+ >>> result = test.execute()
53
+ >>> for finding in result.findings:
54
+ ... print(f"{finding.resource_id}: {finding.title}")
55
+ """
56
+
57
+ def __init__(self, connector: AWSConnector) -> None:
58
+ """Initialize Secrets Manager encryption test.
59
+
60
+ Args:
61
+ connector: AWS connector instance
62
+ """
63
+ super().__init__(
64
+ test_id="secrets_manager_encryption",
65
+ test_name="Secrets Manager KMS Encryption Check",
66
+ description="Verify all secrets use customer-managed KMS keys for encryption",
67
+ control_id="A.8.24",
68
+ connector=connector,
69
+ scope="regional",
70
+ )
71
+
72
+ def execute(self) -> TestResult:
73
+ """Execute Secrets Manager encryption compliance test.
74
+
75
+ Returns:
76
+ TestResult with findings for secrets using AWS-managed keys
77
+
78
+ Example:
79
+ >>> test = SecretsManagerEncryptionTest(connector)
80
+ >>> result = test.execute()
81
+ >>> print(result.score)
82
+ 85.0
83
+ """
84
+ result = TestResult(
85
+ test_id=self.test_id,
86
+ test_name=self.test_name,
87
+ status=TestStatus.PASSED,
88
+ passed=True,
89
+ score=100.0,
90
+ )
91
+
92
+ try:
93
+ # Get Secrets Manager client
94
+ sm_client = self.connector.get_client("secretsmanager")
95
+
96
+ # List all secrets
97
+ self.logger.info("listing_secrets")
98
+ secrets = []
99
+ paginator = sm_client.get_paginator("list_secrets")
100
+
101
+ for page in paginator.paginate():
102
+ secrets.extend(page.get("SecretList", []))
103
+
104
+ if not secrets:
105
+ self.logger.info("no_secrets_found")
106
+ result.metadata["message"] = "No secrets found in region"
107
+ return result
108
+
109
+ self.logger.info("secrets_found", count=len(secrets))
110
+
111
+ # Check each secret for customer-managed KMS key
112
+ compliant_count = 0
113
+ total_count = len(secrets)
114
+
115
+ for secret in secrets:
116
+ secret_name = secret["Name"]
117
+ secret_arn = secret["ARN"]
118
+ kms_key_id = secret.get("KmsKeyId")
119
+ result.resources_scanned += 1
120
+
121
+ # Check if using customer-managed key
122
+ # If KmsKeyId is not present or is alias/aws/secretsmanager, it's using AWS-managed key
123
+ using_customer_key = (
124
+ kms_key_id is not None and
125
+ "alias/aws/secretsmanager" not in str(kms_key_id) and
126
+ kms_key_id != ""
127
+ )
128
+
129
+ # Create evidence
130
+ evidence = self.create_evidence(
131
+ resource_id=secret_name,
132
+ resource_type="secrets_manager_secret",
133
+ data={
134
+ "secret_name": secret_name,
135
+ "secret_arn": secret_arn,
136
+ "kms_key_id": kms_key_id,
137
+ "using_customer_managed_key": using_customer_key,
138
+ "last_changed_date": secret.get("LastChangedDate").isoformat() if secret.get("LastChangedDate") else None,
139
+ "last_accessed_date": secret.get("LastAccessedDate").isoformat() if secret.get("LastAccessedDate") else None,
140
+ }
141
+ )
142
+ result.add_evidence(evidence)
143
+
144
+ if using_customer_key:
145
+ compliant_count += 1
146
+ self.logger.debug(
147
+ "secret_using_customer_key",
148
+ secret_name=secret_name,
149
+ kms_key_id=kms_key_id
150
+ )
151
+ else:
152
+ # Create finding for secret using AWS-managed key
153
+ finding = self.create_finding(
154
+ resource_id=secret_name,
155
+ resource_type="secrets_manager_secret",
156
+ severity=Severity.MEDIUM,
157
+ title="Secret using AWS-managed KMS key",
158
+ description=f"Secret '{secret_name}' is using the default AWS-managed KMS key instead of a "
159
+ "customer-managed key. This reduces control over key rotation, access policies, "
160
+ "and audit trails. ISO 27001 A.8.24 recommends customer-controlled encryption keys.",
161
+ remediation=(
162
+ "Update the secret to use a customer-managed KMS key:\n"
163
+ "1. Create a customer-managed KMS key (if not already created):\n"
164
+ " aws kms create-key --description 'Secrets Manager encryption key'\n"
165
+ "2. Update the secret to use the customer-managed key:\n"
166
+ f" aws secretsmanager update-secret --secret-id {secret_name} "
167
+ "--kms-key-id <your-kms-key-id>\n\n"
168
+ "Note: Ensure your IAM roles/users have permission to use the KMS key.\n"
169
+ "Add a key policy that allows the Secrets Manager service to use the key."
170
+ ),
171
+ evidence=evidence
172
+ )
173
+ result.add_finding(finding)
174
+
175
+ self.logger.warning(
176
+ "secret_using_aws_managed_key",
177
+ secret_name=secret_name,
178
+ kms_key_id=kms_key_id
179
+ )
180
+
181
+ # Calculate compliance score
182
+ if total_count > 0:
183
+ result.score = (compliant_count / total_count) * 100
184
+
185
+ # Determine pass/fail
186
+ result.passed = compliant_count == total_count
187
+ result.status = TestStatus.PASSED if result.passed else TestStatus.FAILED
188
+
189
+ # Add metadata
190
+ result.metadata = {
191
+ "total_secrets": total_count,
192
+ "secrets_with_customer_keys": compliant_count,
193
+ "secrets_with_aws_managed_keys": total_count - compliant_count,
194
+ "compliance_percentage": result.score,
195
+ "region": self.connector.region,
196
+ }
197
+
198
+ self.logger.info(
199
+ "secrets_manager_encryption_test_completed",
200
+ total=total_count,
201
+ compliant=compliant_count,
202
+ score=result.score,
203
+ passed=result.passed
204
+ )
205
+
206
+ except ClientError as e:
207
+ error_code = e.response.get("Error", {}).get("Code")
208
+ self.logger.error("secrets_manager_test_error", error_code=error_code, error=str(e))
209
+ result.status = TestStatus.ERROR
210
+ result.passed = False
211
+ result.score = 0.0
212
+ result.error_message = f"AWS API Error: {error_code} - {str(e)}"
213
+
214
+ except Exception as e:
215
+ self.logger.error("secrets_manager_test_error", error=str(e))
216
+ result.status = TestStatus.ERROR
217
+ result.passed = False
218
+ result.score = 0.0
219
+ result.error_message = str(e)
220
+
221
+ return result
222
+
223
+
224
+ # ============================================================================
225
+ # CONVENIENCE FUNCTION
226
+ # ============================================================================
227
+
228
+
229
+ def run_secrets_manager_encryption_test(connector: AWSConnector) -> TestResult:
230
+ """Run Secrets Manager encryption compliance test.
231
+
232
+ Convenience function for running the test.
233
+
234
+ Args:
235
+ connector: AWS connector
236
+
237
+ Returns:
238
+ TestResult
239
+
240
+ Example:
241
+ >>> from complio.connectors.aws.client import AWSConnector
242
+ >>> connector = AWSConnector("production", "us-east-1")
243
+ >>> connector.connect()
244
+ >>> result = run_secrets_manager_encryption_test(connector)
245
+ >>> print(f"Score: {result.score}%")
246
+ """
247
+ test = SecretsManagerEncryptionTest(connector)
248
+ return test.execute()
@@ -0,0 +1,287 @@
1
+ """
2
+ VPC Flow Logs compliance test.
3
+
4
+ Checks that all VPCs have flow logs enabled for network traffic monitoring.
5
+
6
+ ISO 27001 Control: A.8.16 - Monitoring of network traffic
7
+ Requirement: Network traffic must be logged for security monitoring
8
+
9
+ Example:
10
+ >>> from complio.connectors.aws.client import AWSConnector
11
+ >>> from complio.tests_library.infrastructure.vpc_flow_logs import VPCFlowLogsTest
12
+ >>>
13
+ >>> connector = AWSConnector("production", "us-east-1")
14
+ >>> connector.connect()
15
+ >>>
16
+ >>> test = VPCFlowLogsTest(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 VPCFlowLogsTest(ComplianceTest):
35
+ """Test for VPC Flow Logs compliance.
36
+
37
+ Verifies that all VPCs have flow logs enabled to capture network
38
+ traffic information for security monitoring and troubleshooting.
39
+
40
+ Compliance Requirements:
41
+ - Each VPC must have at least one flow log configured
42
+ - Flow logs should capture accepted, rejected, or all traffic
43
+ - VPCs without flow logs are non-compliant
44
+
45
+ Scoring:
46
+ - 100% if all VPCs have flow logs
47
+ - Proportional score based on compliant/total ratio
48
+ - 0% if no VPCs have flow logs
49
+
50
+ Example:
51
+ >>> test = VPCFlowLogsTest(connector)
52
+ >>> result = test.execute()
53
+ >>> for finding in result.findings:
54
+ ... print(f"{finding.resource_id}: {finding.title}")
55
+ """
56
+
57
+ def __init__(self, connector: AWSConnector) -> None:
58
+ """Initialize VPC Flow Logs test.
59
+
60
+ Args:
61
+ connector: AWS connector instance
62
+ """
63
+ super().__init__(
64
+ test_id="vpc_flow_logs",
65
+ test_name="VPC Flow Logs Check",
66
+ description="Verify all VPCs have flow logs enabled for network traffic monitoring",
67
+ control_id="A.8.16",
68
+ connector=connector,
69
+ scope="regional",
70
+ )
71
+
72
+ def execute(self) -> TestResult:
73
+ """Execute VPC Flow Logs compliance test.
74
+
75
+ Returns:
76
+ TestResult with findings for VPCs without flow logs
77
+
78
+ Example:
79
+ >>> test = VPCFlowLogsTest(connector)
80
+ >>> result = test.execute()
81
+ >>> print(result.score)
82
+ 80.0
83
+ """
84
+ result = TestResult(
85
+ test_id=self.test_id,
86
+ test_name=self.test_name,
87
+ status=TestStatus.PASSED,
88
+ passed=True,
89
+ score=100.0,
90
+ )
91
+
92
+ try:
93
+ # Get EC2 client
94
+ ec2_client = self.connector.get_client("ec2")
95
+
96
+ # List all VPCs
97
+ self.logger.info("listing_vpcs")
98
+ vpcs_response = ec2_client.describe_vpcs()
99
+ vpcs = vpcs_response.get("Vpcs", [])
100
+
101
+ if not vpcs:
102
+ self.logger.info("no_vpcs_found")
103
+ result.metadata["message"] = "No VPCs found in region"
104
+ return result
105
+
106
+ self.logger.info("vpcs_found", count=len(vpcs))
107
+
108
+ # Get all flow logs in the region
109
+ self.logger.info("listing_flow_logs")
110
+ flow_logs_response = ec2_client.describe_flow_logs()
111
+ flow_logs = flow_logs_response.get("FlowLogs", [])
112
+
113
+ # Create a map of VPC ID -> flow logs
114
+ vpc_flow_logs_map: Dict[str, List[Dict[str, Any]]] = {}
115
+ for flow_log in flow_logs:
116
+ resource_id = flow_log.get("ResourceId")
117
+ if resource_id and resource_id.startswith("vpc-"):
118
+ if resource_id not in vpc_flow_logs_map:
119
+ vpc_flow_logs_map[resource_id] = []
120
+ vpc_flow_logs_map[resource_id].append(flow_log)
121
+
122
+ # Check each VPC for flow logs
123
+ compliant_count = 0
124
+ total_count = len(vpcs)
125
+
126
+ for vpc in vpcs:
127
+ vpc_id = vpc["VpcId"]
128
+ is_default = vpc.get("IsDefault", False)
129
+ cidr_block = vpc.get("CidrBlock", "unknown")
130
+ result.resources_scanned += 1
131
+
132
+ # Get VPC name from tags
133
+ vpc_name = "unnamed"
134
+ for tag in vpc.get("Tags", []):
135
+ if tag.get("Key") == "Name":
136
+ vpc_name = tag.get("Value", "unnamed")
137
+ break
138
+
139
+ # Check if VPC has flow logs
140
+ vpc_flow_logs = vpc_flow_logs_map.get(vpc_id, [])
141
+ has_flow_logs = len(vpc_flow_logs) > 0
142
+
143
+ # Create evidence
144
+ evidence = self.create_evidence(
145
+ resource_id=vpc_id,
146
+ resource_type="vpc",
147
+ data={
148
+ "vpc_id": vpc_id,
149
+ "vpc_name": vpc_name,
150
+ "is_default": is_default,
151
+ "cidr_block": cidr_block,
152
+ "has_flow_logs": has_flow_logs,
153
+ "flow_logs_count": len(vpc_flow_logs),
154
+ "flow_logs": [
155
+ {
156
+ "flow_log_id": fl.get("FlowLogId"),
157
+ "traffic_type": fl.get("TrafficType"),
158
+ "log_destination_type": fl.get("LogDestinationType"),
159
+ "log_destination": fl.get("LogDestination"),
160
+ "log_group_name": fl.get("LogGroupName"),
161
+ }
162
+ for fl in vpc_flow_logs
163
+ ] if vpc_flow_logs else [],
164
+ }
165
+ )
166
+ result.add_evidence(evidence)
167
+
168
+ if has_flow_logs:
169
+ compliant_count += 1
170
+ self.logger.debug(
171
+ "vpc_has_flow_logs",
172
+ vpc_id=vpc_id,
173
+ vpc_name=vpc_name,
174
+ flow_logs_count=len(vpc_flow_logs)
175
+ )
176
+ else:
177
+ # Create finding for VPC without flow logs
178
+ finding = self.create_finding(
179
+ resource_id=vpc_id,
180
+ resource_type="vpc",
181
+ severity=Severity.HIGH,
182
+ title="VPC Flow Logs not enabled",
183
+ description=f"VPC '{vpc_name}' ({vpc_id}, {cidr_block}) does not have flow logs enabled. "
184
+ "Flow logs capture information about IP traffic going to and from network interfaces. "
185
+ "Without flow logs, network security monitoring and troubleshooting are severely limited. "
186
+ "This violates ISO 27001 A.8.16 requirement for network traffic monitoring.",
187
+ remediation=(
188
+ f"Enable VPC Flow Logs for VPC '{vpc_id}':\n"
189
+ "1. Create a CloudWatch log group (if using CloudWatch Logs):\n"
190
+ f" aws logs create-log-group --log-group-name /aws/vpc/flowlogs/{vpc_id}\n"
191
+ "2. Create an IAM role for flow logs:\n"
192
+ " (See AWS documentation for required trust policy and permissions)\n"
193
+ "3. Create the flow log:\n"
194
+ f" aws ec2 create-flow-logs --resource-type VPC \\\n"
195
+ f" --resource-ids {vpc_id} \\\n"
196
+ " --traffic-type ALL \\\n"
197
+ " --log-destination-type cloud-watch-logs \\\n"
198
+ f" --log-group-name /aws/vpc/flowlogs/{vpc_id} \\\n"
199
+ " --deliver-logs-permission-arn <iam-role-arn>\n\n"
200
+ "Or use AWS Console:\n"
201
+ "1. Go to VPC → Your VPCs\n"
202
+ f"2. Select VPC '{vpc_id}'\n"
203
+ "3. Actions → Create flow log\n"
204
+ "4. Configure destination (CloudWatch Logs or S3)\n"
205
+ "5. Set traffic type to 'All'\n"
206
+ "6. Click Create flow log"
207
+ ),
208
+ evidence=evidence
209
+ )
210
+ result.add_finding(finding)
211
+
212
+ self.logger.warning(
213
+ "vpc_without_flow_logs",
214
+ vpc_id=vpc_id,
215
+ vpc_name=vpc_name,
216
+ is_default=is_default
217
+ )
218
+
219
+ # Calculate compliance score
220
+ if total_count > 0:
221
+ result.score = (compliant_count / total_count) * 100
222
+
223
+ # Determine pass/fail
224
+ result.passed = compliant_count == total_count
225
+ result.status = TestStatus.PASSED if result.passed else TestStatus.FAILED
226
+
227
+ # Add metadata
228
+ result.metadata = {
229
+ "total_vpcs": total_count,
230
+ "vpcs_with_flow_logs": compliant_count,
231
+ "vpcs_without_flow_logs": total_count - compliant_count,
232
+ "total_flow_logs": len(flow_logs),
233
+ "compliance_percentage": result.score,
234
+ "region": self.connector.region,
235
+ }
236
+
237
+ self.logger.info(
238
+ "vpc_flow_logs_test_completed",
239
+ total=total_count,
240
+ compliant=compliant_count,
241
+ score=result.score,
242
+ passed=result.passed
243
+ )
244
+
245
+ except ClientError as e:
246
+ error_code = e.response.get("Error", {}).get("Code")
247
+ self.logger.error("vpc_flow_logs_test_error", error_code=error_code, error=str(e))
248
+ result.status = TestStatus.ERROR
249
+ result.passed = False
250
+ result.score = 0.0
251
+ result.error_message = f"AWS API Error: {error_code} - {str(e)}"
252
+
253
+ except Exception as e:
254
+ self.logger.error("vpc_flow_logs_test_error", error=str(e))
255
+ result.status = TestStatus.ERROR
256
+ result.passed = False
257
+ result.score = 0.0
258
+ result.error_message = str(e)
259
+
260
+ return result
261
+
262
+
263
+ # ============================================================================
264
+ # CONVENIENCE FUNCTION
265
+ # ============================================================================
266
+
267
+
268
+ def run_vpc_flow_logs_test(connector: AWSConnector) -> TestResult:
269
+ """Run VPC Flow Logs compliance test.
270
+
271
+ Convenience function for running the test.
272
+
273
+ Args:
274
+ connector: AWS connector
275
+
276
+ Returns:
277
+ TestResult
278
+
279
+ Example:
280
+ >>> from complio.connectors.aws.client import AWSConnector
281
+ >>> connector = AWSConnector("production", "us-east-1")
282
+ >>> connector.connect()
283
+ >>> result = run_vpc_flow_logs_test(connector)
284
+ >>> print(f"Score: {result.score}%")
285
+ """
286
+ test = VPCFlowLogsTest(connector)
287
+ return test.execute()
File without changes