aws-cis-controls-assessment 1.1.3__py3-none-any.whl → 1.2.0__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.
- aws_cis_assessment/__init__.py +4 -4
- aws_cis_assessment/config/rules/cis_controls_ig1.yaml +365 -2
- aws_cis_assessment/controls/ig1/control_access_analyzer.py +198 -0
- aws_cis_assessment/controls/ig1/control_access_asset_mgmt.py +360 -0
- aws_cis_assessment/controls/ig1/control_access_control.py +323 -0
- aws_cis_assessment/controls/ig1/control_backup_security.py +579 -0
- aws_cis_assessment/controls/ig1/control_cloudfront_logging.py +215 -0
- aws_cis_assessment/controls/ig1/control_configuration_mgmt.py +407 -0
- aws_cis_assessment/controls/ig1/control_data_classification.py +255 -0
- aws_cis_assessment/controls/ig1/control_dynamodb_encryption.py +279 -0
- aws_cis_assessment/controls/ig1/control_ebs_encryption.py +177 -0
- aws_cis_assessment/controls/ig1/control_efs_encryption.py +243 -0
- aws_cis_assessment/controls/ig1/control_elb_logging.py +195 -0
- aws_cis_assessment/controls/ig1/control_guardduty.py +156 -0
- aws_cis_assessment/controls/ig1/control_inspector.py +184 -0
- aws_cis_assessment/controls/ig1/control_inventory.py +511 -0
- aws_cis_assessment/controls/ig1/control_macie.py +165 -0
- aws_cis_assessment/controls/ig1/control_messaging_encryption.py +419 -0
- aws_cis_assessment/controls/ig1/control_mfa.py +485 -0
- aws_cis_assessment/controls/ig1/control_network_security.py +194 -619
- aws_cis_assessment/controls/ig1/control_patch_management.py +626 -0
- aws_cis_assessment/controls/ig1/control_rds_encryption.py +228 -0
- aws_cis_assessment/controls/ig1/control_s3_encryption.py +383 -0
- aws_cis_assessment/controls/ig1/control_tls_ssl.py +556 -0
- aws_cis_assessment/controls/ig1/control_version_mgmt.py +329 -0
- aws_cis_assessment/controls/ig1/control_vpc_flow_logs.py +205 -0
- aws_cis_assessment/controls/ig1/control_waf_logging.py +226 -0
- aws_cis_assessment/core/models.py +20 -1
- aws_cis_assessment/core/scoring_engine.py +98 -1
- aws_cis_assessment/reporters/base_reporter.py +31 -1
- aws_cis_assessment/reporters/html_reporter.py +172 -11
- aws_cis_controls_assessment-1.2.0.dist-info/METADATA +320 -0
- {aws_cis_controls_assessment-1.1.3.dist-info → aws_cis_controls_assessment-1.2.0.dist-info}/RECORD +39 -15
- docs/developer-guide.md +204 -5
- docs/user-guide.md +137 -4
- aws_cis_controls_assessment-1.1.3.dist-info/METADATA +0 -404
- {aws_cis_controls_assessment-1.1.3.dist-info → aws_cis_controls_assessment-1.2.0.dist-info}/WHEEL +0 -0
- {aws_cis_controls_assessment-1.1.3.dist-info → aws_cis_controls_assessment-1.2.0.dist-info}/entry_points.txt +0 -0
- {aws_cis_controls_assessment-1.1.3.dist-info → aws_cis_controls_assessment-1.2.0.dist-info}/licenses/LICENSE +0 -0
- {aws_cis_controls_assessment-1.1.3.dist-info → aws_cis_controls_assessment-1.2.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CIS Control 10.1 - GuardDuty Malware Defense
|
|
3
|
+
Ensures GuardDuty is enabled for threat detection and malware defense.
|
|
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 GuardDutyEnabledAssessment(BaseConfigRuleAssessment):
|
|
18
|
+
"""
|
|
19
|
+
CIS Control 10.1 - Deploy and Maintain Anti-Malware Software
|
|
20
|
+
AWS Config Rule: guardduty-enabled
|
|
21
|
+
|
|
22
|
+
Ensures GuardDuty is enabled for threat detection and malware defense.
|
|
23
|
+
GuardDuty provides intelligent threat detection by analyzing VPC Flow Logs,
|
|
24
|
+
CloudTrail events, and DNS logs to identify malicious activity.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self):
|
|
28
|
+
super().__init__(
|
|
29
|
+
rule_name="guardduty-enabled",
|
|
30
|
+
control_id="10.1",
|
|
31
|
+
resource_types=["AWS::GuardDuty::Detector"]
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
|
|
35
|
+
"""Get GuardDuty detector configuration for the region."""
|
|
36
|
+
if resource_type != "AWS::GuardDuty::Detector":
|
|
37
|
+
return []
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
guardduty_client = aws_factory.get_client('guardduty', region)
|
|
41
|
+
|
|
42
|
+
# List all detectors in the region
|
|
43
|
+
response = guardduty_client.list_detectors()
|
|
44
|
+
detector_ids = response.get('DetectorIds', [])
|
|
45
|
+
|
|
46
|
+
if not detector_ids:
|
|
47
|
+
# No detector found - return a placeholder resource for non-compliance
|
|
48
|
+
return [{
|
|
49
|
+
'DetectorId': 'none',
|
|
50
|
+
'Status': 'DISABLED',
|
|
51
|
+
'Region': region,
|
|
52
|
+
'AccountId': aws_factory.get_account_info().get('account_id', 'unknown')
|
|
53
|
+
}]
|
|
54
|
+
|
|
55
|
+
# Get details for each detector
|
|
56
|
+
detectors = []
|
|
57
|
+
for detector_id in detector_ids:
|
|
58
|
+
try:
|
|
59
|
+
detector_response = guardduty_client.get_detector(DetectorId=detector_id)
|
|
60
|
+
|
|
61
|
+
detectors.append({
|
|
62
|
+
'DetectorId': detector_id,
|
|
63
|
+
'Status': detector_response.get('Status', 'UNKNOWN'),
|
|
64
|
+
'FindingPublishingFrequency': detector_response.get('FindingPublishingFrequency', 'UNKNOWN'),
|
|
65
|
+
'DataSources': detector_response.get('DataSources', {}),
|
|
66
|
+
'Region': region,
|
|
67
|
+
'AccountId': aws_factory.get_account_info().get('account_id', 'unknown')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
except ClientError as e:
|
|
71
|
+
logger.warning(f"Error getting detector {detector_id} details: {e}")
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
return detectors if detectors else [{
|
|
75
|
+
'DetectorId': 'none',
|
|
76
|
+
'Status': 'DISABLED',
|
|
77
|
+
'Region': region,
|
|
78
|
+
'AccountId': aws_factory.get_account_info().get('account_id', 'unknown')
|
|
79
|
+
}]
|
|
80
|
+
|
|
81
|
+
except ClientError as e:
|
|
82
|
+
error_code = e.response.get('Error', {}).get('Code', '')
|
|
83
|
+
if error_code == 'AccessDeniedException':
|
|
84
|
+
logger.warning(f"Access denied to GuardDuty in {region}")
|
|
85
|
+
else:
|
|
86
|
+
logger.error(f"Error listing GuardDuty detectors in {region}: {e}")
|
|
87
|
+
return []
|
|
88
|
+
|
|
89
|
+
def _evaluate_resource_compliance(
|
|
90
|
+
self,
|
|
91
|
+
resource: Dict[str, Any],
|
|
92
|
+
aws_factory: AWSClientFactory,
|
|
93
|
+
region: str
|
|
94
|
+
) -> ComplianceResult:
|
|
95
|
+
"""Evaluate if GuardDuty is enabled."""
|
|
96
|
+
detector_id = resource.get('DetectorId', 'none')
|
|
97
|
+
status = resource.get('Status', 'DISABLED')
|
|
98
|
+
|
|
99
|
+
# Check if GuardDuty is enabled
|
|
100
|
+
is_compliant = status == 'ENABLED'
|
|
101
|
+
|
|
102
|
+
if is_compliant:
|
|
103
|
+
evaluation_reason = (
|
|
104
|
+
f"GuardDuty detector {detector_id} is enabled in {region}. "
|
|
105
|
+
f"Finding publishing frequency: {resource.get('FindingPublishingFrequency', 'UNKNOWN')}"
|
|
106
|
+
)
|
|
107
|
+
compliance_status = ComplianceStatus.COMPLIANT
|
|
108
|
+
else:
|
|
109
|
+
if detector_id == 'none':
|
|
110
|
+
evaluation_reason = f"GuardDuty is not enabled in {region}. No detector found."
|
|
111
|
+
else:
|
|
112
|
+
evaluation_reason = f"GuardDuty detector {detector_id} is disabled in {region}."
|
|
113
|
+
compliance_status = ComplianceStatus.NON_COMPLIANT
|
|
114
|
+
|
|
115
|
+
return ComplianceResult(
|
|
116
|
+
resource_id=detector_id,
|
|
117
|
+
resource_type="AWS::GuardDuty::Detector",
|
|
118
|
+
compliance_status=compliance_status,
|
|
119
|
+
evaluation_reason=evaluation_reason,
|
|
120
|
+
config_rule_name=self.rule_name,
|
|
121
|
+
region=region
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def _get_rule_remediation_steps(self) -> List[str]:
|
|
125
|
+
"""Get remediation steps for enabling GuardDuty."""
|
|
126
|
+
return [
|
|
127
|
+
"1. Enable GuardDuty in the AWS Console:",
|
|
128
|
+
" - Navigate to GuardDuty service",
|
|
129
|
+
" - Click 'Get Started' or 'Enable GuardDuty'",
|
|
130
|
+
" - Review and accept the service terms",
|
|
131
|
+
" - Click 'Enable GuardDuty'",
|
|
132
|
+
"",
|
|
133
|
+
"2. Enable GuardDuty using AWS CLI:",
|
|
134
|
+
" aws guardduty create-detector --enable --region <region>",
|
|
135
|
+
"",
|
|
136
|
+
"3. Configure finding publishing frequency (optional):",
|
|
137
|
+
" aws guardduty update-detector \\",
|
|
138
|
+
" --detector-id <detector-id> \\",
|
|
139
|
+
" --finding-publishing-frequency FIFTEEN_MINUTES \\",
|
|
140
|
+
" --region <region>",
|
|
141
|
+
"",
|
|
142
|
+
"4. Enable additional data sources (recommended):",
|
|
143
|
+
" - S3 Protection: Monitors S3 data events",
|
|
144
|
+
" - EKS Protection: Monitors Kubernetes audit logs",
|
|
145
|
+
" - Malware Protection: Scans EBS volumes",
|
|
146
|
+
"",
|
|
147
|
+
"5. Set up notifications:",
|
|
148
|
+
" - Create an SNS topic for GuardDuty findings",
|
|
149
|
+
" - Configure EventBridge rules to route findings",
|
|
150
|
+
"",
|
|
151
|
+
"Priority: HIGH - GuardDuty provides critical threat detection",
|
|
152
|
+
"Effort: Low - Can be enabled in minutes",
|
|
153
|
+
"",
|
|
154
|
+
"AWS Documentation:",
|
|
155
|
+
"https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_settingup.html"
|
|
156
|
+
]
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CIS Control 7.1 - Amazon Inspector Vulnerability Management
|
|
3
|
+
Ensures Amazon Inspector is enabled for vulnerability scanning.
|
|
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 InspectorEnabledAssessment(BaseConfigRuleAssessment):
|
|
18
|
+
"""
|
|
19
|
+
CIS Control 7.1 - Establish and Maintain a Vulnerability Management Process
|
|
20
|
+
AWS Config Rule: inspector-enabled
|
|
21
|
+
|
|
22
|
+
Ensures Amazon Inspector v2 is enabled for automated vulnerability scanning.
|
|
23
|
+
Inspector scans EC2 instances, container images, and Lambda functions for
|
|
24
|
+
software vulnerabilities and network exposure.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self):
|
|
28
|
+
super().__init__(
|
|
29
|
+
rule_name="inspector-enabled",
|
|
30
|
+
control_id="7.1",
|
|
31
|
+
resource_types=["AWS::Inspector::AssessmentTarget"]
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
|
|
35
|
+
"""Get Amazon Inspector configuration for the region."""
|
|
36
|
+
if resource_type != "AWS::Inspector::AssessmentTarget":
|
|
37
|
+
return []
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
inspector_client = aws_factory.get_client('inspector2', region)
|
|
41
|
+
|
|
42
|
+
# Check if Inspector is enabled by getting account status
|
|
43
|
+
try:
|
|
44
|
+
status_response = inspector_client.batch_get_account_status(
|
|
45
|
+
accountIds=[aws_factory.get_account_info().get('account_id', '')]
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
accounts = status_response.get('accounts', [])
|
|
49
|
+
if not accounts:
|
|
50
|
+
# Inspector not enabled
|
|
51
|
+
return [{
|
|
52
|
+
'InspectorId': 'none',
|
|
53
|
+
'Status': 'DISABLED',
|
|
54
|
+
'Region': region,
|
|
55
|
+
'AccountId': aws_factory.get_account_info().get('account_id', 'unknown'),
|
|
56
|
+
'ResourceTypes': []
|
|
57
|
+
}]
|
|
58
|
+
|
|
59
|
+
account_status = accounts[0]
|
|
60
|
+
state = account_status.get('state', {}).get('status', 'DISABLED')
|
|
61
|
+
|
|
62
|
+
# Get coverage statistics
|
|
63
|
+
coverage_response = inspector_client.list_coverage(
|
|
64
|
+
maxResults=1 # Just checking if any resources are covered
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
covered_resources = coverage_response.get('coveredResources', [])
|
|
68
|
+
has_coverage = len(covered_resources) > 0
|
|
69
|
+
|
|
70
|
+
# Get resource types being scanned
|
|
71
|
+
resource_state = account_status.get('resourceState', {})
|
|
72
|
+
enabled_resource_types = [
|
|
73
|
+
rt for rt, status in resource_state.items()
|
|
74
|
+
if status.get('status') == 'ENABLED'
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
return [{
|
|
78
|
+
'InspectorId': f"inspector-{region}",
|
|
79
|
+
'Status': state,
|
|
80
|
+
'Region': region,
|
|
81
|
+
'AccountId': aws_factory.get_account_info().get('account_id', 'unknown'),
|
|
82
|
+
'ResourceTypes': enabled_resource_types,
|
|
83
|
+
'HasCoverage': has_coverage
|
|
84
|
+
}]
|
|
85
|
+
|
|
86
|
+
except ClientError as e:
|
|
87
|
+
error_code = e.response.get('Error', {}).get('Code', '')
|
|
88
|
+
if error_code == 'ResourceNotFoundException':
|
|
89
|
+
# Inspector not enabled
|
|
90
|
+
return [{
|
|
91
|
+
'InspectorId': 'none',
|
|
92
|
+
'Status': 'DISABLED',
|
|
93
|
+
'Region': region,
|
|
94
|
+
'AccountId': aws_factory.get_account_info().get('account_id', 'unknown'),
|
|
95
|
+
'ResourceTypes': []
|
|
96
|
+
}]
|
|
97
|
+
raise
|
|
98
|
+
|
|
99
|
+
except ClientError as e:
|
|
100
|
+
error_code = e.response.get('Error', {}).get('Code', '')
|
|
101
|
+
if error_code == 'AccessDeniedException':
|
|
102
|
+
logger.warning(f"Access denied to Inspector in {region}")
|
|
103
|
+
else:
|
|
104
|
+
logger.error(f"Error checking Inspector status in {region}: {e}")
|
|
105
|
+
return []
|
|
106
|
+
|
|
107
|
+
def _evaluate_resource_compliance(
|
|
108
|
+
self,
|
|
109
|
+
resource: Dict[str, Any],
|
|
110
|
+
aws_factory: AWSClientFactory,
|
|
111
|
+
region: str
|
|
112
|
+
) -> ComplianceResult:
|
|
113
|
+
"""Evaluate if Amazon Inspector is enabled."""
|
|
114
|
+
inspector_id = resource.get('InspectorId', 'none')
|
|
115
|
+
status = resource.get('Status', 'DISABLED')
|
|
116
|
+
resource_types = resource.get('ResourceTypes', [])
|
|
117
|
+
|
|
118
|
+
# Check if Inspector is enabled
|
|
119
|
+
is_compliant = status == 'ENABLED' and len(resource_types) > 0
|
|
120
|
+
|
|
121
|
+
if is_compliant:
|
|
122
|
+
evaluation_reason = (
|
|
123
|
+
f"Amazon Inspector is enabled in {region}. "
|
|
124
|
+
f"Scanning resource types: {', '.join(resource_types)}"
|
|
125
|
+
)
|
|
126
|
+
compliance_status = ComplianceStatus.COMPLIANT
|
|
127
|
+
else:
|
|
128
|
+
if inspector_id == 'none' or status == 'DISABLED':
|
|
129
|
+
evaluation_reason = f"Amazon Inspector is not enabled in {region}."
|
|
130
|
+
elif len(resource_types) == 0:
|
|
131
|
+
evaluation_reason = f"Amazon Inspector is enabled but no resource types are being scanned in {region}."
|
|
132
|
+
else:
|
|
133
|
+
evaluation_reason = f"Amazon Inspector status is {status} in {region}."
|
|
134
|
+
compliance_status = ComplianceStatus.NON_COMPLIANT
|
|
135
|
+
|
|
136
|
+
return ComplianceResult(
|
|
137
|
+
resource_id=inspector_id,
|
|
138
|
+
resource_type="AWS::Inspector::AssessmentTarget",
|
|
139
|
+
compliance_status=compliance_status,
|
|
140
|
+
evaluation_reason=evaluation_reason,
|
|
141
|
+
config_rule_name=self.rule_name,
|
|
142
|
+
region=region
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def _get_rule_remediation_steps(self) -> List[str]:
|
|
146
|
+
"""Get remediation steps for enabling Amazon Inspector."""
|
|
147
|
+
return [
|
|
148
|
+
"1. Enable Amazon Inspector v2 in the AWS Console:",
|
|
149
|
+
" - Navigate to Amazon Inspector service",
|
|
150
|
+
" - Click 'Get Started' or 'Enable Inspector'",
|
|
151
|
+
" - Select resource types to scan:",
|
|
152
|
+
" * EC2 instances (for OS vulnerabilities)",
|
|
153
|
+
" * ECR container images (for container vulnerabilities)",
|
|
154
|
+
" * Lambda functions (for code vulnerabilities)",
|
|
155
|
+
" - Click 'Enable'",
|
|
156
|
+
"",
|
|
157
|
+
"2. Enable Inspector using AWS CLI:",
|
|
158
|
+
" aws inspector2 enable \\",
|
|
159
|
+
" --resource-types EC2 ECR LAMBDA \\",
|
|
160
|
+
" --region <region>",
|
|
161
|
+
"",
|
|
162
|
+
"3. Verify Inspector is scanning resources:",
|
|
163
|
+
" aws inspector2 list-coverage \\",
|
|
164
|
+
" --region <region>",
|
|
165
|
+
"",
|
|
166
|
+
"4. Configure finding aggregation (optional):",
|
|
167
|
+
" - Set up cross-region aggregation for centralized findings",
|
|
168
|
+
" - Configure finding suppression rules for false positives",
|
|
169
|
+
"",
|
|
170
|
+
"5. Set up notifications:",
|
|
171
|
+
" - Create EventBridge rules to route findings to SNS/Slack",
|
|
172
|
+
" - Configure Security Hub integration for centralized findings",
|
|
173
|
+
"",
|
|
174
|
+
"6. Review findings regularly:",
|
|
175
|
+
" - Critical and High severity findings should be addressed immediately",
|
|
176
|
+
" - Medium findings within 30 days",
|
|
177
|
+
" - Low findings within 90 days",
|
|
178
|
+
"",
|
|
179
|
+
"Priority: HIGH - Vulnerability scanning is critical for security",
|
|
180
|
+
"Effort: Low - Can be enabled in minutes, ongoing effort for remediation",
|
|
181
|
+
"",
|
|
182
|
+
"AWS Documentation:",
|
|
183
|
+
"https://docs.aws.amazon.com/inspector/latest/user/getting_started_tutorial.html"
|
|
184
|
+
]
|