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,359 @@
1
+ """Control 7.1: Establish and Maintain a Vulnerability Management Process assessments."""
2
+
3
+ from typing import Dict, List, Any
4
+ import logging
5
+ import json
6
+ from botocore.exceptions import ClientError
7
+
8
+ from aws_cis_assessment.controls.base_control import BaseConfigRuleAssessment
9
+ from aws_cis_assessment.core.models import ComplianceResult, ComplianceStatus
10
+ from aws_cis_assessment.core.aws_client_factory import AWSClientFactory
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class ECRPrivateImageScanningEnabledAssessment(BaseConfigRuleAssessment):
16
+ """Assessment for ecr-private-image-scanning-enabled Config rule."""
17
+
18
+ def __init__(self):
19
+ """Initialize ECR private image scanning enabled assessment."""
20
+ super().__init__(
21
+ rule_name="ecr-private-image-scanning-enabled",
22
+ control_id="7.1",
23
+ resource_types=["AWS::ECR::Repository"]
24
+ )
25
+
26
+ def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
27
+ """Get all ECR repositories in the region."""
28
+ if resource_type != "AWS::ECR::Repository":
29
+ return []
30
+
31
+ try:
32
+ ecr_client = aws_factory.get_client('ecr', region)
33
+
34
+ response = aws_factory.aws_api_call_with_retry(
35
+ lambda: ecr_client.describe_repositories()
36
+ )
37
+
38
+ repositories = []
39
+ for repo in response.get('repositories', []):
40
+ repositories.append({
41
+ 'repositoryName': repo.get('repositoryName'),
42
+ 'repositoryArn': repo.get('repositoryArn'),
43
+ 'repositoryUri': repo.get('repositoryUri'),
44
+ 'registryId': repo.get('registryId'),
45
+ 'imageScanningConfiguration': repo.get('imageScanningConfiguration', {}),
46
+ 'createdAt': repo.get('createdAt'),
47
+ 'imageTagMutability': repo.get('imageTagMutability')
48
+ })
49
+
50
+ logger.debug(f"Found {len(repositories)} ECR repositories in region {region}")
51
+ return repositories
52
+
53
+ except ClientError as e:
54
+ logger.error(f"Error retrieving ECR repositories in region {region}: {e}")
55
+ raise
56
+ except Exception as e:
57
+ logger.error(f"Unexpected error retrieving ECR repositories in region {region}: {e}")
58
+ raise
59
+
60
+ def _evaluate_resource_compliance(self, resource: Dict[str, Any], aws_factory: AWSClientFactory, region: str) -> ComplianceResult:
61
+ """Evaluate if ECR repository has image scanning enabled."""
62
+ repo_name = resource.get('repositoryName', 'unknown')
63
+ repo_arn = resource.get('repositoryArn', 'unknown')
64
+ scanning_config = resource.get('imageScanningConfiguration', {})
65
+
66
+ # Check if scan on push is enabled
67
+ scan_on_push = scanning_config.get('scanOnPush', False)
68
+
69
+ if scan_on_push:
70
+ compliance_status = ComplianceStatus.COMPLIANT
71
+ evaluation_reason = f"ECR repository {repo_name} has image scanning enabled (scanOnPush: true)"
72
+ else:
73
+ compliance_status = ComplianceStatus.NON_COMPLIANT
74
+ evaluation_reason = f"ECR repository {repo_name} does not have image scanning enabled (scanOnPush: false)"
75
+
76
+ return ComplianceResult(
77
+ resource_id=repo_arn,
78
+ resource_type="AWS::ECR::Repository",
79
+ compliance_status=compliance_status,
80
+ evaluation_reason=evaluation_reason,
81
+ config_rule_name=self.rule_name,
82
+ region=region
83
+ )
84
+
85
+ def _get_rule_remediation_steps(self) -> List[str]:
86
+ """Get specific remediation steps for ECR image scanning."""
87
+ return [
88
+ "Identify ECR repositories without image scanning enabled",
89
+ "For each non-compliant repository:",
90
+ " 1. Enable scan on push for the repository",
91
+ " 2. Consider enabling enhanced scanning for more comprehensive vulnerability detection",
92
+ " 3. Set up notifications for scan results",
93
+ "Use AWS CLI: aws ecr put-image-scanning-configuration --repository-name <repo-name> --image-scanning-configuration scanOnPush=true",
94
+ "Enable enhanced scanning: aws ecr put-registry-scanning-configuration --scan-type ENHANCED",
95
+ "Set up EventBridge rules to receive scan completion notifications",
96
+ "Review scan results regularly and remediate identified vulnerabilities",
97
+ "Consider implementing automated workflows to block deployment of vulnerable images",
98
+ "Document vulnerability management process for container images"
99
+ ]
100
+
101
+
102
+ class GuardDutyEnabledCentralizedAssessment(BaseConfigRuleAssessment):
103
+ """Assessment for guardduty-enabled-centralized Config rule."""
104
+
105
+ def __init__(self):
106
+ """Initialize GuardDuty enabled centralized assessment."""
107
+ super().__init__(
108
+ rule_name="guardduty-enabled-centralized",
109
+ control_id="7.1",
110
+ resource_types=["AWS::::Account"]
111
+ )
112
+
113
+ def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
114
+ """Get account-level resource for GuardDuty assessment."""
115
+ if resource_type != "AWS::::Account":
116
+ return []
117
+
118
+ # Return a single account resource for this region
119
+ account_info = aws_factory.get_account_info()
120
+ return [{
121
+ 'accountId': account_info.get('account_id', 'unknown'),
122
+ 'region': region
123
+ }]
124
+
125
+ def _evaluate_resource_compliance(self, resource: Dict[str, Any], aws_factory: AWSClientFactory, region: str) -> ComplianceResult:
126
+ """Evaluate if GuardDuty is enabled in the account/region."""
127
+ account_id = resource.get('accountId', 'unknown')
128
+ resource_id = f"account-{account_id}-{region}"
129
+
130
+ try:
131
+ guardduty_client = aws_factory.get_client('guardduty', region)
132
+
133
+ # List detectors to see if GuardDuty is enabled
134
+ response = aws_factory.aws_api_call_with_retry(
135
+ lambda: guardduty_client.list_detectors()
136
+ )
137
+
138
+ detector_ids = response.get('DetectorIds', [])
139
+
140
+ if not detector_ids:
141
+ compliance_status = ComplianceStatus.NON_COMPLIANT
142
+ evaluation_reason = f"GuardDuty is not enabled in account {account_id} region {region}"
143
+ else:
144
+ # Check if any detector is enabled
145
+ enabled_detectors = []
146
+
147
+ for detector_id in detector_ids:
148
+ try:
149
+ detector_response = aws_factory.aws_api_call_with_retry(
150
+ lambda: guardduty_client.get_detector(DetectorId=detector_id)
151
+ )
152
+
153
+ status = detector_response.get('Status', 'DISABLED')
154
+ if status == 'ENABLED':
155
+ enabled_detectors.append(detector_id)
156
+
157
+ except ClientError as e:
158
+ logger.warning(f"Could not get detector {detector_id} details: {e}")
159
+ continue
160
+
161
+ if enabled_detectors:
162
+ compliance_status = ComplianceStatus.COMPLIANT
163
+ evaluation_reason = f"GuardDuty is enabled in account {account_id} region {region} with {len(enabled_detectors)} active detector(s)"
164
+ else:
165
+ compliance_status = ComplianceStatus.NON_COMPLIANT
166
+ evaluation_reason = f"GuardDuty detectors exist but are disabled in account {account_id} region {region}"
167
+
168
+ except ClientError as e:
169
+ error_code = e.response.get('Error', {}).get('Code', '')
170
+ if error_code in ['AccessDenied', 'UnauthorizedOperation']:
171
+ compliance_status = ComplianceStatus.ERROR
172
+ evaluation_reason = f"Insufficient permissions to check GuardDuty status in account {account_id} region {region}"
173
+ else:
174
+ compliance_status = ComplianceStatus.ERROR
175
+ evaluation_reason = f"Error checking GuardDuty status in account {account_id} region {region}: {str(e)}"
176
+ except Exception as e:
177
+ compliance_status = ComplianceStatus.ERROR
178
+ evaluation_reason = f"Unexpected error checking GuardDuty status in account {account_id} region {region}: {str(e)}"
179
+
180
+ return ComplianceResult(
181
+ resource_id=resource_id,
182
+ resource_type="AWS::::Account",
183
+ compliance_status=compliance_status,
184
+ evaluation_reason=evaluation_reason,
185
+ config_rule_name=self.rule_name,
186
+ region=region
187
+ )
188
+
189
+ def _get_rule_remediation_steps(self) -> List[str]:
190
+ """Get specific remediation steps for GuardDuty enablement."""
191
+ return [
192
+ "Enable GuardDuty in all AWS regions where resources are deployed",
193
+ "For each region without GuardDuty:",
194
+ " 1. Create a GuardDuty detector",
195
+ " 2. Enable the detector",
196
+ " 3. Configure finding export to S3 or other destinations",
197
+ " 4. Set up notifications for high-severity findings",
198
+ "Use AWS CLI: aws guardduty create-detector --enable --region <region>",
199
+ "Enable S3 protection: aws guardduty update-detector --detector-id <detector-id> --data-sources S3Logs={Enable=true}",
200
+ "Enable Kubernetes protection: aws guardduty update-detector --detector-id <detector-id> --data-sources Kubernetes={AuditLogs={Enable=true}}",
201
+ "Set up centralized logging by configuring finding export",
202
+ "Create EventBridge rules to route findings to security teams",
203
+ "Consider enabling GuardDuty Malware Protection for EC2 instances",
204
+ "Implement automated response workflows for critical findings",
205
+ "Regularly review and tune GuardDuty findings to reduce false positives"
206
+ ]
207
+
208
+
209
+ class EC2ManagedInstancePatchComplianceAssessment(BaseConfigRuleAssessment):
210
+ """Assessment for ec2-managedinstance-patch-compliance-status-check Config rule."""
211
+
212
+ def __init__(self):
213
+ """Initialize EC2 managed instance patch compliance assessment."""
214
+ super().__init__(
215
+ rule_name="ec2-managedinstance-patch-compliance-status-check",
216
+ control_id="7.1",
217
+ resource_types=["AWS::EC2::Instance"]
218
+ )
219
+
220
+ def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
221
+ """Get all EC2 instances that are managed by Systems Manager."""
222
+ if resource_type != "AWS::EC2::Instance":
223
+ return []
224
+
225
+ try:
226
+ ec2_client = aws_factory.get_client('ec2', region)
227
+ ssm_client = aws_factory.get_client('ssm', region)
228
+
229
+ # First get all EC2 instances
230
+ response = aws_factory.aws_api_call_with_retry(
231
+ lambda: ec2_client.describe_instances()
232
+ )
233
+
234
+ all_instances = []
235
+ for reservation in response.get('Reservations', []):
236
+ for instance in reservation.get('Instances', []):
237
+ if instance.get('State', {}).get('Name') == 'running':
238
+ all_instances.append({
239
+ 'InstanceId': instance.get('InstanceId'),
240
+ 'InstanceType': instance.get('InstanceType'),
241
+ 'Platform': instance.get('Platform', 'Linux'),
242
+ 'LaunchTime': instance.get('LaunchTime'),
243
+ 'Tags': instance.get('Tags', [])
244
+ })
245
+
246
+ # Filter to only instances managed by Systems Manager
247
+ managed_instances = []
248
+ if all_instances:
249
+ try:
250
+ # Get managed instances from Systems Manager
251
+ ssm_response = aws_factory.aws_api_call_with_retry(
252
+ lambda: ssm_client.describe_instance_information()
253
+ )
254
+
255
+ managed_instance_ids = set()
256
+ for instance_info in ssm_response.get('InstanceInformationList', []):
257
+ managed_instance_ids.add(instance_info.get('InstanceId'))
258
+
259
+ # Filter EC2 instances to only managed ones
260
+ for instance in all_instances:
261
+ if instance['InstanceId'] in managed_instance_ids:
262
+ managed_instances.append(instance)
263
+
264
+ except ClientError as e:
265
+ logger.warning(f"Could not get Systems Manager managed instances: {e}")
266
+ # If we can't access SSM, we'll evaluate all running instances
267
+ managed_instances = all_instances
268
+
269
+ logger.debug(f"Found {len(managed_instances)} managed EC2 instances in region {region}")
270
+ return managed_instances
271
+
272
+ except ClientError as e:
273
+ logger.error(f"Error retrieving EC2 instances in region {region}: {e}")
274
+ raise
275
+ except Exception as e:
276
+ logger.error(f"Unexpected error retrieving EC2 instances in region {region}: {e}")
277
+ raise
278
+
279
+ def _evaluate_resource_compliance(self, resource: Dict[str, Any], aws_factory: AWSClientFactory, region: str) -> ComplianceResult:
280
+ """Evaluate if EC2 instance has compliant patch status."""
281
+ instance_id = resource.get('InstanceId', 'unknown')
282
+ instance_type = resource.get('InstanceType', 'unknown')
283
+ platform = resource.get('Platform', 'Linux')
284
+
285
+ try:
286
+ ssm_client = aws_factory.get_client('ssm', region)
287
+
288
+ # Get patch compliance information for this instance
289
+ response = aws_factory.aws_api_call_with_retry(
290
+ lambda: ssm_client.describe_instance_patch_states(
291
+ InstanceIds=[instance_id]
292
+ )
293
+ )
294
+
295
+ patch_states = response.get('InstancePatchStates', [])
296
+
297
+ if not patch_states:
298
+ compliance_status = ComplianceStatus.NON_COMPLIANT
299
+ evaluation_reason = f"EC2 instance {instance_id} ({platform}) has no patch compliance information available"
300
+ else:
301
+ patch_state = patch_states[0]
302
+ operation_end_time = patch_state.get('OperationEndTime')
303
+ failed_count = patch_state.get('FailedCount', 0)
304
+ missing_count = patch_state.get('MissingCount', 0)
305
+ installed_count = patch_state.get('InstalledCount', 0)
306
+ not_applicable_count = patch_state.get('NotApplicableCount', 0)
307
+
308
+ # Consider compliant if no failed or missing patches
309
+ if failed_count == 0 and missing_count == 0:
310
+ compliance_status = ComplianceStatus.COMPLIANT
311
+ evaluation_reason = f"EC2 instance {instance_id} ({platform}) is patch compliant - {installed_count} installed, {not_applicable_count} not applicable"
312
+ else:
313
+ compliance_status = ComplianceStatus.NON_COMPLIANT
314
+ evaluation_reason = f"EC2 instance {instance_id} ({platform}) is not patch compliant - {missing_count} missing, {failed_count} failed patches"
315
+
316
+ except ClientError as e:
317
+ error_code = e.response.get('Error', {}).get('Code', '')
318
+ if error_code in ['AccessDenied', 'UnauthorizedOperation']:
319
+ compliance_status = ComplianceStatus.ERROR
320
+ evaluation_reason = f"Insufficient permissions to check patch compliance for EC2 instance {instance_id}"
321
+ elif error_code == 'InvalidInstanceId.NotFound':
322
+ compliance_status = ComplianceStatus.ERROR
323
+ evaluation_reason = f"EC2 instance {instance_id} not found in Systems Manager (may not be managed)"
324
+ else:
325
+ compliance_status = ComplianceStatus.ERROR
326
+ evaluation_reason = f"Error checking patch compliance for EC2 instance {instance_id}: {str(e)}"
327
+ except Exception as e:
328
+ compliance_status = ComplianceStatus.ERROR
329
+ evaluation_reason = f"Unexpected error checking patch compliance for EC2 instance {instance_id}: {str(e)}"
330
+
331
+ return ComplianceResult(
332
+ resource_id=instance_id,
333
+ resource_type="AWS::EC2::Instance",
334
+ compliance_status=compliance_status,
335
+ evaluation_reason=evaluation_reason,
336
+ config_rule_name=self.rule_name,
337
+ region=region
338
+ )
339
+
340
+ def _get_rule_remediation_steps(self) -> List[str]:
341
+ """Get specific remediation steps for EC2 patch compliance."""
342
+ return [
343
+ "Ensure EC2 instances are managed by AWS Systems Manager",
344
+ "For instances not managed by Systems Manager:",
345
+ " 1. Install SSM Agent (pre-installed on Amazon Linux, Windows, Ubuntu)",
346
+ " 2. Attach IAM role with AmazonSSMManagedInstanceCore policy",
347
+ " 3. Verify instance appears in Systems Manager console",
348
+ "For non-compliant patch status:",
349
+ " 1. Create or update patch baselines for your operating systems",
350
+ " 2. Create maintenance windows for patch installation",
351
+ " 3. Run patch scans to identify missing patches",
352
+ " 4. Install missing patches during maintenance windows",
353
+ "Use AWS CLI: aws ssm send-command --document-name 'AWS-RunPatchBaseline' --instance-ids <instance-id> --parameters 'Operation=Scan'",
354
+ "Install patches: aws ssm send-command --document-name 'AWS-RunPatchBaseline' --instance-ids <instance-id> --parameters 'Operation=Install'",
355
+ "Set up automated patching with maintenance windows",
356
+ "Monitor patch compliance using Systems Manager Compliance",
357
+ "Create CloudWatch alarms for patch compliance failures",
358
+ "Implement patch testing procedures before production deployment"
359
+ ]
@@ -0,0 +1 @@
1
+ """Core components for AWS CIS assessment engine."""