complio 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- CHANGELOG.md +208 -0
- README.md +343 -0
- complio/__init__.py +48 -0
- complio/cli/__init__.py +0 -0
- complio/cli/banner.py +87 -0
- complio/cli/commands/__init__.py +0 -0
- complio/cli/commands/history.py +439 -0
- complio/cli/commands/scan.py +700 -0
- complio/cli/main.py +115 -0
- complio/cli/output.py +338 -0
- complio/config/__init__.py +17 -0
- complio/config/settings.py +333 -0
- complio/connectors/__init__.py +9 -0
- complio/connectors/aws/__init__.py +0 -0
- complio/connectors/aws/client.py +342 -0
- complio/connectors/base.py +135 -0
- complio/core/__init__.py +10 -0
- complio/core/registry.py +228 -0
- complio/core/runner.py +351 -0
- complio/py.typed +0 -0
- complio/reporters/__init__.py +7 -0
- complio/reporters/generator.py +417 -0
- complio/tests_library/__init__.py +0 -0
- complio/tests_library/base.py +492 -0
- complio/tests_library/identity/__init__.py +0 -0
- complio/tests_library/identity/access_key_rotation.py +302 -0
- complio/tests_library/identity/mfa_enforcement.py +327 -0
- complio/tests_library/identity/root_account_protection.py +470 -0
- complio/tests_library/infrastructure/__init__.py +0 -0
- complio/tests_library/infrastructure/cloudtrail_encryption.py +286 -0
- complio/tests_library/infrastructure/cloudtrail_log_validation.py +274 -0
- complio/tests_library/infrastructure/cloudtrail_logging.py +400 -0
- complio/tests_library/infrastructure/ebs_encryption.py +244 -0
- complio/tests_library/infrastructure/ec2_security_groups.py +321 -0
- complio/tests_library/infrastructure/iam_password_policy.py +460 -0
- complio/tests_library/infrastructure/nacl_security.py +356 -0
- complio/tests_library/infrastructure/rds_encryption.py +252 -0
- complio/tests_library/infrastructure/s3_encryption.py +301 -0
- complio/tests_library/infrastructure/s3_public_access.py +369 -0
- complio/tests_library/infrastructure/secrets_manager_encryption.py +248 -0
- complio/tests_library/infrastructure/vpc_flow_logs.py +287 -0
- complio/tests_library/logging/__init__.py +0 -0
- complio/tests_library/logging/cloudwatch_alarms.py +354 -0
- complio/tests_library/logging/cloudwatch_logs_encryption.py +281 -0
- complio/tests_library/logging/cloudwatch_retention.py +252 -0
- complio/tests_library/logging/config_enabled.py +393 -0
- complio/tests_library/logging/eventbridge_rules.py +460 -0
- complio/tests_library/logging/guardduty_enabled.py +436 -0
- complio/tests_library/logging/security_hub_enabled.py +416 -0
- complio/tests_library/logging/sns_encryption.py +273 -0
- complio/tests_library/network/__init__.py +0 -0
- complio/tests_library/network/alb_nlb_security.py +421 -0
- complio/tests_library/network/api_gateway_security.py +452 -0
- complio/tests_library/network/cloudfront_https.py +332 -0
- complio/tests_library/network/direct_connect_security.py +343 -0
- complio/tests_library/network/nacl_configuration.py +367 -0
- complio/tests_library/network/network_firewall.py +355 -0
- complio/tests_library/network/transit_gateway_security.py +318 -0
- complio/tests_library/network/vpc_endpoints_security.py +339 -0
- complio/tests_library/network/vpn_security.py +333 -0
- complio/tests_library/network/waf_configuration.py +428 -0
- complio/tests_library/security/__init__.py +0 -0
- complio/tests_library/security/kms_key_rotation.py +314 -0
- complio/tests_library/storage/__init__.py +0 -0
- complio/tests_library/storage/backup_encryption.py +288 -0
- complio/tests_library/storage/dynamodb_encryption.py +280 -0
- complio/tests_library/storage/efs_encryption.py +257 -0
- complio/tests_library/storage/elasticache_encryption.py +370 -0
- complio/tests_library/storage/redshift_encryption.py +252 -0
- complio/tests_library/storage/s3_versioning.py +264 -0
- complio/utils/__init__.py +26 -0
- complio/utils/errors.py +179 -0
- complio/utils/exceptions.py +151 -0
- complio/utils/history.py +243 -0
- complio/utils/logger.py +391 -0
- complio-0.1.1.dist-info/METADATA +385 -0
- complio-0.1.1.dist-info/RECORD +79 -0
- complio-0.1.1.dist-info/WHEEL +4 -0
- complio-0.1.1.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,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()
|