aws-cis-controls-assessment 1.0.3__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 (77) hide show
  1. aws_cis_assessment/__init__.py +11 -0
  2. aws_cis_assessment/cli/__init__.py +3 -0
  3. aws_cis_assessment/cli/examples.py +274 -0
  4. aws_cis_assessment/cli/main.py +1259 -0
  5. aws_cis_assessment/cli/utils.py +356 -0
  6. aws_cis_assessment/config/__init__.py +1 -0
  7. aws_cis_assessment/config/config_loader.py +328 -0
  8. aws_cis_assessment/config/rules/cis_controls_ig1.yaml +590 -0
  9. aws_cis_assessment/config/rules/cis_controls_ig2.yaml +412 -0
  10. aws_cis_assessment/config/rules/cis_controls_ig3.yaml +100 -0
  11. aws_cis_assessment/controls/__init__.py +1 -0
  12. aws_cis_assessment/controls/base_control.py +400 -0
  13. aws_cis_assessment/controls/ig1/__init__.py +239 -0
  14. aws_cis_assessment/controls/ig1/control_1_1.py +586 -0
  15. aws_cis_assessment/controls/ig1/control_2_2.py +231 -0
  16. aws_cis_assessment/controls/ig1/control_3_3.py +718 -0
  17. aws_cis_assessment/controls/ig1/control_3_4.py +235 -0
  18. aws_cis_assessment/controls/ig1/control_4_1.py +461 -0
  19. aws_cis_assessment/controls/ig1/control_access_keys.py +310 -0
  20. aws_cis_assessment/controls/ig1/control_advanced_security.py +512 -0
  21. aws_cis_assessment/controls/ig1/control_backup_recovery.py +510 -0
  22. aws_cis_assessment/controls/ig1/control_cloudtrail_logging.py +197 -0
  23. aws_cis_assessment/controls/ig1/control_critical_security.py +422 -0
  24. aws_cis_assessment/controls/ig1/control_data_protection.py +898 -0
  25. aws_cis_assessment/controls/ig1/control_iam_advanced.py +573 -0
  26. aws_cis_assessment/controls/ig1/control_iam_governance.py +493 -0
  27. aws_cis_assessment/controls/ig1/control_iam_policies.py +383 -0
  28. aws_cis_assessment/controls/ig1/control_instance_optimization.py +100 -0
  29. aws_cis_assessment/controls/ig1/control_network_enhancements.py +203 -0
  30. aws_cis_assessment/controls/ig1/control_network_security.py +672 -0
  31. aws_cis_assessment/controls/ig1/control_s3_enhancements.py +173 -0
  32. aws_cis_assessment/controls/ig1/control_s3_security.py +422 -0
  33. aws_cis_assessment/controls/ig1/control_vpc_security.py +235 -0
  34. aws_cis_assessment/controls/ig2/__init__.py +172 -0
  35. aws_cis_assessment/controls/ig2/control_3_10.py +698 -0
  36. aws_cis_assessment/controls/ig2/control_3_11.py +1330 -0
  37. aws_cis_assessment/controls/ig2/control_5_2.py +393 -0
  38. aws_cis_assessment/controls/ig2/control_advanced_encryption.py +355 -0
  39. aws_cis_assessment/controls/ig2/control_codebuild_security.py +263 -0
  40. aws_cis_assessment/controls/ig2/control_encryption_rest.py +382 -0
  41. aws_cis_assessment/controls/ig2/control_encryption_transit.py +382 -0
  42. aws_cis_assessment/controls/ig2/control_network_ha.py +467 -0
  43. aws_cis_assessment/controls/ig2/control_remaining_encryption.py +426 -0
  44. aws_cis_assessment/controls/ig2/control_remaining_rules.py +363 -0
  45. aws_cis_assessment/controls/ig2/control_service_logging.py +402 -0
  46. aws_cis_assessment/controls/ig3/__init__.py +49 -0
  47. aws_cis_assessment/controls/ig3/control_12_8.py +395 -0
  48. aws_cis_assessment/controls/ig3/control_13_1.py +467 -0
  49. aws_cis_assessment/controls/ig3/control_3_14.py +523 -0
  50. aws_cis_assessment/controls/ig3/control_7_1.py +359 -0
  51. aws_cis_assessment/core/__init__.py +1 -0
  52. aws_cis_assessment/core/accuracy_validator.py +425 -0
  53. aws_cis_assessment/core/assessment_engine.py +1266 -0
  54. aws_cis_assessment/core/audit_trail.py +491 -0
  55. aws_cis_assessment/core/aws_client_factory.py +313 -0
  56. aws_cis_assessment/core/error_handler.py +607 -0
  57. aws_cis_assessment/core/models.py +166 -0
  58. aws_cis_assessment/core/scoring_engine.py +459 -0
  59. aws_cis_assessment/reporters/__init__.py +8 -0
  60. aws_cis_assessment/reporters/base_reporter.py +454 -0
  61. aws_cis_assessment/reporters/csv_reporter.py +835 -0
  62. aws_cis_assessment/reporters/html_reporter.py +2162 -0
  63. aws_cis_assessment/reporters/json_reporter.py +561 -0
  64. aws_cis_controls_assessment-1.0.3.dist-info/METADATA +248 -0
  65. aws_cis_controls_assessment-1.0.3.dist-info/RECORD +77 -0
  66. aws_cis_controls_assessment-1.0.3.dist-info/WHEEL +5 -0
  67. aws_cis_controls_assessment-1.0.3.dist-info/entry_points.txt +2 -0
  68. aws_cis_controls_assessment-1.0.3.dist-info/licenses/LICENSE +21 -0
  69. aws_cis_controls_assessment-1.0.3.dist-info/top_level.txt +2 -0
  70. docs/README.md +94 -0
  71. docs/assessment-logic.md +766 -0
  72. docs/cli-reference.md +698 -0
  73. docs/config-rule-mappings.md +393 -0
  74. docs/developer-guide.md +858 -0
  75. docs/installation.md +299 -0
  76. docs/troubleshooting.md +634 -0
  77. docs/user-guide.md +487 -0
