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,452 @@
1
+ """
2
+ API Gateway security compliance test.
3
+
4
+ Checks that API Gateway APIs use secure configurations.
5
+
6
+ ISO 27001 Control: A.8.22 - Network segregation
7
+ Requirement: API Gateway APIs must use secure authentication, authorization, and encryption
8
+
9
+ Example:
10
+ >>> from complio.connectors.aws.client import AWSConnector
11
+ >>> from complio.tests_library.network.api_gateway_security import APIGatewaySecurityTest
12
+ >>>
13
+ >>> connector = AWSConnector("production", "us-east-1")
14
+ >>> connector.connect()
15
+ >>>
16
+ >>> test = APIGatewaySecurityTest(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 APIGatewaySecurityTest(ComplianceTest):
35
+ """Test for API Gateway security compliance.
36
+
37
+ Verifies that API Gateway APIs use secure configurations:
38
+ - Use authentication (API keys, IAM, Cognito, Lambda authorizers)
39
+ - Use HTTPS with TLS 1.2+
40
+ - Have access logging enabled
41
+ - Have throttling configured
42
+ - Use WAF for protection (recommended)
43
+ - Have proper resource policies (for private APIs)
44
+
45
+ Compliance Requirements:
46
+ - Authentication/authorization configured
47
+ - TLS encryption enforced
48
+ - Access logging enabled
49
+ - Throttling/rate limiting configured
50
+ - WAF protection for public APIs
51
+
52
+ Scoring:
53
+ - Based on security configuration completeness
54
+ - Multiple checks per API
55
+ - Weighted scoring
56
+
57
+ Example:
58
+ >>> test = APIGatewaySecurityTest(connector)
59
+ >>> result = test.execute()
60
+ >>> for finding in result.findings:
61
+ ... print(f"{finding.resource_id}: {finding.title}")
62
+ """
63
+
64
+ def __init__(self, connector: AWSConnector) -> None:
65
+ """Initialize API Gateway security test.
66
+
67
+ Args:
68
+ connector: AWS connector instance
69
+ """
70
+ super().__init__(
71
+ test_id="api_gateway_security",
72
+ test_name="API Gateway Security Check",
73
+ description="Verify API Gateway APIs use secure configurations",
74
+ control_id="A.8.22",
75
+ connector=connector,
76
+ scope="regional",
77
+ )
78
+
79
+ def _check_rest_api_security(self, api_id: str, api_name: str, apigateway_client: Any) -> tuple:
80
+ """Check REST API security configuration.
81
+
82
+ Args:
83
+ api_id: API Gateway REST API ID
84
+ api_name: API name
85
+ apigateway_client: API Gateway client
86
+
87
+ Returns:
88
+ Tuple of (has_issues, issues_list, severity, score_points)
89
+ """
90
+ issues = []
91
+ severity = Severity.MEDIUM
92
+ score_points = 0 # Out of 100
93
+
94
+ try:
95
+ # Get API stages
96
+ stages_response = apigateway_client.get_stages(restApiId=api_id)
97
+ stages = stages_response.get("item", [])
98
+
99
+ if not stages:
100
+ issues.append("No stages deployed")
101
+ return True, issues, Severity.HIGH, 0
102
+
103
+ # Check each stage
104
+ for stage in stages:
105
+ stage_name = stage.get("stageName", "")
106
+
107
+ # Check access logging (20 points)
108
+ access_log_settings = stage.get("accessLogSettings")
109
+ if access_log_settings and access_log_settings.get("destinationArn"):
110
+ score_points += 20
111
+ else:
112
+ issues.append(f"Stage '{stage_name}' has no access logging")
113
+
114
+ # Check throttling (20 points)
115
+ throttle_settings = stage.get("throttleSettings", {})
116
+ if throttle_settings.get("rateLimit") or throttle_settings.get("burstLimit"):
117
+ score_points += 20
118
+ else:
119
+ issues.append(f"Stage '{stage_name}' has no throttling configured")
120
+
121
+ # Check WAF association (20 points)
122
+ web_acl_arn = stage.get("webAclArn")
123
+ if web_acl_arn:
124
+ score_points += 20
125
+ else:
126
+ issues.append(f"Stage '{stage_name}' not protected by WAF")
127
+ severity = Severity.MEDIUM
128
+
129
+ # Check tracing (10 points - nice to have)
130
+ tracing_enabled = stage.get("tracingEnabled", False)
131
+ if tracing_enabled:
132
+ score_points += 10
133
+
134
+ # Check authentication at method level (30 points)
135
+ # Get resources and methods
136
+ resources_response = apigateway_client.get_resources(restApiId=api_id)
137
+ resources = resources_response.get("items", [])
138
+
139
+ has_auth = False
140
+ open_methods = []
141
+
142
+ for resource in resources:
143
+ resource_methods = resource.get("resourceMethods", {})
144
+ for method, method_data in resource_methods.items():
145
+ authorization_type = method_data.get("authorizationType", "NONE")
146
+ if authorization_type != "NONE":
147
+ has_auth = True
148
+ else:
149
+ resource_path = resource.get("path", "/")
150
+ open_methods.append(f"{method} {resource_path}")
151
+
152
+ if has_auth:
153
+ score_points += 30
154
+ else:
155
+ issues.append("No authentication configured on any methods")
156
+ severity = Severity.HIGH
157
+
158
+ if open_methods:
159
+ issues.append(f"Open methods without auth: {', '.join(open_methods[:5])}")
160
+
161
+ # Normalize score to 100
162
+ score_points = min(score_points, 100)
163
+
164
+ return len(issues) > 0, issues, severity, score_points
165
+
166
+ except ClientError as e:
167
+ error_code = e.response.get("Error", {}).get("Code")
168
+ issues.append(f"Error checking REST API: {error_code}")
169
+ return True, issues, Severity.MEDIUM, 0
170
+
171
+ def execute(self) -> TestResult:
172
+ """Execute API Gateway security compliance test.
173
+
174
+ Returns:
175
+ TestResult with findings for insecure API configurations
176
+
177
+ Example:
178
+ >>> test = APIGatewaySecurityTest(connector)
179
+ >>> result = test.execute()
180
+ >>> print(result.score)
181
+ 75.0
182
+ """
183
+ result = TestResult(
184
+ test_id=self.test_id,
185
+ test_name=self.test_name,
186
+ status=TestStatus.PASSED,
187
+ passed=True,
188
+ score=100.0,
189
+ )
190
+
191
+ try:
192
+ # Get API Gateway client
193
+ apigateway_client = self.connector.get_client("apigateway")
194
+
195
+ # List REST APIs
196
+ self.logger.info("listing_rest_apis")
197
+ rest_apis = []
198
+
199
+ paginator = apigateway_client.get_paginator("get_rest_apis")
200
+ for page in paginator.paginate():
201
+ rest_apis.extend(page.get("items", []))
202
+
203
+ if not rest_apis:
204
+ self.logger.info("no_rest_apis_found")
205
+ result.metadata["message"] = "No API Gateway REST APIs found in region"
206
+ # Not a failure, just informational
207
+ return result
208
+
209
+ self.logger.info("rest_apis_found", count=len(rest_apis))
210
+
211
+ # Check security for each API
212
+ total_score = 0
213
+ apis_checked = 0
214
+
215
+ for api in rest_apis:
216
+ api_id = api.get("id", "")
217
+ api_name = api.get("name", "")
218
+ api_endpoint = api.get("endpoint", "")
219
+
220
+ result.resources_scanned += 1
221
+ apis_checked += 1
222
+
223
+ # Check REST API security
224
+ has_issues, issues_list, severity, api_score = \
225
+ self._check_rest_api_security(api_id, api_name, apigateway_client)
226
+
227
+ total_score += api_score
228
+
229
+ # Create evidence
230
+ evidence = self.create_evidence(
231
+ resource_id=api_id,
232
+ resource_type="api_gateway_rest_api",
233
+ data={
234
+ "api_id": api_id,
235
+ "api_name": api_name,
236
+ "api_endpoint": api_endpoint,
237
+ "has_issues": has_issues,
238
+ "issues": issues_list,
239
+ "security_score": api_score,
240
+ }
241
+ )
242
+ result.add_evidence(evidence)
243
+
244
+ if has_issues:
245
+ # Create finding for insecure API
246
+ finding = self.create_finding(
247
+ resource_id=api_id,
248
+ resource_type="api_gateway_rest_api",
249
+ severity=severity,
250
+ title="API Gateway REST API has security issues",
251
+ description=f"API Gateway REST API '{api_name}' ({api_id}) has security configuration "
252
+ f"issues: {'; '.join(issues_list)}. API Gateway APIs should use authentication, "
253
+ "HTTPS encryption, access logging, throttling, and WAF protection to secure "
254
+ "access and protect against attacks. ISO 27001 A.8.22 requires secure network "
255
+ "access controls and encryption.",
256
+ remediation=(
257
+ f"Improve API Gateway REST API '{api_name}' security:\n\n"
258
+ "1. Enable access logging:\n"
259
+ "# Create CloudWatch Log Group first\n"
260
+ "aws logs create-log-group --log-group-name /aws/apigateway/myapi\n\n"
261
+ "# Enable logging on stage\n"
262
+ f"aws apigateway update-stage \\\n"
263
+ f" --rest-api-id {api_id} \\\n"
264
+ " --stage-name prod \\\n"
265
+ " --patch-operations \\\n"
266
+ ' op=replace,path=/accessLogSettings/destinationArn,value=arn:aws:logs:REGION:ACCOUNT:log-group:/aws/apigateway/myapi \\\n'
267
+ ' op=replace,path=/accessLogSettings/format,value=\'$context.requestId\'\n\n'
268
+ "2. Configure throttling:\n"
269
+ f"aws apigateway update-stage \\\n"
270
+ f" --rest-api-id {api_id} \\\n"
271
+ " --stage-name prod \\\n"
272
+ " --patch-operations \\\n"
273
+ " op=replace,path=/throttle/rateLimit,value=1000 \\\n"
274
+ " op=replace,path=/throttle/burstLimit,value=2000\n\n"
275
+ "3. Associate WAF WebACL:\n"
276
+ f"aws wafv2 associate-web-acl \\\n"
277
+ " --web-acl-arn <WAF-WEBACL-ARN> \\\n"
278
+ f" --resource-arn arn:aws:apigateway:REGION::/restapis/{api_id}/stages/prod\n\n"
279
+ "4. Configure authentication:\n"
280
+ "For IAM authentication:\n"
281
+ f"aws apigateway update-method \\\n"
282
+ f" --rest-api-id {api_id} \\\n"
283
+ " --resource-id <RESOURCE-ID> \\\n"
284
+ " --http-method GET \\\n"
285
+ " --patch-operations op=replace,path=/authorizationType,value=AWS_IAM\n\n"
286
+ "For Cognito User Pool:\n"
287
+ f"aws apigateway update-method \\\n"
288
+ f" --rest-api-id {api_id} \\\n"
289
+ " --resource-id <RESOURCE-ID> \\\n"
290
+ " --http-method GET \\\n"
291
+ " --patch-operations \\\n"
292
+ " op=replace,path=/authorizationType,value=COGNITO_USER_POOLS \\\n"
293
+ " op=replace,path=/authorizerId,value=<AUTHORIZER-ID>\n\n"
294
+ "For Lambda Authorizer:\n"
295
+ "# Create authorizer first\n"
296
+ f"aws apigateway create-authorizer \\\n"
297
+ f" --rest-api-id {api_id} \\\n"
298
+ " --name MyAuthorizer \\\n"
299
+ " --type TOKEN \\\n"
300
+ " --authorizer-uri <LAMBDA-ARN> \\\n"
301
+ " --identity-source method.request.header.Authorization\n\n"
302
+ "5. Enable X-Ray tracing:\n"
303
+ f"aws apigateway update-stage \\\n"
304
+ f" --rest-api-id {api_id} \\\n"
305
+ " --stage-name prod \\\n"
306
+ " --patch-operations op=replace,path=/tracingEnabled,value=true\n\n"
307
+ "Or use AWS Console:\n"
308
+ "1. Go to API Gateway console\n"
309
+ f"2. Select API '{api_name}'\n"
310
+ "3. Go to Stages → Select stage (e.g., 'prod')\n"
311
+ "4. Configure Logs:\n"
312
+ " - Enable CloudWatch Logs\n"
313
+ " - Enable Access Logging\n"
314
+ " - Select/create log group\n"
315
+ "5. Configure throttling:\n"
316
+ " - Set Rate: 1000 requests/sec\n"
317
+ " - Set Burst: 2000 requests\n"
318
+ "6. Associate WAF:\n"
319
+ " - Go to 'Web ACL' section\n"
320
+ " - Select WAF WebACL\n"
321
+ "7. Configure authorization:\n"
322
+ " - Go to Resources\n"
323
+ " - For each method:\n"
324
+ " • Click method (GET, POST, etc.)\n"
325
+ " • Method Request → Authorization\n"
326
+ " • Select: AWS_IAM, Cognito, or Lambda\n"
327
+ "8. Enable X-Ray tracing for debugging\n\n"
328
+ "Security best practices:\n"
329
+ "- Use API keys for basic rate limiting\n"
330
+ "- Use IAM for AWS service-to-service\n"
331
+ "- Use Cognito for user authentication\n"
332
+ "- Use Lambda authorizers for custom logic\n"
333
+ "- Enable request/response validation\n"
334
+ "- Use custom domain with ACM certificate\n"
335
+ "- Implement CORS properly\n"
336
+ "- Use resource policies for VPC endpoints\n"
337
+ "- Monitor with CloudWatch metrics and alarms\n"
338
+ "- Enable AWS WAF for DDoS and injection attacks\n"
339
+ "- Use usage plans for API quotas\n"
340
+ "- Implement proper error handling\n"
341
+ "- Use stage variables for environment config\n\n"
342
+ "Authentication options comparison:\n"
343
+ "API Keys:\n"
344
+ "- Basic identification\n"
345
+ "- Not for security (use with other auth)\n"
346
+ "- Good for usage tracking\n\n"
347
+ "IAM Authentication:\n"
348
+ "- AWS Signature Version 4\n"
349
+ "- Best for service-to-service\n"
350
+ "- Leverages AWS credentials\n\n"
351
+ "Cognito User Pools:\n"
352
+ "- OAuth 2.0 / OpenID Connect\n"
353
+ "- Best for user authentication\n"
354
+ "- Built-in user management\n\n"
355
+ "Lambda Authorizers:\n"
356
+ "- Custom authentication logic\n"
357
+ "- Flexible validation\n"
358
+ "- Can validate JWT, OAuth, etc.\n\n"
359
+ "Monitoring and alerting:\n"
360
+ "- Set CloudWatch alarms for:\n"
361
+ " • 4XX errors (client errors)\n"
362
+ " • 5XX errors (server errors)\n"
363
+ " • Latency > threshold\n"
364
+ " • Request count spikes\n"
365
+ "- Analyze access logs with Athena\n"
366
+ "- Use X-Ray for performance analysis\n"
367
+ "- Monitor WAF blocked requests"
368
+ ),
369
+ evidence=evidence
370
+ )
371
+ result.add_finding(finding)
372
+
373
+ self.logger.warning(
374
+ "api_gateway_insecure",
375
+ api_id=api_id,
376
+ api_name=api_name,
377
+ issues=issues_list
378
+ )
379
+ else:
380
+ self.logger.debug(
381
+ "api_gateway_secure",
382
+ api_id=api_id,
383
+ api_name=api_name
384
+ )
385
+
386
+ # Calculate overall compliance score
387
+ if apis_checked > 0:
388
+ result.score = total_score / apis_checked
389
+ else:
390
+ result.score = 100.0
391
+
392
+ # Determine pass/fail (require at least 70% score)
393
+ result.passed = result.score >= 70.0
394
+ result.status = TestStatus.PASSED if result.passed else TestStatus.FAILED
395
+
396
+ # Add metadata
397
+ result.metadata = {
398
+ "total_apis": len(rest_apis),
399
+ "average_security_score": result.score,
400
+ "compliance_percentage": result.score,
401
+ }
402
+
403
+ self.logger.info(
404
+ "api_gateway_security_test_completed",
405
+ total_apis=len(rest_apis),
406
+ average_score=result.score,
407
+ passed=result.passed
408
+ )
409
+
410
+ except ClientError as e:
411
+ error_code = e.response.get("Error", {}).get("Code")
412
+ self.logger.error("api_gateway_security_test_error", error_code=error_code, error=str(e))
413
+ result.status = TestStatus.ERROR
414
+ result.passed = False
415
+ result.score = 0.0
416
+ result.error_message = f"AWS API Error: {error_code} - {str(e)}"
417
+
418
+ except Exception as e:
419
+ self.logger.error("api_gateway_security_test_error", error=str(e))
420
+ result.status = TestStatus.ERROR
421
+ result.passed = False
422
+ result.score = 0.0
423
+ result.error_message = str(e)
424
+
425
+ return result
426
+
427
+
428
+ # ============================================================================
429
+ # CONVENIENCE FUNCTION
430
+ # ============================================================================
431
+
432
+
433
+ def run_api_gateway_security_test(connector: AWSConnector) -> TestResult:
434
+ """Run API Gateway security compliance test.
435
+
436
+ Convenience function for running the test.
437
+
438
+ Args:
439
+ connector: AWS connector
440
+
441
+ Returns:
442
+ TestResult
443
+
444
+ Example:
445
+ >>> from complio.connectors.aws.client import AWSConnector
446
+ >>> connector = AWSConnector("production", "us-east-1")
447
+ >>> connector.connect()
448
+ >>> result = run_api_gateway_security_test(connector)
449
+ >>> print(f"Score: {result.score}%")
450
+ """
451
+ test = APIGatewaySecurityTest(connector)
452
+ return test.execute()