aws-cis-controls-assessment 1.1.4__py3-none-any.whl → 1.2.2__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 (45) hide show
  1. aws_cis_assessment/__init__.py +4 -4
  2. aws_cis_assessment/config/rules/cis_controls_ig1.yaml +365 -2
  3. aws_cis_assessment/controls/base_control.py +106 -24
  4. aws_cis_assessment/controls/ig1/__init__.py +144 -15
  5. aws_cis_assessment/controls/ig1/control_4_1.py +4 -4
  6. aws_cis_assessment/controls/ig1/control_access_analyzer.py +198 -0
  7. aws_cis_assessment/controls/ig1/control_access_asset_mgmt.py +360 -0
  8. aws_cis_assessment/controls/ig1/control_access_control.py +323 -0
  9. aws_cis_assessment/controls/ig1/control_backup_security.py +579 -0
  10. aws_cis_assessment/controls/ig1/control_cloudfront_logging.py +215 -0
  11. aws_cis_assessment/controls/ig1/control_configuration_mgmt.py +407 -0
  12. aws_cis_assessment/controls/ig1/control_data_classification.py +255 -0
  13. aws_cis_assessment/controls/ig1/control_dynamodb_encryption.py +279 -0
  14. aws_cis_assessment/controls/ig1/control_ebs_encryption.py +177 -0
  15. aws_cis_assessment/controls/ig1/control_efs_encryption.py +243 -0
  16. aws_cis_assessment/controls/ig1/control_elb_logging.py +195 -0
  17. aws_cis_assessment/controls/ig1/control_guardduty.py +156 -0
  18. aws_cis_assessment/controls/ig1/control_inspector.py +184 -0
  19. aws_cis_assessment/controls/ig1/control_inventory.py +511 -0
  20. aws_cis_assessment/controls/ig1/control_macie.py +165 -0
  21. aws_cis_assessment/controls/ig1/control_messaging_encryption.py +419 -0
  22. aws_cis_assessment/controls/ig1/control_mfa.py +485 -0
  23. aws_cis_assessment/controls/ig1/control_network_security.py +194 -619
  24. aws_cis_assessment/controls/ig1/control_patch_management.py +626 -0
  25. aws_cis_assessment/controls/ig1/control_rds_encryption.py +228 -0
  26. aws_cis_assessment/controls/ig1/control_s3_encryption.py +383 -0
  27. aws_cis_assessment/controls/ig1/control_tls_ssl.py +556 -0
  28. aws_cis_assessment/controls/ig1/control_version_mgmt.py +337 -0
  29. aws_cis_assessment/controls/ig1/control_vpc_flow_logs.py +205 -0
  30. aws_cis_assessment/controls/ig1/control_waf_logging.py +226 -0
  31. aws_cis_assessment/core/assessment_engine.py +160 -11
  32. aws_cis_assessment/core/aws_client_factory.py +17 -5
  33. aws_cis_assessment/core/models.py +20 -1
  34. aws_cis_assessment/core/scoring_engine.py +102 -1
  35. aws_cis_assessment/reporters/base_reporter.py +58 -13
  36. aws_cis_assessment/reporters/html_reporter.py +186 -9
  37. aws_cis_controls_assessment-1.2.2.dist-info/METADATA +320 -0
  38. {aws_cis_controls_assessment-1.1.4.dist-info → aws_cis_controls_assessment-1.2.2.dist-info}/RECORD +44 -20
  39. docs/developer-guide.md +204 -5
  40. docs/user-guide.md +137 -4
  41. aws_cis_controls_assessment-1.1.4.dist-info/METADATA +0 -404
  42. {aws_cis_controls_assessment-1.1.4.dist-info → aws_cis_controls_assessment-1.2.2.dist-info}/WHEEL +0 -0
  43. {aws_cis_controls_assessment-1.1.4.dist-info → aws_cis_controls_assessment-1.2.2.dist-info}/entry_points.txt +0 -0
  44. {aws_cis_controls_assessment-1.1.4.dist-info → aws_cis_controls_assessment-1.2.2.dist-info}/licenses/LICENSE +0 -0
  45. {aws_cis_controls_assessment-1.1.4.dist-info → aws_cis_controls_assessment-1.2.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,215 @@
1
+ """
2
+ CIS Control 8.2 - CloudFront Access Logging
3
+ Ensures CloudFront distributions have access logging enabled.
4
+ """
5
+
6
+ import logging
7
+ from typing import List, Dict, Any
8
+ from botocore.exceptions import ClientError
9
+
10
+ from aws_cis_assessment.controls.base_control import BaseConfigRuleAssessment
11
+ from aws_cis_assessment.core.models import ComplianceResult, ComplianceStatus
12
+ from aws_cis_assessment.core.aws_client_factory import AWSClientFactory
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class CloudFrontLoggingEnabledAssessment(BaseConfigRuleAssessment):
18
+ """
19
+ CIS Control 8.2 - Collect Audit Logs
20
+ AWS Config Rule: cloudfront-logging-enabled
21
+
22
+ Ensures CloudFront distributions have access logging enabled.
23
+ Access logs contain detailed information about every request made to the distribution,
24
+ essential for security analysis, troubleshooting, and compliance.
25
+ """
26
+
27
+ def __init__(self):
28
+ super().__init__(
29
+ rule_name="cloudfront-logging-enabled",
30
+ control_id="8.2",
31
+ resource_types=["AWS::CloudFront::Distribution"]
32
+ )
33
+
34
+ def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
35
+ """Get CloudFront distributions and their logging configuration."""
36
+ if resource_type != "AWS::CloudFront::Distribution":
37
+ return []
38
+
39
+ # CloudFront is a global service, only query from us-east-1
40
+ if region != 'us-east-1':
41
+ return []
42
+
43
+ try:
44
+ cloudfront_client = aws_factory.get_client('cloudfront', 'us-east-1')
45
+
46
+ # List all distributions
47
+ response = cloudfront_client.list_distributions()
48
+ distribution_list = response.get('DistributionList', {})
49
+ distributions = distribution_list.get('Items', [])
50
+
51
+ if not distributions:
52
+ return []
53
+
54
+ # Get detailed configuration for each distribution
55
+ dist_resources = []
56
+ for dist in distributions:
57
+ dist_id = dist.get('Id', '')
58
+ dist_arn = dist.get('ARN', '')
59
+
60
+ try:
61
+ # Get full distribution configuration
62
+ dist_response = cloudfront_client.get_distribution(Id=dist_id)
63
+ dist_config = dist_response.get('Distribution', {}).get('DistributionConfig', {})
64
+
65
+ # Check logging configuration
66
+ logging_config = dist_config.get('Logging', {})
67
+ logging_enabled = logging_config.get('Enabled', False)
68
+ s3_bucket = logging_config.get('Bucket', '')
69
+ log_prefix = logging_config.get('Prefix', '')
70
+
71
+ dist_resources.append({
72
+ 'DistributionId': dist_id,
73
+ 'DistributionArn': dist_arn,
74
+ 'DomainName': dist.get('DomainName', ''),
75
+ 'Status': dist.get('Status', ''),
76
+ 'Enabled': dist.get('Enabled', False),
77
+ 'Region': 'global', # CloudFront is global
78
+ 'LoggingEnabled': logging_enabled,
79
+ 'S3Bucket': s3_bucket,
80
+ 'LogPrefix': log_prefix,
81
+ 'Comment': dist_config.get('Comment', '')
82
+ })
83
+
84
+ except ClientError as e:
85
+ logger.warning(f"Error getting distribution {dist_id} details: {e}")
86
+ continue
87
+
88
+ return dist_resources
89
+
90
+ except ClientError as e:
91
+ error_code = e.response.get('Error', {}).get('Code', '')
92
+ if error_code == 'AccessDenied':
93
+ logger.warning(f"Access denied to CloudFront")
94
+ else:
95
+ logger.error(f"Error listing CloudFront distributions: {e}")
96
+ return []
97
+
98
+ def _evaluate_resource_compliance(
99
+ self,
100
+ resource: Dict[str, Any],
101
+ aws_factory: AWSClientFactory,
102
+ region: str
103
+ ) -> ComplianceResult:
104
+ """Evaluate if CloudFront distribution has logging enabled."""
105
+ dist_id = resource.get('DistributionId', '')
106
+ dist_arn = resource.get('DistributionArn', '')
107
+ domain_name = resource.get('DomainName', '')
108
+ logging_enabled = resource.get('LoggingEnabled', False)
109
+ s3_bucket = resource.get('S3Bucket', '')
110
+ dist_enabled = resource.get('Enabled', False)
111
+
112
+ # Check if logging is enabled
113
+ is_compliant = logging_enabled and s3_bucket
114
+
115
+ if is_compliant:
116
+ evaluation_reason = (
117
+ f"CloudFront distribution {dist_id} ({domain_name}) has access logging enabled. "
118
+ f"Logs are stored in S3 bucket: {s3_bucket}"
119
+ )
120
+ compliance_status = ComplianceStatus.COMPLIANT
121
+ else:
122
+ if not dist_enabled:
123
+ evaluation_reason = f"CloudFront distribution {dist_id} is disabled."
124
+ compliance_status = ComplianceStatus.NOT_APPLICABLE
125
+ elif not logging_enabled:
126
+ evaluation_reason = f"CloudFront distribution {dist_id} ({domain_name}) does not have access logging enabled."
127
+ compliance_status = ComplianceStatus.NON_COMPLIANT
128
+ else:
129
+ evaluation_reason = f"CloudFront distribution {dist_id} ({domain_name}) has logging enabled but no S3 bucket configured."
130
+ compliance_status = ComplianceStatus.NON_COMPLIANT
131
+
132
+ return ComplianceResult(
133
+ resource_id=dist_arn,
134
+ resource_type="AWS::CloudFront::Distribution",
135
+ compliance_status=compliance_status,
136
+ evaluation_reason=evaluation_reason,
137
+ config_rule_name=self.rule_name,
138
+ region='global'
139
+ )
140
+
141
+ def _get_rule_remediation_steps(self) -> List[str]:
142
+ """Get remediation steps for enabling CloudFront access logging."""
143
+ return [
144
+ "1. Enable CloudFront access logging in the AWS Console:",
145
+ " - Navigate to CloudFront service",
146
+ " - Select the distribution",
147
+ " - Click 'Edit'",
148
+ " - Under 'Standard logging', select 'On'",
149
+ " - Specify S3 bucket for logs (must be in same account)",
150
+ " - Optionally specify log prefix",
151
+ " - Click 'Save changes'",
152
+ "",
153
+ "2. Create S3 bucket for logs (if needed):",
154
+ " aws s3 mb s3://<bucket-name> --region us-east-1",
155
+ "",
156
+ "3. Add bucket policy to allow CloudFront to write logs:",
157
+ " {",
158
+ ' "Version": "2012-10-17",',
159
+ ' "Statement": [{',
160
+ ' "Effect": "Allow",',
161
+ ' "Principal": {"Service": "cloudfront.amazonaws.com"},',
162
+ ' "Action": "s3:PutObject",',
163
+ ' "Resource": "arn:aws:s3:::<bucket-name>/*",',
164
+ ' "Condition": {',
165
+ ' "StringEquals": {',
166
+ ' "AWS:SourceAccount": "<account-id>"',
167
+ " }",
168
+ " }",
169
+ " }]",
170
+ " }",
171
+ "",
172
+ "4. Enable access logging using AWS CLI:",
173
+ " # First, get current distribution config",
174
+ " aws cloudfront get-distribution-config \\",
175
+ " --id <distribution-id> > dist-config.json",
176
+ "",
177
+ " # Edit dist-config.json to add logging:",
178
+ ' "Logging": {',
179
+ ' "Enabled": true,',
180
+ ' "IncludeCookies": false,',
181
+ ' "Bucket": "<bucket-name>.s3.amazonaws.com",',
182
+ ' "Prefix": "cloudfront-logs/"',
183
+ " }",
184
+ "",
185
+ " # Update distribution",
186
+ " aws cloudfront update-distribution \\",
187
+ " --id <distribution-id> \\",
188
+ " --distribution-config file://dist-config.json \\",
189
+ " --if-match <etag>",
190
+ "",
191
+ "5. Best practices:",
192
+ " - Use a dedicated S3 bucket for logs",
193
+ " - Enable S3 bucket encryption",
194
+ " - Set lifecycle policies (30-90 days retention)",
195
+ " - Use S3 Intelligent-Tiering for cost optimization",
196
+ " - Enable for all distributions",
197
+ " - Include cookies in logs if needed for analysis",
198
+ "",
199
+ "6. Analyze access logs:",
200
+ " - Use Athena to query logs",
201
+ " - Look for:",
202
+ " * Geographic distribution of requests",
203
+ " * Most requested content",
204
+ " * Error rates (4xx, 5xx)",
205
+ " * Potential DDoS attacks",
206
+ " * Cache hit/miss ratios",
207
+ "",
208
+ "7. Note: CloudFront logs may take up to 24 hours to appear",
209
+ "",
210
+ "Priority: HIGH - CloudFront logs are essential for security and performance analysis",
211
+ "Effort: Low - Can be enabled in minutes per distribution",
212
+ "",
213
+ "AWS Documentation:",
214
+ "https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html"
215
+ ]
@@ -0,0 +1,407 @@
1
+ """
2
+ CIS Control 4.1 - Configuration Management
3
+ Ensures proper configuration management practices across AWS resources.
4
+ """
5
+
6
+ import logging
7
+ from typing import List, Dict, Any
8
+ from botocore.exceptions import ClientError
9
+
10
+ from aws_cis_assessment.controls.base_control import BaseConfigRuleAssessment
11
+ from aws_cis_assessment.core.models import ComplianceResult, ComplianceStatus
12
+ from aws_cis_assessment.core.aws_client_factory import AWSClientFactory
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class ConfigConformancePackDeployedAssessment(BaseConfigRuleAssessment):
18
+ """
19
+ CIS Control 4.1 - Establish and Maintain a Secure Configuration Process
20
+ AWS Config Rule: config-conformance-pack-deployed
21
+
22
+ Ensures AWS Config conformance packs are deployed for configuration management.
23
+ """
24
+
25
+ def __init__(self):
26
+ super().__init__(
27
+ rule_name="config-conformance-pack-deployed",
28
+ control_id="4.1",
29
+ resource_types=["AWS::::Account"]
30
+ )
31
+
32
+ def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
33
+ """Check if conformance packs are deployed."""
34
+ if resource_type != "AWS::::Account":
35
+ return []
36
+
37
+ try:
38
+ config_client = aws_factory.get_client('config', region)
39
+
40
+ response = config_client.describe_conformance_packs()
41
+ packs = response.get('ConformancePackDetails', [])
42
+
43
+ return [{
44
+ 'AccountId': region,
45
+ 'ConformancePacksDeployed': len(packs) > 0,
46
+ 'PackCount': len(packs),
47
+ 'PackNames': [p.get('ConformancePackName') for p in packs]
48
+ }]
49
+
50
+ except ClientError as e:
51
+ logger.error(f"Error checking conformance packs in {region}: {e}")
52
+ return []
53
+
54
+ def _evaluate_resource_compliance(
55
+ self,
56
+ resource: Dict[str, Any],
57
+ aws_factory: AWSClientFactory,
58
+ region: str
59
+ ) -> ComplianceResult:
60
+ """Evaluate if conformance packs are deployed."""
61
+ packs_deployed = resource.get('ConformancePacksDeployed', False)
62
+ pack_count = resource.get('PackCount', 0)
63
+
64
+ if packs_deployed:
65
+ evaluation_reason = f"{pack_count} conformance pack(s) deployed"
66
+ compliance_status = ComplianceStatus.COMPLIANT
67
+ else:
68
+ evaluation_reason = "No conformance packs deployed"
69
+ compliance_status = ComplianceStatus.NON_COMPLIANT
70
+
71
+ return ComplianceResult(
72
+ resource_id=f"account-{region}",
73
+ resource_type="AWS::::Account",
74
+ compliance_status=compliance_status,
75
+ evaluation_reason=evaluation_reason,
76
+ config_rule_name=self.rule_name,
77
+ region=region
78
+ )
79
+
80
+ def _get_rule_remediation_steps(self) -> List[str]:
81
+ """Get remediation steps for deploying conformance packs."""
82
+ return [
83
+ "1. Deploy a conformance pack:",
84
+ " aws configservice put-conformance-pack \\",
85
+ " --conformance-pack-name security-best-practices \\",
86
+ " --template-s3-uri s3://bucket/conformance-pack.yaml",
87
+ "",
88
+ "2. Deploy AWS managed conformance pack:",
89
+ " aws configservice put-conformance-pack \\",
90
+ " --conformance-pack-name operational-best-practices-for-cis",
91
+ "",
92
+ "Priority: HIGH - Essential for configuration compliance",
93
+ "Effort: Medium - Requires pack configuration",
94
+ "",
95
+ "AWS Documentation:",
96
+ "https://docs.aws.amazon.com/config/latest/developerguide/conformance-packs.html"
97
+ ]
98
+
99
+
100
+ class SecurityHubStandardsEnabledAssessment(BaseConfigRuleAssessment):
101
+ """
102
+ CIS Control 4.1 - Establish and Maintain a Secure Configuration Process
103
+ AWS Config Rule: securityhub-standards-enabled
104
+
105
+ Ensures Security Hub standards are enabled for security configuration management.
106
+ """
107
+
108
+ def __init__(self):
109
+ super().__init__(
110
+ rule_name="securityhub-standards-enabled",
111
+ control_id="4.1",
112
+ resource_types=["AWS::::Account"]
113
+ )
114
+
115
+ def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
116
+ """Check if Security Hub standards are enabled."""
117
+ if resource_type != "AWS::::Account":
118
+ return []
119
+
120
+ try:
121
+ securityhub_client = aws_factory.get_client('securityhub', region)
122
+
123
+ response = securityhub_client.get_enabled_standards()
124
+ standards = response.get('StandardsSubscriptions', [])
125
+
126
+ enabled_standards = [s for s in standards if s.get('StandardsStatus') == 'READY']
127
+
128
+ return [{
129
+ 'AccountId': region,
130
+ 'StandardsEnabled': len(enabled_standards) > 0,
131
+ 'StandardCount': len(enabled_standards),
132
+ 'Standards': [s.get('StandardsArn') for s in enabled_standards]
133
+ }]
134
+
135
+ except ClientError as e:
136
+ if e.response.get('Error', {}).get('Code') == 'InvalidAccessException':
137
+ # Security Hub not enabled
138
+ return [{
139
+ 'AccountId': region,
140
+ 'StandardsEnabled': False,
141
+ 'StandardCount': 0,
142
+ 'Standards': []
143
+ }]
144
+ logger.error(f"Error checking Security Hub standards in {region}: {e}")
145
+ return []
146
+
147
+ def _evaluate_resource_compliance(
148
+ self,
149
+ resource: Dict[str, Any],
150
+ aws_factory: AWSClientFactory,
151
+ region: str
152
+ ) -> ComplianceResult:
153
+ """Evaluate if Security Hub standards are enabled."""
154
+ standards_enabled = resource.get('StandardsEnabled', False)
155
+ standard_count = resource.get('StandardCount', 0)
156
+
157
+ if standards_enabled:
158
+ evaluation_reason = f"{standard_count} Security Hub standard(s) enabled"
159
+ compliance_status = ComplianceStatus.COMPLIANT
160
+ else:
161
+ evaluation_reason = "No Security Hub standards enabled"
162
+ compliance_status = ComplianceStatus.NON_COMPLIANT
163
+
164
+ return ComplianceResult(
165
+ resource_id=f"account-{region}",
166
+ resource_type="AWS::::Account",
167
+ compliance_status=compliance_status,
168
+ evaluation_reason=evaluation_reason,
169
+ config_rule_name=self.rule_name,
170
+ region=region
171
+ )
172
+
173
+ def _get_rule_remediation_steps(self) -> List[str]:
174
+ """Get remediation steps for enabling Security Hub standards."""
175
+ return [
176
+ "1. Enable Security Hub:",
177
+ " aws securityhub enable-security-hub",
178
+ "",
179
+ "2. Enable CIS AWS Foundations Benchmark:",
180
+ " aws securityhub batch-enable-standards \\",
181
+ " --standards-subscription-requests StandardsArn=arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0",
182
+ "",
183
+ "3. Enable AWS Foundational Security Best Practices:",
184
+ " aws securityhub batch-enable-standards \\",
185
+ " --standards-subscription-requests StandardsArn=arn:aws:securityhub:us-east-1::standards/aws-foundational-security-best-practices/v/1.0.0",
186
+ "",
187
+ "Priority: HIGH - Critical for security posture",
188
+ "Effort: Low - Quick enablement",
189
+ "",
190
+ "AWS Documentation:",
191
+ "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-standards.html"
192
+ ]
193
+
194
+
195
+ class AssetTaggingComplianceAssessment(BaseConfigRuleAssessment):
196
+ """
197
+ CIS Control 1.1 - Establish and Maintain Detailed Enterprise Asset Inventory
198
+ AWS Config Rule: asset-tagging-compliance
199
+
200
+ Ensures resources have required tags for asset management.
201
+ """
202
+
203
+ def __init__(self):
204
+ super().__init__(
205
+ rule_name="asset-tagging-compliance",
206
+ control_id="1.1",
207
+ resource_types=["AWS::EC2::Instance"]
208
+ )
209
+
210
+ def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
211
+ """Get EC2 instances and check for required tags."""
212
+ if resource_type != "AWS::EC2::Instance":
213
+ return []
214
+
215
+ try:
216
+ ec2_client = aws_factory.get_client('ec2', region)
217
+
218
+ response = ec2_client.describe_instances()
219
+ instances = []
220
+
221
+ for reservation in response.get('Reservations', []):
222
+ for instance in reservation.get('Instances', []):
223
+ tags = {tag['Key']: tag['Value'] for tag in instance.get('Tags', [])}
224
+
225
+ required_tags = ['Name', 'Environment', 'Owner']
226
+ has_required_tags = all(tag in tags for tag in required_tags)
227
+
228
+ instances.append({
229
+ 'InstanceId': instance.get('InstanceId'),
230
+ 'Tags': tags,
231
+ 'HasRequiredTags': has_required_tags,
232
+ 'MissingTags': [tag for tag in required_tags if tag not in tags]
233
+ })
234
+
235
+ return instances
236
+
237
+ except ClientError as e:
238
+ logger.error(f"Error retrieving EC2 instances in {region}: {e}")
239
+ return []
240
+
241
+ def _evaluate_resource_compliance(
242
+ self,
243
+ resource: Dict[str, Any],
244
+ aws_factory: AWSClientFactory,
245
+ region: str
246
+ ) -> ComplianceResult:
247
+ """Evaluate if instance has required tags."""
248
+ instance_id = resource.get('InstanceId', 'unknown')
249
+ has_required_tags = resource.get('HasRequiredTags', False)
250
+ missing_tags = resource.get('MissingTags', [])
251
+
252
+ if has_required_tags:
253
+ evaluation_reason = f"Instance {instance_id} has all required tags"
254
+ compliance_status = ComplianceStatus.COMPLIANT
255
+ else:
256
+ evaluation_reason = f"Instance {instance_id} missing tags: {', '.join(missing_tags)}"
257
+ compliance_status = ComplianceStatus.NON_COMPLIANT
258
+
259
+ return ComplianceResult(
260
+ resource_id=instance_id,
261
+ resource_type="AWS::EC2::Instance",
262
+ compliance_status=compliance_status,
263
+ evaluation_reason=evaluation_reason,
264
+ config_rule_name=self.rule_name,
265
+ region=region
266
+ )
267
+
268
+ def _get_rule_remediation_steps(self) -> List[str]:
269
+ """Get remediation steps for asset tagging."""
270
+ return [
271
+ "1. Tag an EC2 instance:",
272
+ " aws ec2 create-tags \\",
273
+ " --resources <instance-id> \\",
274
+ " --tags Key=Name,Value=web-server \\",
275
+ " Key=Environment,Value=production \\",
276
+ " Key=Owner,Value=team-name",
277
+ "",
278
+ "2. Tag multiple instances:",
279
+ " for instance in $(aws ec2 describe-instances --query 'Reservations[].Instances[].InstanceId' --output text); do",
280
+ " aws ec2 create-tags --resources $instance --tags Key=Environment,Value=production",
281
+ " done",
282
+ "",
283
+ "Priority: MEDIUM - Important for asset management",
284
+ "Effort: Low - Simple tagging",
285
+ "",
286
+ "AWS Documentation:",
287
+ "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html"
288
+ ]
289
+
290
+
291
+ class InspectorAssessmentEnabledAssessment(BaseConfigRuleAssessment):
292
+ """
293
+ CIS Control 7.5 - Perform Automated Vulnerability Scans
294
+ AWS Config Rule: inspector-assessment-enabled
295
+
296
+ Ensures Amazon Inspector assessments are actively running.
297
+ """
298
+
299
+ def __init__(self):
300
+ super().__init__(
301
+ rule_name="inspector-assessment-enabled",
302
+ control_id="7.5",
303
+ resource_types=["AWS::::Account"]
304
+ )
305
+
306
+ def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
307
+ """Check if Inspector assessments are enabled."""
308
+ if resource_type != "AWS::::Account":
309
+ return []
310
+
311
+ try:
312
+ inspector_client = aws_factory.get_client('inspector2', region)
313
+
314
+ # Check Inspector status
315
+ response = inspector_client.batch_get_account_status(accountIds=[])
316
+ accounts = response.get('accounts', [])
317
+
318
+ if accounts:
319
+ account = accounts[0]
320
+ status = account.get('status', 'DISABLED')
321
+ resource_state = account.get('resourceState', {})
322
+
323
+ ec2_enabled = resource_state.get('ec2', {}).get('status') == 'ENABLED'
324
+ ecr_enabled = resource_state.get('ecr', {}).get('status') == 'ENABLED'
325
+ lambda_enabled = resource_state.get('lambda', {}).get('status') == 'ENABLED'
326
+
327
+ return [{
328
+ 'AccountId': region,
329
+ 'InspectorEnabled': status == 'ENABLED',
330
+ 'EC2Enabled': ec2_enabled,
331
+ 'ECREnabled': ecr_enabled,
332
+ 'LambdaEnabled': lambda_enabled
333
+ }]
334
+
335
+ return [{
336
+ 'AccountId': region,
337
+ 'InspectorEnabled': False,
338
+ 'EC2Enabled': False,
339
+ 'ECREnabled': False,
340
+ 'LambdaEnabled': False
341
+ }]
342
+
343
+ except ClientError as e:
344
+ logger.error(f"Error checking Inspector in {region}: {e}")
345
+ return []
346
+
347
+ def _evaluate_resource_compliance(
348
+ self,
349
+ resource: Dict[str, Any],
350
+ aws_factory: AWSClientFactory,
351
+ region: str
352
+ ) -> ComplianceResult:
353
+ """Evaluate if Inspector assessments are enabled."""
354
+ inspector_enabled = resource.get('InspectorEnabled', False)
355
+ ec2_enabled = resource.get('EC2Enabled', False)
356
+ ecr_enabled = resource.get('ECREnabled', False)
357
+ lambda_enabled = resource.get('LambdaEnabled', False)
358
+
359
+ if inspector_enabled and (ec2_enabled or ecr_enabled or lambda_enabled):
360
+ enabled_types = []
361
+ if ec2_enabled:
362
+ enabled_types.append('EC2')
363
+ if ecr_enabled:
364
+ enabled_types.append('ECR')
365
+ if lambda_enabled:
366
+ enabled_types.append('Lambda')
367
+
368
+ evaluation_reason = f"Inspector enabled for: {', '.join(enabled_types)}"
369
+ compliance_status = ComplianceStatus.COMPLIANT
370
+ else:
371
+ evaluation_reason = "Inspector is not enabled or no resource types are being scanned"
372
+ compliance_status = ComplianceStatus.NON_COMPLIANT
373
+
374
+ return ComplianceResult(
375
+ resource_id=f"account-{region}",
376
+ resource_type="AWS::::Account",
377
+ compliance_status=compliance_status,
378
+ evaluation_reason=evaluation_reason,
379
+ config_rule_name=self.rule_name,
380
+ region=region
381
+ )
382
+
383
+ def _get_rule_remediation_steps(self) -> List[str]:
384
+ """Get remediation steps for enabling Inspector."""
385
+ return [
386
+ "1. Enable Inspector for EC2:",
387
+ " aws inspector2 enable \\",
388
+ " --resource-types EC2",
389
+ "",
390
+ "2. Enable Inspector for ECR:",
391
+ " aws inspector2 enable \\",
392
+ " --resource-types ECR",
393
+ "",
394
+ "3. Enable Inspector for Lambda:",
395
+ " aws inspector2 enable \\",
396
+ " --resource-types LAMBDA",
397
+ "",
398
+ "4. Enable all resource types:",
399
+ " aws inspector2 enable \\",
400
+ " --resource-types EC2 ECR LAMBDA",
401
+ "",
402
+ "Priority: HIGH - Critical for vulnerability management",
403
+ "Effort: Low - Quick enablement",
404
+ "",
405
+ "AWS Documentation:",
406
+ "https://docs.aws.amazon.com/inspector/latest/user/getting_started_tutorial.html"
407
+ ]