@@ -0,0 +1,512 @@
1
+ """
2
+ CIS Control 3.3 - Advanced Security Controls
3
+ Advanced security controls for comprehensive security coverage.
4
+ """
5
+
6
+ import logging
7
+ from typing import List, Dict, Any, Optional
8
+ import boto3
9
+ import json
10
+ from botocore.exceptions import ClientError, NoCredentialsError
11
+
12
+ from aws_cis_assessment.controls.base_control import BaseConfigRuleAssessment
13
+ from aws_cis_assessment.core.models import ComplianceResult, ComplianceStatus
14
+ from aws_cis_assessment.core.aws_client_factory import AWSClientFactory
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class EC2ManagedInstanceAssociationComplianceStatusCheckAssessment(BaseConfigRuleAssessment):
20
+ """
21
+ CIS Control 1.1/2.4/4.1 - Systems Management
22
+ AWS Config Rule: ec2-managedinstance-association-compliance-status-check
23
+
24
+ Ensures EC2 instances have proper Systems Manager associations for compliance tracking.
25
+ """
26
+
27
+ def __init__(self):
28
+ super().__init__(
29
+ rule_name="ec2-managedinstance-association-compliance-status-check",
30
+ control_id="3.3",
31
+ resource_types=["AWS::EC2::Instance"]
32
+ )
33
+
34
+ def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
35
+ """Get all EC2 instances that should be managed by SSM."""
36
+ if resource_type != "AWS::EC2::Instance":
37
+ return []
38
+
39
+ try:
40
+ ec2_client = aws_factory.get_client('ec2', region)
41
+ ssm_client = aws_factory.get_client('ssm', region)
42
+
43
+ # Get all running EC2 instances
44
+ paginator = ec2_client.get_paginator('describe_instances')
45
+ instances = []
46
+
47
+ for page in paginator.paginate(
48
+ Filters=[
49
+ {'Name': 'instance-state-name', 'Values': ['running']}
50
+ ]
51
+ ):
52
+ for reservation in page['Reservations']:
53
+ for instance in reservation['Instances']:
54
+ instance_id = instance['InstanceId']
55
+
56
+ try:
57
+ # Check if instance is managed by SSM
58
+ ssm_response = ssm_client.describe_instance_information(
59
+ Filters=[
60
+ {'Key': 'InstanceIds', 'Values': [instance_id]}
61
+ ]
62
+ )
63
+
64
+ managed_instances = ssm_response.get('InstanceInformationList', [])
65
+ is_managed = len(managed_instances) > 0
66
+
67
+ if is_managed:
68
+ # Get association compliance status
69
+ try:
70
+ compliance_response = ssm_client.list_compliance_items(
71
+ ResourceId=instance_id,
72
+ ResourceType='ManagedInstance'
73
+ )
74
+
75
+ compliance_items = compliance_response.get('ComplianceItems', [])
76
+ association_compliance = []
77
+
78
+ for item in compliance_items:
79
+ if item.get('ComplianceType') == 'Association':
80
+ association_compliance.append({
81
+ 'Id': item.get('Id', ''),
82
+ 'Status': item.get('Status', ''),
83
+ 'Severity': item.get('Severity', ''),
84
+ 'Title': item.get('Title', '')
85
+ })
86
+
87
+ instances.append({
88
+ 'InstanceId': instance_id,
89
+ 'InstanceType': instance.get('InstanceType', ''),
90
+ 'Platform': instance.get('Platform', 'Linux'),
91
+ 'VpcId': instance.get('VpcId', ''),
92
+ 'IsSSMManaged': True,
93
+ 'AssociationCompliance': association_compliance,
94
+ 'HasAssociations': len(association_compliance) > 0
95
+ })
96
+
97
+ except ClientError as e:
98
+ # Instance is managed but can't get compliance info
99
+ instances.append({
100
+ 'InstanceId': instance_id,
101
+ 'InstanceType': instance.get('InstanceType', ''),
102
+ 'Platform': instance.get('Platform', 'Linux'),
103
+ 'VpcId': instance.get('VpcId', ''),
104
+ 'IsSSMManaged': True,
105
+ 'AssociationCompliance': [],
106
+ 'HasAssociations': None # Unknown
107
+ })
108
+ else:
109
+ # Instance is not managed by SSM
110
+ instances.append({
111
+ 'InstanceId': instance_id,
112
+ 'InstanceType': instance.get('InstanceType', ''),
113
+ 'Platform': instance.get('Platform', 'Linux'),
114
+ 'VpcId': instance.get('VpcId', ''),
115
+ 'IsSSMManaged': False,
116
+ 'AssociationCompliance': [],
117
+ 'HasAssociations': False
118
+ })
119
+
120
+ except ClientError as e:
121
+ logger.warning(f"Error checking SSM status for instance {instance_id}: {e}")
122
+ continue
123
+
124
+ logger.debug(f"Found {len(instances)} running EC2 instances in {region}")
125
+ return instances
126
+
127
+ except ClientError as e:
128
+ logger.error(f"Error retrieving EC2 instances in {region}: {e}")
129
+ raise
130
+ except Exception as e:
131
+ logger.error(f"Unexpected error retrieving EC2 instances in {region}: {e}")
132
+ raise
133
+
134
+ def _evaluate_resource_compliance(self, resource: Dict[str, Any], aws_factory: AWSClientFactory, region: str) -> ComplianceResult:
135
+ """Evaluate if EC2 instance has proper SSM association compliance."""
136
+ instance_id = resource.get('InstanceId', 'unknown')
137
+ is_ssm_managed = resource.get('IsSSMManaged', False)
138
+ has_associations = resource.get('HasAssociations', False)
139
+ association_compliance = resource.get('AssociationCompliance', [])
140
+
141
+ if not is_ssm_managed:
142
+ return ComplianceResult(
143
+ resource_id=instance_id,
144
+ resource_type="AWS::EC2::Instance",
145
+ compliance_status=ComplianceStatus.NON_COMPLIANT,
146
+ evaluation_reason="EC2 instance is not managed by Systems Manager",
147
+ config_rule_name=self.rule_name,
148
+ region=region
149
+ )
150
+
151
+ if has_associations is None:
152
+ return ComplianceResult(
153
+ resource_id=instance_id,
154
+ resource_type="AWS::EC2::Instance",
155
+ compliance_status=ComplianceStatus.ERROR,
156
+ evaluation_reason="Unable to determine association compliance status",
157
+ config_rule_name=self.rule_name,
158
+ region=region
159
+ )
160
+
161
+ if not has_associations:
162
+ return ComplianceResult(
163
+ resource_id=instance_id,
164
+ resource_type="AWS::EC2::Instance",
165
+ compliance_status=ComplianceStatus.NON_COMPLIANT,
166
+ evaluation_reason="EC2 instance has no SSM associations configured",
167
+ config_rule_name=self.rule_name,
168
+ region=region
169
+ )
170
+
171
+ # Check compliance status of associations
172
+ non_compliant_associations = [
173
+ assoc for assoc in association_compliance
174
+ if assoc.get('Status') != 'COMPLIANT'
175
+ ]
176
+
177
+ if non_compliant_associations:
178
+ return ComplianceResult(
179
+ resource_id=instance_id,
180
+ resource_type="AWS::EC2::Instance",
181
+ compliance_status=ComplianceStatus.NON_COMPLIANT,
182
+ evaluation_reason=f"EC2 instance has {len(non_compliant_associations)} non-compliant SSM associations",
183
+ config_rule_name=self.rule_name,
184
+ region=region
185
+ )
186
+ else:
187
+ return ComplianceResult(
188
+ resource_id=instance_id,
189
+ resource_type="AWS::EC2::Instance",
190
+ compliance_status=ComplianceStatus.COMPLIANT,
191
+ evaluation_reason=f"EC2 instance has {len(association_compliance)} compliant SSM associations",
192
+ config_rule_name=self.rule_name,
193
+ region=region
194
+ )
195
+
196
+
197
+ class EMRKerberosEnabledAssessment(BaseConfigRuleAssessment):
198
+ """
199
+ CIS Control 3.3 - Configure Data Access Control Lists
200
+ AWS Config Rule: emr-kerberos-enabled
201
+
202
+ Ensures EMR clusters have Kerberos authentication enabled to prevent unauthorized access.
203
+ """
204
+
205
+ def __init__(self):
206
+ super().__init__(
207
+ rule_name="emr-kerberos-enabled",
208
+ control_id="3.3",
209
+ resource_types=["AWS::EMR::Cluster"]
210
+ )
211
+
212
+ def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
213
+ """Get all EMR clusters in the region."""
214
+ if resource_type != "AWS::EMR::Cluster":
215
+ return []
216
+
217
+ try:
218
+ emr_client = aws_factory.get_client('emr', region)
219
+
220
+ # Get all EMR clusters
221
+ paginator = emr_client.get_paginator('list_clusters')
222
+ clusters = []
223
+
224
+ for page in paginator.paginate():
225
+ for cluster_summary in page['Clusters']:
226
+ cluster_id = cluster_summary['Id']
227
+ state = cluster_summary.get('Status', {}).get('State', '')
228
+
229
+ # Skip terminated clusters
230
+ if state in ['TERMINATED', 'TERMINATED_WITH_ERRORS']:
231
+ continue
232
+
233
+ try:
234
+ # Get detailed cluster information
235
+ cluster_response = emr_client.describe_cluster(ClusterId=cluster_id)
236
+ cluster = cluster_response['Cluster']
237
+
238
+ # Check for Kerberos configuration
239
+ kerberos_attributes = cluster.get('KerberosAttributes', {})
240
+ has_kerberos = bool(kerberos_attributes)
241
+
242
+ clusters.append({
243
+ 'ClusterId': cluster_id,
244
+ 'Name': cluster.get('Name', ''),
245
+ 'State': state,
246
+ 'ReleaseLabel': cluster.get('ReleaseLabel', ''),
247
+ 'Applications': [app['Name'] for app in cluster.get('Applications', [])],
248
+ 'HasKerberos': has_kerberos,
249
+ 'KerberosAttributes': kerberos_attributes
250
+ })
251
+
252
+ except ClientError as e:
253
+ logger.warning(f"Error getting details for EMR cluster {cluster_id}: {e}")
254
+ continue
255
+
256
+ logger.debug(f"Found {len(clusters)} active EMR clusters in {region}")
257
+ return clusters
258
+
259
+ except ClientError as e:
260
+ logger.error(f"Error retrieving EMR clusters in {region}: {e}")
261
+ raise
262
+ except Exception as e:
263
+ logger.error(f"Unexpected error retrieving EMR clusters in {region}: {e}")
264
+ raise
265
+
266
+ def _evaluate_resource_compliance(self, resource: Dict[str, Any], aws_factory: AWSClientFactory, region: str) -> ComplianceResult:
267
+ """Evaluate if EMR cluster has Kerberos authentication enabled."""
268
+ cluster_id = resource.get('ClusterId', 'unknown')
269
+ state = resource.get('State', '')
270
+ has_kerberos = resource.get('HasKerberos', False)
271
+
272
+ # Skip clusters that are not running
273
+ if state in ['TERMINATED', 'TERMINATED_WITH_ERRORS', 'TERMINATING']:
274
+ return ComplianceResult(
275
+ resource_id=cluster_id,
276
+ resource_type="AWS::EMR::Cluster",
277
+ compliance_status=ComplianceStatus.NOT_APPLICABLE,
278
+ evaluation_reason=f"EMR cluster is in state '{state}'",
279
+ config_rule_name=self.rule_name,
280
+ region=region
281
+ )
282
+
283
+ if has_kerberos:
284
+ return ComplianceResult(
285
+ resource_id=cluster_id,
286
+ resource_type="AWS::EMR::Cluster",
287
+ compliance_status=ComplianceStatus.COMPLIANT,
288
+ evaluation_reason="EMR cluster has Kerberos authentication enabled",
289
+ config_rule_name=self.rule_name,
290
+ region=region
291
+ )
292
+ else:
293
+ return ComplianceResult(
294
+ resource_id=cluster_id,
295
+ resource_type="AWS::EMR::Cluster",
296
+ compliance_status=ComplianceStatus.NON_COMPLIANT,
297
+ evaluation_reason="EMR cluster does not have Kerberos authentication enabled",
298
+ config_rule_name=self.rule_name,
299
+ region=region
300
+ )
301
+
302
+
303
+ class LambdaInsideVPCAssessment(BaseConfigRuleAssessment):
304
+ """
305
+ CIS Control 3.3 - Configure Data Access Control Lists
306
+ AWS Config Rule: lambda-inside-vpc
307
+
308
+ Ensures Lambda functions are deployed within VPC when needed for network isolation.
309
+ """
310
+
311
+ def __init__(self):
312
+ super().__init__(
313
+ rule_name="lambda-inside-vpc",
314
+ control_id="3.3",
315
+ resource_types=["AWS::Lambda::Function"]
316
+ )
317
+
318
+ def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
319
+ """Get all Lambda functions in the region."""
320
+ if resource_type != "AWS::Lambda::Function":
321
+ return []
322
+
323
+ try:
324
+ lambda_client = aws_factory.get_client('lambda', region)
325
+
326
+ # Get all Lambda functions
327
+ paginator = lambda_client.get_paginator('list_functions')
328
+ functions = []
329
+
330
+ for page in paginator.paginate():
331
+ for function in page['Functions']:
332
+ vpc_config = function.get('VpcConfig', {})
333
+
334
+ functions.append({
335
+ 'FunctionName': function['FunctionName'],
336
+ 'FunctionArn': function['FunctionArn'],
337
+ 'Runtime': function.get('Runtime', ''),
338
+ 'Role': function.get('Role', ''),
339
+ 'VpcConfig': vpc_config,
340
+ 'VpcId': vpc_config.get('VpcId', ''),
341
+ 'SubnetIds': vpc_config.get('SubnetIds', []),
342
+ 'SecurityGroupIds': vpc_config.get('SecurityGroupIds', []),
343
+ 'IsInVPC': bool(vpc_config.get('VpcId'))
344
+ })
345
+
346
+ logger.debug(f"Found {len(functions)} Lambda functions in {region}")
347
+ return functions
348
+
349
+ except ClientError as e:
350
+ logger.error(f"Error retrieving Lambda functions in {region}: {e}")
351
+ raise
352
+ except Exception as e:
353
+ logger.error(f"Unexpected error retrieving Lambda functions in {region}: {e}")
354
+ raise
355
+
356
+ def _evaluate_resource_compliance(self, resource: Dict[str, Any], aws_factory: AWSClientFactory, region: str) -> ComplianceResult:
357
+ """Evaluate if Lambda function is deployed within VPC."""
358
+ function_name = resource.get('FunctionName', 'unknown')
359
+ is_in_vpc = resource.get('IsInVPC', False)
360
+ vpc_id = resource.get('VpcId', '')
361
+
362
+ # Note: This rule is context-dependent. Some Lambda functions may not need VPC access.
363
+ # For this implementation, we'll consider functions that access VPC resources should be in VPC.
364
+ # This is a simplified check - in practice, you might want to check function tags or naming patterns.
365
+
366
+ if is_in_vpc:
367
+ return ComplianceResult(
368
+ resource_id=function_name,
369
+ resource_type="AWS::Lambda::Function",
370
+ compliance_status=ComplianceStatus.COMPLIANT,
371
+ evaluation_reason=f"Lambda function is deployed within VPC {vpc_id}",
372
+ config_rule_name=self.rule_name,
373
+ region=region
374
+ )
375
+ else:
376
+ # For this assessment, we'll mark as informational rather than non-compliant
377
+ # since not all Lambda functions need VPC access
378
+ return ComplianceResult(
379
+ resource_id=function_name,
380
+ resource_type="AWS::Lambda::Function",
381
+ compliance_status=ComplianceStatus.NOT_APPLICABLE,
382
+ evaluation_reason="Lambda function is not deployed within VPC (may not require VPC access)",
383
+ config_rule_name=self.rule_name,
384
+ region=region
385
+ )
386
+
387
+
388
+ class ECSTaskDefinitionUserForHostModeCheckAssessment(BaseConfigRuleAssessment):
389
+ """
390
+ CIS Control 3.3 - Configure Data Access Control Lists
391
+ AWS Config Rule: ecs-task-definition-user-for-host-mode-check
392
+
393
+ Ensures ECS tasks in host mode do not run with elevated privileges to prevent container privilege escalation.
394
+ """
395
+
396
+ def __init__(self):
397
+ super().__init__(
398
+ rule_name="ecs-task-definition-user-for-host-mode-check",
399
+ control_id="3.3",
400
+ resource_types=["AWS::ECS::TaskDefinition"]
401
+ )
402
+
403
+ def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
404
+ """Get all ECS task definitions in the region."""
405
+ if resource_type != "AWS::ECS::TaskDefinition":
406
+ return []
407
+
408
+ try:
409
+ ecs_client = aws_factory.get_client('ecs', region)
410
+
411
+ # Get all task definition families
412
+ families_response = ecs_client.list_task_definition_families(status='ACTIVE')
413
+ task_definitions = []
414
+
415
+ for family in families_response.get('families', []):
416
+ try:
417
+ # Get the latest revision of each family
418
+ list_response = ecs_client.list_task_definitions(
419
+ familyPrefix=family,
420
+ status='ACTIVE',
421
+ sort='DESC',
422
+ maxResults=1
423
+ )
424
+
425
+ if list_response.get('taskDefinitionArns'):
426
+ task_def_arn = list_response['taskDefinitionArns'][0]
427
+
428
+ # Get detailed task definition
429
+ describe_response = ecs_client.describe_task_definition(
430
+ taskDefinition=task_def_arn
431
+ )
432
+
433
+ task_def = describe_response['taskDefinition']
434
+ network_mode = task_def.get('networkMode', 'bridge')
435
+
436
+ # Analyze container definitions for host mode issues
437
+ containers = task_def.get('containerDefinitions', [])
438
+ host_mode_issues = []
439
+
440
+ if network_mode == 'host':
441
+ for container in containers:
442
+ container_name = container.get('name', 'unknown')
443
+ user = container.get('user', '')
444
+ privileged = container.get('privileged', False)
445
+
446
+ # Check for privilege escalation risks in host mode
447
+ if privileged:
448
+ host_mode_issues.append(f"Container '{container_name}' runs in privileged mode")
449
+ elif not user or user == 'root' or user == '0':
450
+ host_mode_issues.append(f"Container '{container_name}' runs as root user")
451
+
452
+ task_definitions.append({
453
+ 'TaskDefinitionArn': task_def_arn,
454
+ 'Family': task_def.get('family', ''),
455
+ 'Revision': task_def.get('revision', 0),
456
+ 'NetworkMode': network_mode,
457
+ 'ContainerCount': len(containers),
458
+ 'IsHostMode': network_mode == 'host',
459
+ 'HostModeIssues': host_mode_issues,
460
+ 'HasHostModeIssues': len(host_mode_issues) > 0
461
+ })
462
+
463
+ except ClientError as e:
464
+ logger.warning(f"Error getting task definition details for family {family}: {e}")
465
+ continue
466
+
467
+ logger.debug(f"Found {len(task_definitions)} active ECS task definitions in {region}")
468
+ return task_definitions
469
+
470
+ except ClientError as e:
471
+ logger.error(f"Error retrieving ECS task definitions in {region}: {e}")
472
+ raise
473
+ except Exception as e:
474
+ logger.error(f"Unexpected error retrieving ECS task definitions in {region}: {e}")
475
+ raise
476
+
477
+ def _evaluate_resource_compliance(self, resource: Dict[str, Any], aws_factory: AWSClientFactory, region: str) -> ComplianceResult:
478
+ """Evaluate if ECS task definition has proper user configuration for host mode."""
479
+ task_def_arn = resource.get('TaskDefinitionArn', 'unknown')
480
+ family = resource.get('Family', 'unknown')
481
+ is_host_mode = resource.get('IsHostMode', False)
482
+ has_host_mode_issues = resource.get('HasHostModeIssues', False)
483
+ host_mode_issues = resource.get('HostModeIssues', [])
484
+
485
+ if not is_host_mode:
486
+ return ComplianceResult(
487
+ resource_id=task_def_arn,
488
+ resource_type="AWS::ECS::TaskDefinition",
489
+ compliance_status=ComplianceStatus.NOT_APPLICABLE,
490
+ evaluation_reason="ECS task definition does not use host network mode",
491
+ config_rule_name=self.rule_name,
492
+ region=region
493
+ )
494
+
495
+ if has_host_mode_issues:
496
+ return ComplianceResult(
497
+ resource_id=task_def_arn,
498
+ resource_type="AWS::ECS::TaskDefinition",
499
+ compliance_status=ComplianceStatus.NON_COMPLIANT,
500
+ evaluation_reason=f"ECS task definition in host mode has privilege escalation risks: {'; '.join(host_mode_issues)}",
501
+ config_rule_name=self.rule_name,
502
+ region=region
503
+ )
504
+ else:
505
+ return ComplianceResult(
506
+ resource_id=task_def_arn,
507
+ resource_type="AWS::ECS::TaskDefinition",
508
+ compliance_status=ComplianceStatus.COMPLIANT,
509
+ evaluation_reason="ECS task definition in host mode has proper user configuration",
510
+ config_rule_name=self.rule_name,
511
+ region=region
512
+ )