regscale-cli 6.27.3.0__py3-none-any.whl → 6.28.0.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.
Potentially problematic release.
This version of regscale-cli might be problematic. Click here for more details.
- regscale/_version.py +1 -1
- regscale/core/app/utils/app_utils.py +11 -2
- regscale/dev/cli.py +26 -0
- regscale/dev/version.py +72 -0
- regscale/integrations/commercial/__init__.py +15 -1
- regscale/integrations/commercial/amazon/amazon/__init__.py +0 -0
- regscale/integrations/commercial/amazon/amazon/common.py +204 -0
- regscale/integrations/commercial/amazon/common.py +48 -58
- regscale/integrations/commercial/aws/audit_manager_compliance.py +2671 -0
- regscale/integrations/commercial/aws/cli.py +3093 -55
- regscale/integrations/commercial/aws/cloudtrail_control_mappings.py +333 -0
- regscale/integrations/commercial/aws/cloudtrail_evidence.py +501 -0
- regscale/integrations/commercial/aws/cloudwatch_control_mappings.py +357 -0
- regscale/integrations/commercial/aws/cloudwatch_evidence.py +490 -0
- regscale/integrations/commercial/aws/config_compliance.py +914 -0
- regscale/integrations/commercial/aws/conformance_pack_mappings.py +198 -0
- regscale/integrations/commercial/aws/evidence_generator.py +283 -0
- regscale/integrations/commercial/aws/guardduty_control_mappings.py +340 -0
- regscale/integrations/commercial/aws/guardduty_evidence.py +1053 -0
- regscale/integrations/commercial/aws/iam_control_mappings.py +368 -0
- regscale/integrations/commercial/aws/iam_evidence.py +574 -0
- regscale/integrations/commercial/aws/inventory/__init__.py +223 -22
- regscale/integrations/commercial/aws/inventory/base.py +107 -5
- regscale/integrations/commercial/aws/inventory/resources/audit_manager.py +513 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudtrail.py +315 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudtrail_logs_metadata.py +476 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudwatch.py +191 -0
- regscale/integrations/commercial/aws/inventory/resources/compute.py +66 -9
- regscale/integrations/commercial/aws/inventory/resources/config.py +464 -0
- regscale/integrations/commercial/aws/inventory/resources/containers.py +74 -9
- regscale/integrations/commercial/aws/inventory/resources/database.py +106 -31
- regscale/integrations/commercial/aws/inventory/resources/guardduty.py +286 -0
- regscale/integrations/commercial/aws/inventory/resources/iam.py +470 -0
- regscale/integrations/commercial/aws/inventory/resources/inspector.py +476 -0
- regscale/integrations/commercial/aws/inventory/resources/integration.py +175 -61
- regscale/integrations/commercial/aws/inventory/resources/kms.py +447 -0
- regscale/integrations/commercial/aws/inventory/resources/networking.py +103 -67
- regscale/integrations/commercial/aws/inventory/resources/s3.py +394 -0
- regscale/integrations/commercial/aws/inventory/resources/security.py +268 -72
- regscale/integrations/commercial/aws/inventory/resources/securityhub.py +473 -0
- regscale/integrations/commercial/aws/inventory/resources/storage.py +53 -29
- regscale/integrations/commercial/aws/inventory/resources/systems_manager.py +657 -0
- regscale/integrations/commercial/aws/inventory/resources/vpc.py +655 -0
- regscale/integrations/commercial/aws/kms_control_mappings.py +288 -0
- regscale/integrations/commercial/aws/kms_evidence.py +879 -0
- regscale/integrations/commercial/aws/ocsf/__init__.py +7 -0
- regscale/integrations/commercial/aws/ocsf/constants.py +115 -0
- regscale/integrations/commercial/aws/ocsf/mapper.py +435 -0
- regscale/integrations/commercial/aws/org_control_mappings.py +286 -0
- regscale/integrations/commercial/aws/org_evidence.py +666 -0
- regscale/integrations/commercial/aws/s3_control_mappings.py +356 -0
- regscale/integrations/commercial/aws/s3_evidence.py +632 -0
- regscale/integrations/commercial/aws/scanner.py +851 -206
- regscale/integrations/commercial/aws/security_hub.py +319 -0
- regscale/integrations/commercial/aws/session_manager.py +282 -0
- regscale/integrations/commercial/aws/ssm_control_mappings.py +291 -0
- regscale/integrations/commercial/aws/ssm_evidence.py +492 -0
- regscale/integrations/compliance_integration.py +308 -38
- regscale/integrations/due_date_handler.py +3 -0
- regscale/integrations/scanner_integration.py +399 -84
- regscale/models/integration_models/cisa_kev_data.json +34 -4
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +17 -9
- regscale/models/regscale_models/assessment.py +2 -1
- regscale/models/regscale_models/control_objective.py +74 -5
- regscale/models/regscale_models/file.py +2 -0
- regscale/models/regscale_models/issue.py +2 -5
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/METADATA +1 -1
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/RECORD +112 -33
- tests/regscale/integrations/commercial/aws/__init__.py +0 -0
- tests/regscale/integrations/commercial/aws/test_audit_manager_compliance.py +1304 -0
- tests/regscale/integrations/commercial/aws/test_audit_manager_evidence_aggregation.py +341 -0
- tests/regscale/integrations/commercial/aws/test_aws_audit_manager_collector.py +1155 -0
- tests/regscale/integrations/commercial/aws/test_aws_cloudtrail_collector.py +534 -0
- tests/regscale/integrations/commercial/aws/test_aws_config_collector.py +400 -0
- tests/regscale/integrations/commercial/aws/test_aws_guardduty_collector.py +315 -0
- tests/regscale/integrations/commercial/aws/test_aws_iam_collector.py +458 -0
- tests/regscale/integrations/commercial/aws/test_aws_inspector_collector.py +353 -0
- tests/regscale/integrations/commercial/aws/test_aws_inventory_integration.py +530 -0
- tests/regscale/integrations/commercial/aws/test_aws_kms_collector.py +919 -0
- tests/regscale/integrations/commercial/aws/test_aws_s3_collector.py +722 -0
- tests/regscale/integrations/commercial/aws/test_aws_scanner_integration.py +722 -0
- tests/regscale/integrations/commercial/aws/test_aws_securityhub_collector.py +792 -0
- tests/regscale/integrations/commercial/aws/test_aws_systems_manager_collector.py +918 -0
- tests/regscale/integrations/commercial/aws/test_aws_vpc_collector.py +996 -0
- tests/regscale/integrations/commercial/aws/test_cli_evidence.py +431 -0
- tests/regscale/integrations/commercial/aws/test_cloudtrail_control_mappings.py +452 -0
- tests/regscale/integrations/commercial/aws/test_cloudtrail_evidence.py +788 -0
- tests/regscale/integrations/commercial/aws/test_config_compliance.py +298 -0
- tests/regscale/integrations/commercial/aws/test_conformance_pack_mappings.py +200 -0
- tests/regscale/integrations/commercial/aws/test_evidence_generator.py +386 -0
- tests/regscale/integrations/commercial/aws/test_guardduty_control_mappings.py +564 -0
- tests/regscale/integrations/commercial/aws/test_guardduty_evidence.py +1041 -0
- tests/regscale/integrations/commercial/aws/test_iam_control_mappings.py +718 -0
- tests/regscale/integrations/commercial/aws/test_iam_evidence.py +1375 -0
- tests/regscale/integrations/commercial/aws/test_kms_control_mappings.py +656 -0
- tests/regscale/integrations/commercial/aws/test_kms_evidence.py +1163 -0
- tests/regscale/integrations/commercial/aws/test_ocsf_mapper.py +370 -0
- tests/regscale/integrations/commercial/aws/test_org_control_mappings.py +546 -0
- tests/regscale/integrations/commercial/aws/test_org_evidence.py +1240 -0
- tests/regscale/integrations/commercial/aws/test_s3_control_mappings.py +672 -0
- tests/regscale/integrations/commercial/aws/test_s3_evidence.py +987 -0
- tests/regscale/integrations/commercial/aws/test_scanner_evidence.py +373 -0
- tests/regscale/integrations/commercial/aws/test_security_hub_config_filtering.py +539 -0
- tests/regscale/integrations/commercial/aws/test_session_manager.py +516 -0
- tests/regscale/integrations/commercial/aws/test_ssm_control_mappings.py +588 -0
- tests/regscale/integrations/commercial/aws/test_ssm_evidence.py +735 -0
- tests/regscale/integrations/commercial/test_aws.py +55 -56
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1163 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Unit tests for AWS KMS Evidence Integration."""
|
|
4
|
+
|
|
5
|
+
import gzip
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import time
|
|
9
|
+
from datetime import datetime, timedelta
|
|
10
|
+
from io import BytesIO
|
|
11
|
+
from unittest.mock import MagicMock, Mock, call, mock_open, patch
|
|
12
|
+
|
|
13
|
+
import pytest
|
|
14
|
+
from botocore.exceptions import ClientError
|
|
15
|
+
|
|
16
|
+
from regscale.integrations.commercial.aws.kms_evidence import (
|
|
17
|
+
AWSKMSEvidenceIntegration,
|
|
18
|
+
KMSComplianceItem,
|
|
19
|
+
CACHE_TTL_SECONDS,
|
|
20
|
+
KMS_CACHE_FILE,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
PATH = "regscale.integrations.commercial.aws.kms_evidence"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TestKMSComplianceItem:
|
|
27
|
+
"""Test cases for KMSComplianceItem class."""
|
|
28
|
+
|
|
29
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
30
|
+
def test_init_with_complete_data(self, mock_mapper_class):
|
|
31
|
+
"""Test initialization with complete KMS key data."""
|
|
32
|
+
mock_mapper = MagicMock()
|
|
33
|
+
mock_mapper.assess_key_compliance.return_value = {
|
|
34
|
+
"SC-12": "PASS",
|
|
35
|
+
"SC-13": "PASS",
|
|
36
|
+
"SC-28": "PASS",
|
|
37
|
+
}
|
|
38
|
+
mock_mapper.get_control_description.side_effect = lambda x: f"{x} description"
|
|
39
|
+
mock_mapper.framework = "NIST800-53R5"
|
|
40
|
+
|
|
41
|
+
key_data = {
|
|
42
|
+
"KeyId": "12345678-1234-1234-1234-123456789012",
|
|
43
|
+
"Arn": "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012",
|
|
44
|
+
"KeyState": "Enabled",
|
|
45
|
+
"RotationEnabled": True,
|
|
46
|
+
"KeyManager": "CUSTOMER",
|
|
47
|
+
"Description": "Test KMS Key",
|
|
48
|
+
"Tags": [{"TagKey": "Name", "TagValue": "TestKey"}],
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
item = KMSComplianceItem(key_data, mock_mapper)
|
|
52
|
+
|
|
53
|
+
assert item.resource_id == "12345678-1234-1234-1234-123456789012"
|
|
54
|
+
assert "TestKey" in item.resource_name
|
|
55
|
+
assert item.control_id in ["SC-12", "SC-13", "SC-28"]
|
|
56
|
+
assert item.compliance_result == "PASS"
|
|
57
|
+
assert item.severity is None
|
|
58
|
+
assert item.framework == "NIST800-53R5"
|
|
59
|
+
|
|
60
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
61
|
+
def test_init_with_minimal_data(self, mock_mapper_class):
|
|
62
|
+
"""Test initialization with minimal KMS key data."""
|
|
63
|
+
mock_mapper = MagicMock()
|
|
64
|
+
mock_mapper.assess_key_compliance.return_value = {}
|
|
65
|
+
mock_mapper.get_control_description.return_value = "Test description"
|
|
66
|
+
mock_mapper.framework = "NIST800-53R5"
|
|
67
|
+
|
|
68
|
+
key_data = {}
|
|
69
|
+
|
|
70
|
+
item = KMSComplianceItem(key_data, mock_mapper)
|
|
71
|
+
|
|
72
|
+
assert item.resource_id == ""
|
|
73
|
+
assert "KMS Key" in item.resource_name
|
|
74
|
+
assert item.control_id == "SC-12"
|
|
75
|
+
assert item.compliance_result == "PASS"
|
|
76
|
+
|
|
77
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
78
|
+
def test_resource_name_with_tag(self, mock_mapper_class):
|
|
79
|
+
"""Test resource name extraction with tags."""
|
|
80
|
+
mock_mapper = MagicMock()
|
|
81
|
+
mock_mapper.assess_key_compliance.return_value = {}
|
|
82
|
+
mock_mapper.framework = "NIST800-53R5"
|
|
83
|
+
|
|
84
|
+
key_data = {
|
|
85
|
+
"KeyId": "12345678-1234-1234-1234-123456789012",
|
|
86
|
+
"Tags": [{"TagKey": "Name", "TagValue": "ProductionKey"}],
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
item = KMSComplianceItem(key_data, mock_mapper)
|
|
90
|
+
|
|
91
|
+
assert "ProductionKey" in item.resource_name
|
|
92
|
+
assert "12345678" in item.resource_name
|
|
93
|
+
|
|
94
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
95
|
+
def test_resource_name_with_description(self, mock_mapper_class):
|
|
96
|
+
"""Test resource name extraction with description."""
|
|
97
|
+
mock_mapper = MagicMock()
|
|
98
|
+
mock_mapper.assess_key_compliance.return_value = {}
|
|
99
|
+
mock_mapper.framework = "NIST800-53R5"
|
|
100
|
+
|
|
101
|
+
key_data = {
|
|
102
|
+
"KeyId": "12345678-1234-1234-1234-123456789012",
|
|
103
|
+
"Description": "Key for encrypting sensitive data in production environment",
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
item = KMSComplianceItem(key_data, mock_mapper)
|
|
107
|
+
|
|
108
|
+
assert "Key for encrypting sensitive data in production" in item.resource_name
|
|
109
|
+
assert "12345678" in item.resource_name
|
|
110
|
+
|
|
111
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
112
|
+
def test_control_id_first_failing_control(self, mock_mapper_class):
|
|
113
|
+
"""Test control_id property returns first failing control."""
|
|
114
|
+
mock_mapper = MagicMock()
|
|
115
|
+
mock_mapper.assess_key_compliance.return_value = {
|
|
116
|
+
"SC-12": "PASS",
|
|
117
|
+
"SC-13": "FAIL",
|
|
118
|
+
"SC-28": "PASS",
|
|
119
|
+
}
|
|
120
|
+
mock_mapper.framework = "NIST800-53R5"
|
|
121
|
+
|
|
122
|
+
key_data = {"KeyId": "test-key"}
|
|
123
|
+
|
|
124
|
+
item = KMSComplianceItem(key_data, mock_mapper)
|
|
125
|
+
|
|
126
|
+
assert item.control_id == "SC-13"
|
|
127
|
+
|
|
128
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
129
|
+
def test_control_id_all_passing(self, mock_mapper_class):
|
|
130
|
+
"""Test control_id property when all controls pass."""
|
|
131
|
+
mock_mapper = MagicMock()
|
|
132
|
+
mock_mapper.assess_key_compliance.return_value = {
|
|
133
|
+
"SC-12": "PASS",
|
|
134
|
+
"SC-13": "PASS",
|
|
135
|
+
"SC-28": "PASS",
|
|
136
|
+
}
|
|
137
|
+
mock_mapper.framework = "NIST800-53R5"
|
|
138
|
+
|
|
139
|
+
key_data = {"KeyId": "test-key"}
|
|
140
|
+
|
|
141
|
+
item = KMSComplianceItem(key_data, mock_mapper)
|
|
142
|
+
|
|
143
|
+
assert item.control_id in ["SC-12", "SC-13", "SC-28"]
|
|
144
|
+
|
|
145
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
146
|
+
def test_compliance_result_fail(self, mock_mapper_class):
|
|
147
|
+
"""Test compliance_result property with failures."""
|
|
148
|
+
mock_mapper = MagicMock()
|
|
149
|
+
mock_mapper.assess_key_compliance.return_value = {
|
|
150
|
+
"SC-12": "PASS",
|
|
151
|
+
"SC-13": "FAIL",
|
|
152
|
+
"SC-28": "PASS",
|
|
153
|
+
}
|
|
154
|
+
mock_mapper.framework = "NIST800-53R5"
|
|
155
|
+
|
|
156
|
+
key_data = {"KeyId": "test-key"}
|
|
157
|
+
|
|
158
|
+
item = KMSComplianceItem(key_data, mock_mapper)
|
|
159
|
+
|
|
160
|
+
assert item.compliance_result == "FAIL"
|
|
161
|
+
|
|
162
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
163
|
+
def test_compliance_result_pass(self, mock_mapper_class):
|
|
164
|
+
"""Test compliance_result property with all passing."""
|
|
165
|
+
mock_mapper = MagicMock()
|
|
166
|
+
mock_mapper.assess_key_compliance.return_value = {
|
|
167
|
+
"SC-12": "PASS",
|
|
168
|
+
"SC-13": "PASS",
|
|
169
|
+
"SC-28": "PASS",
|
|
170
|
+
}
|
|
171
|
+
mock_mapper.framework = "NIST800-53R5"
|
|
172
|
+
|
|
173
|
+
key_data = {"KeyId": "test-key"}
|
|
174
|
+
|
|
175
|
+
item = KMSComplianceItem(key_data, mock_mapper)
|
|
176
|
+
|
|
177
|
+
assert item.compliance_result == "PASS"
|
|
178
|
+
|
|
179
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
180
|
+
def test_severity_sc12_failure(self, mock_mapper_class):
|
|
181
|
+
"""Test severity for SC-12 failure."""
|
|
182
|
+
mock_mapper = MagicMock()
|
|
183
|
+
mock_mapper.assess_key_compliance.return_value = {
|
|
184
|
+
"SC-12": "FAIL",
|
|
185
|
+
"SC-13": "PASS",
|
|
186
|
+
"SC-28": "PASS",
|
|
187
|
+
}
|
|
188
|
+
mock_mapper.framework = "NIST800-53R5"
|
|
189
|
+
|
|
190
|
+
key_data = {"KeyId": "test-key"}
|
|
191
|
+
|
|
192
|
+
item = KMSComplianceItem(key_data, mock_mapper)
|
|
193
|
+
|
|
194
|
+
assert item.severity == "HIGH"
|
|
195
|
+
|
|
196
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
197
|
+
def test_severity_sc13_failure(self, mock_mapper_class):
|
|
198
|
+
"""Test severity for SC-13 failure."""
|
|
199
|
+
mock_mapper = MagicMock()
|
|
200
|
+
mock_mapper.assess_key_compliance.return_value = {
|
|
201
|
+
"SC-12": "PASS",
|
|
202
|
+
"SC-13": "FAIL",
|
|
203
|
+
"SC-28": "PASS",
|
|
204
|
+
}
|
|
205
|
+
mock_mapper.framework = "NIST800-53R5"
|
|
206
|
+
|
|
207
|
+
key_data = {"KeyId": "test-key"}
|
|
208
|
+
|
|
209
|
+
item = KMSComplianceItem(key_data, mock_mapper)
|
|
210
|
+
|
|
211
|
+
assert item.severity == "MEDIUM"
|
|
212
|
+
|
|
213
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
214
|
+
def test_severity_sc28_failure(self, mock_mapper_class):
|
|
215
|
+
"""Test severity for SC-28 failure."""
|
|
216
|
+
mock_mapper = MagicMock()
|
|
217
|
+
mock_mapper.assess_key_compliance.return_value = {
|
|
218
|
+
"SC-12": "PASS",
|
|
219
|
+
"SC-13": "PASS",
|
|
220
|
+
"SC-28": "FAIL",
|
|
221
|
+
}
|
|
222
|
+
mock_mapper.framework = "NIST800-53R5"
|
|
223
|
+
|
|
224
|
+
key_data = {"KeyId": "test-key"}
|
|
225
|
+
|
|
226
|
+
item = KMSComplianceItem(key_data, mock_mapper)
|
|
227
|
+
|
|
228
|
+
assert item.severity == "MEDIUM"
|
|
229
|
+
|
|
230
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
231
|
+
def test_severity_pass(self, mock_mapper_class):
|
|
232
|
+
"""Test severity when all controls pass."""
|
|
233
|
+
mock_mapper = MagicMock()
|
|
234
|
+
mock_mapper.assess_key_compliance.return_value = {
|
|
235
|
+
"SC-12": "PASS",
|
|
236
|
+
"SC-13": "PASS",
|
|
237
|
+
"SC-28": "PASS",
|
|
238
|
+
}
|
|
239
|
+
mock_mapper.framework = "NIST800-53R5"
|
|
240
|
+
|
|
241
|
+
key_data = {"KeyId": "test-key"}
|
|
242
|
+
|
|
243
|
+
item = KMSComplianceItem(key_data, mock_mapper)
|
|
244
|
+
|
|
245
|
+
assert item.severity is None
|
|
246
|
+
|
|
247
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
248
|
+
def test_description_property(self, mock_mapper_class):
|
|
249
|
+
"""Test description property generates HTML."""
|
|
250
|
+
mock_mapper = MagicMock()
|
|
251
|
+
mock_mapper.assess_key_compliance.return_value = {
|
|
252
|
+
"SC-12": "FAIL",
|
|
253
|
+
"SC-13": "PASS",
|
|
254
|
+
}
|
|
255
|
+
mock_mapper.get_control_description.side_effect = lambda x: f"{x} description"
|
|
256
|
+
mock_mapper.framework = "NIST800-53R5"
|
|
257
|
+
|
|
258
|
+
key_data = {
|
|
259
|
+
"KeyId": "12345678-1234-1234-1234-123456789012",
|
|
260
|
+
"Arn": "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012",
|
|
261
|
+
"KeyState": "Enabled",
|
|
262
|
+
"RotationEnabled": False,
|
|
263
|
+
"KeyManager": "CUSTOMER",
|
|
264
|
+
"Description": "Test key",
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
item = KMSComplianceItem(key_data, mock_mapper)
|
|
268
|
+
description = item.description
|
|
269
|
+
|
|
270
|
+
assert "AWS KMS Key Compliance Assessment" in description
|
|
271
|
+
assert "12345678-1234-1234-1234-123456789012" in description
|
|
272
|
+
assert "Rotation Enabled" in description
|
|
273
|
+
assert "No" in description
|
|
274
|
+
assert "Control Compliance Results" in description
|
|
275
|
+
assert "SC-12" in description
|
|
276
|
+
assert "FAIL" in description
|
|
277
|
+
assert "Remediation Guidance" in description
|
|
278
|
+
|
|
279
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
280
|
+
def test_description_with_remediation_rotation(self, mock_mapper_class):
|
|
281
|
+
"""Test description includes rotation remediation."""
|
|
282
|
+
mock_mapper = MagicMock()
|
|
283
|
+
mock_mapper.assess_key_compliance.return_value = {
|
|
284
|
+
"SC-12": "FAIL",
|
|
285
|
+
}
|
|
286
|
+
mock_mapper.get_control_description.return_value = "SC-12 description"
|
|
287
|
+
mock_mapper.framework = "NIST800-53R5"
|
|
288
|
+
|
|
289
|
+
key_data = {
|
|
290
|
+
"KeyId": "test-key",
|
|
291
|
+
"Arn": "arn:aws:kms:us-east-1:123456789012:key/test-key",
|
|
292
|
+
"KeyState": "Enabled",
|
|
293
|
+
"RotationEnabled": False,
|
|
294
|
+
"KeyManager": "CUSTOMER",
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
item = KMSComplianceItem(key_data, mock_mapper)
|
|
298
|
+
description = item.description
|
|
299
|
+
|
|
300
|
+
assert "Enable automatic key rotation" in description
|
|
301
|
+
|
|
302
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
303
|
+
def test_description_with_remediation_key_state(self, mock_mapper_class):
|
|
304
|
+
"""Test description includes key state remediation."""
|
|
305
|
+
mock_mapper = MagicMock()
|
|
306
|
+
mock_mapper.assess_key_compliance.return_value = {
|
|
307
|
+
"SC-12": "FAIL",
|
|
308
|
+
}
|
|
309
|
+
mock_mapper.get_control_description.return_value = "SC-12 description"
|
|
310
|
+
mock_mapper.framework = "NIST800-53R5"
|
|
311
|
+
|
|
312
|
+
key_data = {
|
|
313
|
+
"KeyId": "test-key",
|
|
314
|
+
"Arn": "arn:aws:kms:us-east-1:123456789012:key/test-key",
|
|
315
|
+
"KeyState": "PendingDeletion",
|
|
316
|
+
"RotationEnabled": True,
|
|
317
|
+
"KeyManager": "CUSTOMER",
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
item = KMSComplianceItem(key_data, mock_mapper)
|
|
321
|
+
description = item.description
|
|
322
|
+
|
|
323
|
+
assert "Key is PendingDeletion" in description
|
|
324
|
+
assert "review key lifecycle" in description
|
|
325
|
+
|
|
326
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
327
|
+
def test_description_with_remediation_key_spec(self, mock_mapper_class):
|
|
328
|
+
"""Test description includes key spec remediation."""
|
|
329
|
+
mock_mapper = MagicMock()
|
|
330
|
+
mock_mapper.assess_key_compliance.return_value = {
|
|
331
|
+
"SC-13": "FAIL",
|
|
332
|
+
}
|
|
333
|
+
mock_mapper.get_control_description.return_value = "SC-13 description"
|
|
334
|
+
mock_mapper.framework = "NIST800-53R5"
|
|
335
|
+
|
|
336
|
+
key_data = {
|
|
337
|
+
"KeyId": "test-key",
|
|
338
|
+
"Arn": "arn:aws:kms:us-east-1:123456789012:key/test-key",
|
|
339
|
+
"KeyState": "Enabled",
|
|
340
|
+
"RotationEnabled": True,
|
|
341
|
+
"KeyManager": "CUSTOMER",
|
|
342
|
+
"KeySpec": "RSA_1024",
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
item = KMSComplianceItem(key_data, mock_mapper)
|
|
346
|
+
description = item.description
|
|
347
|
+
|
|
348
|
+
assert "Review key specification" in description
|
|
349
|
+
assert "RSA_1024" in description
|
|
350
|
+
assert "FIPS-validated" in description
|
|
351
|
+
|
|
352
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
353
|
+
def test_extract_region_from_arn(self, mock_mapper_class):
|
|
354
|
+
"""Test extracting region from ARN."""
|
|
355
|
+
result = KMSComplianceItem._extract_region_from_arn("arn:aws:kms:us-west-2:123456789012:key/test-key")
|
|
356
|
+
assert result == "us-west-2"
|
|
357
|
+
|
|
358
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
359
|
+
def test_extract_region_from_invalid_arn(self, mock_mapper_class):
|
|
360
|
+
"""Test extracting region from invalid ARN."""
|
|
361
|
+
result = KMSComplianceItem._extract_region_from_arn("invalid-arn")
|
|
362
|
+
assert result == "unknown"
|
|
363
|
+
|
|
364
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
365
|
+
def test_extract_account_from_arn(self, mock_mapper_class):
|
|
366
|
+
"""Test extracting account from ARN."""
|
|
367
|
+
result = KMSComplianceItem._extract_account_from_arn("arn:aws:kms:us-east-1:123456789012:key/test-key")
|
|
368
|
+
assert result == "123456789012"
|
|
369
|
+
|
|
370
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
371
|
+
def test_extract_account_from_invalid_arn(self, mock_mapper_class):
|
|
372
|
+
"""Test extracting account from invalid ARN."""
|
|
373
|
+
result = KMSComplianceItem._extract_account_from_arn("invalid-arn")
|
|
374
|
+
assert result == "unknown"
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
class TestAWSKMSEvidenceIntegrationInit:
|
|
378
|
+
"""Test cases for AWSKMSEvidenceIntegration initialization."""
|
|
379
|
+
|
|
380
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
381
|
+
@patch(f"{PATH}.boto3.Session")
|
|
382
|
+
def test_init_with_defaults(self, mock_session_class, mock_mapper_class):
|
|
383
|
+
"""Test initialization with default parameters."""
|
|
384
|
+
mock_session = MagicMock()
|
|
385
|
+
mock_client = MagicMock()
|
|
386
|
+
mock_session.client.return_value = mock_client
|
|
387
|
+
mock_session_class.return_value = mock_session
|
|
388
|
+
|
|
389
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
390
|
+
|
|
391
|
+
assert integration.plan_id == 123
|
|
392
|
+
assert integration.region == "us-east-1"
|
|
393
|
+
assert integration.title == "AWS KMS"
|
|
394
|
+
assert integration.collect_evidence is False
|
|
395
|
+
assert integration.evidence_as_attachments is True
|
|
396
|
+
assert integration.evidence_control_ids is None
|
|
397
|
+
assert integration.evidence_frequency == 30
|
|
398
|
+
assert integration.force_refresh is False
|
|
399
|
+
assert integration.account_id is None
|
|
400
|
+
assert integration.tags == {}
|
|
401
|
+
assert integration.raw_kms_data == []
|
|
402
|
+
|
|
403
|
+
mock_session_class.assert_called_once()
|
|
404
|
+
mock_mapper_class.assert_called_once_with(framework="NIST800-53R5")
|
|
405
|
+
|
|
406
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
407
|
+
@patch(f"{PATH}.boto3.Session")
|
|
408
|
+
def test_init_with_explicit_credentials(self, mock_session_class, mock_mapper_class):
|
|
409
|
+
"""Test initialization with explicit AWS credentials."""
|
|
410
|
+
mock_session = MagicMock()
|
|
411
|
+
mock_client = MagicMock()
|
|
412
|
+
mock_session.client.return_value = mock_client
|
|
413
|
+
mock_session_class.return_value = mock_session
|
|
414
|
+
|
|
415
|
+
integration = AWSKMSEvidenceIntegration(
|
|
416
|
+
plan_id=123,
|
|
417
|
+
region="us-west-2",
|
|
418
|
+
aws_access_key_id="AKIAIOSFODNN7EXAMPLE",
|
|
419
|
+
aws_secret_access_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
|
420
|
+
aws_session_token="session-token",
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
assert integration.region == "us-west-2"
|
|
424
|
+
|
|
425
|
+
mock_session_class.assert_called_once_with(
|
|
426
|
+
region_name="us-west-2",
|
|
427
|
+
aws_access_key_id="AKIAIOSFODNN7EXAMPLE",
|
|
428
|
+
aws_secret_access_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
|
429
|
+
aws_session_token="session-token",
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
433
|
+
@patch(f"{PATH}.boto3.Session")
|
|
434
|
+
def test_init_with_profile(self, mock_session_class, mock_mapper_class):
|
|
435
|
+
"""Test initialization with AWS profile."""
|
|
436
|
+
mock_session = MagicMock()
|
|
437
|
+
mock_client = MagicMock()
|
|
438
|
+
mock_session.client.return_value = mock_client
|
|
439
|
+
mock_session_class.return_value = mock_session
|
|
440
|
+
|
|
441
|
+
AWSKMSEvidenceIntegration(plan_id=123, profile="test-profile")
|
|
442
|
+
|
|
443
|
+
mock_session_class.assert_called_once_with(profile_name="test-profile", region_name="us-east-1")
|
|
444
|
+
|
|
445
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
446
|
+
@patch(f"{PATH}.boto3.Session")
|
|
447
|
+
def test_init_with_all_options(self, mock_session_class, mock_mapper_class):
|
|
448
|
+
"""Test initialization with all optional parameters."""
|
|
449
|
+
mock_session = MagicMock()
|
|
450
|
+
mock_client = MagicMock()
|
|
451
|
+
mock_session.client.return_value = mock_client
|
|
452
|
+
mock_session_class.return_value = mock_session
|
|
453
|
+
|
|
454
|
+
integration = AWSKMSEvidenceIntegration(
|
|
455
|
+
plan_id=456,
|
|
456
|
+
region="eu-west-1",
|
|
457
|
+
framework="ISO27001",
|
|
458
|
+
create_issues=False,
|
|
459
|
+
update_control_status=False,
|
|
460
|
+
create_poams=True,
|
|
461
|
+
parent_module="assessments",
|
|
462
|
+
collect_evidence=True,
|
|
463
|
+
evidence_as_attachments=False,
|
|
464
|
+
evidence_control_ids=["SC-12", "SC-13"],
|
|
465
|
+
evidence_frequency=60,
|
|
466
|
+
force_refresh=True,
|
|
467
|
+
account_id="123456789012",
|
|
468
|
+
tags={"Environment": "Production"},
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
assert integration.plan_id == 456
|
|
472
|
+
assert integration.region == "eu-west-1"
|
|
473
|
+
assert integration.framework == "ISO27001"
|
|
474
|
+
assert integration.create_issues is False
|
|
475
|
+
assert integration.update_control_status is False
|
|
476
|
+
assert integration.create_poams is True
|
|
477
|
+
assert integration.collect_evidence is True
|
|
478
|
+
assert integration.evidence_as_attachments is False
|
|
479
|
+
assert integration.evidence_control_ids == ["SC-12", "SC-13"]
|
|
480
|
+
assert integration.evidence_frequency == 60
|
|
481
|
+
assert integration.force_refresh is True
|
|
482
|
+
assert integration.account_id == "123456789012"
|
|
483
|
+
assert integration.tags == {"Environment": "Production"}
|
|
484
|
+
|
|
485
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
486
|
+
@patch(f"{PATH}.boto3.Session")
|
|
487
|
+
def test_init_client_creation_failure(self, mock_session_class, mock_mapper_class):
|
|
488
|
+
"""Test initialization when client creation fails."""
|
|
489
|
+
mock_session = MagicMock()
|
|
490
|
+
mock_session.client.side_effect = Exception("Failed to create KMS client")
|
|
491
|
+
mock_session_class.return_value = mock_session
|
|
492
|
+
|
|
493
|
+
with pytest.raises(Exception) as exc_info:
|
|
494
|
+
AWSKMSEvidenceIntegration(plan_id=123)
|
|
495
|
+
|
|
496
|
+
assert "Failed to create KMS client" in str(exc_info.value)
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
class TestCacheManagement:
|
|
500
|
+
"""Test cases for cache management methods."""
|
|
501
|
+
|
|
502
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
503
|
+
@patch(f"{PATH}.boto3.Session")
|
|
504
|
+
@patch(f"{PATH}.os.path.exists")
|
|
505
|
+
def test_is_cache_valid_no_file(self, mock_exists, mock_session_class, mock_mapper_class):
|
|
506
|
+
"""Test cache validation when file does not exist."""
|
|
507
|
+
mock_exists.return_value = False
|
|
508
|
+
mock_session = MagicMock()
|
|
509
|
+
mock_session.client.return_value = MagicMock()
|
|
510
|
+
mock_session_class.return_value = mock_session
|
|
511
|
+
|
|
512
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
513
|
+
|
|
514
|
+
assert integration._is_cache_valid() is False
|
|
515
|
+
|
|
516
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
517
|
+
@patch(f"{PATH}.boto3.Session")
|
|
518
|
+
@patch(f"{PATH}.os.path.exists")
|
|
519
|
+
@patch(f"{PATH}.os.path.getmtime")
|
|
520
|
+
@patch(f"{PATH}.time.time")
|
|
521
|
+
def test_is_cache_valid_expired(self, mock_time, mock_getmtime, mock_exists, mock_session_class, mock_mapper_class):
|
|
522
|
+
"""Test cache validation when cache is expired."""
|
|
523
|
+
mock_exists.return_value = True
|
|
524
|
+
mock_time.return_value = 1000000
|
|
525
|
+
mock_getmtime.return_value = 1000000 - CACHE_TTL_SECONDS - 100
|
|
526
|
+
mock_session = MagicMock()
|
|
527
|
+
mock_session.client.return_value = MagicMock()
|
|
528
|
+
mock_session_class.return_value = mock_session
|
|
529
|
+
|
|
530
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
531
|
+
|
|
532
|
+
assert integration._is_cache_valid() is False
|
|
533
|
+
|
|
534
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
535
|
+
@patch(f"{PATH}.boto3.Session")
|
|
536
|
+
@patch(f"{PATH}.os.path.exists")
|
|
537
|
+
@patch(f"{PATH}.os.path.getmtime")
|
|
538
|
+
@patch(f"{PATH}.time.time")
|
|
539
|
+
def test_is_cache_valid_fresh(self, mock_time, mock_getmtime, mock_exists, mock_session_class, mock_mapper_class):
|
|
540
|
+
"""Test cache validation when cache is fresh."""
|
|
541
|
+
mock_exists.return_value = True
|
|
542
|
+
mock_time.return_value = 1000000
|
|
543
|
+
mock_getmtime.return_value = 1000000 - 1000
|
|
544
|
+
mock_session = MagicMock()
|
|
545
|
+
mock_session.client.return_value = MagicMock()
|
|
546
|
+
mock_session_class.return_value = mock_session
|
|
547
|
+
|
|
548
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
549
|
+
|
|
550
|
+
assert integration._is_cache_valid() is True
|
|
551
|
+
|
|
552
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
553
|
+
@patch(f"{PATH}.boto3.Session")
|
|
554
|
+
def test_load_cached_data_success(self, mock_session_class, mock_mapper_class):
|
|
555
|
+
"""Test loading cached data successfully."""
|
|
556
|
+
mock_session = MagicMock()
|
|
557
|
+
mock_session.client.return_value = MagicMock()
|
|
558
|
+
mock_session_class.return_value = mock_session
|
|
559
|
+
|
|
560
|
+
test_data = [{"KeyId": "test-key", "KeyState": "Enabled"}]
|
|
561
|
+
mock_file = mock_open(read_data=json.dumps(test_data))
|
|
562
|
+
|
|
563
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
564
|
+
|
|
565
|
+
with patch("builtins.open", mock_file):
|
|
566
|
+
result = integration._load_cached_data()
|
|
567
|
+
|
|
568
|
+
assert result == test_data
|
|
569
|
+
|
|
570
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
571
|
+
@patch(f"{PATH}.boto3.Session")
|
|
572
|
+
def test_load_cached_data_json_error(self, mock_session_class, mock_mapper_class):
|
|
573
|
+
"""Test loading cached data with JSON decode error."""
|
|
574
|
+
mock_session = MagicMock()
|
|
575
|
+
mock_session.client.return_value = MagicMock()
|
|
576
|
+
mock_session_class.return_value = mock_session
|
|
577
|
+
|
|
578
|
+
mock_file = mock_open(read_data="invalid json")
|
|
579
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
580
|
+
|
|
581
|
+
with patch("builtins.open", mock_file):
|
|
582
|
+
result = integration._load_cached_data()
|
|
583
|
+
|
|
584
|
+
assert result == []
|
|
585
|
+
|
|
586
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
587
|
+
@patch(f"{PATH}.boto3.Session")
|
|
588
|
+
def test_load_cached_data_io_error(self, mock_session_class, mock_mapper_class):
|
|
589
|
+
"""Test loading cached data with IO error."""
|
|
590
|
+
mock_session = MagicMock()
|
|
591
|
+
mock_session.client.return_value = MagicMock()
|
|
592
|
+
mock_session_class.return_value = mock_session
|
|
593
|
+
|
|
594
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
595
|
+
|
|
596
|
+
with patch("builtins.open", side_effect=IOError("File not found")):
|
|
597
|
+
result = integration._load_cached_data()
|
|
598
|
+
|
|
599
|
+
assert result == []
|
|
600
|
+
|
|
601
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
602
|
+
@patch(f"{PATH}.boto3.Session")
|
|
603
|
+
@patch(f"{PATH}.os.makedirs")
|
|
604
|
+
def test_save_to_cache_success(self, mock_makedirs, mock_session_class, mock_mapper_class):
|
|
605
|
+
"""Test saving data to cache successfully."""
|
|
606
|
+
mock_session = MagicMock()
|
|
607
|
+
mock_session.client.return_value = MagicMock()
|
|
608
|
+
mock_session_class.return_value = mock_session
|
|
609
|
+
|
|
610
|
+
test_data = [{"KeyId": "test-key"}]
|
|
611
|
+
mock_file = mock_open()
|
|
612
|
+
|
|
613
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
614
|
+
|
|
615
|
+
with patch("builtins.open", mock_file):
|
|
616
|
+
integration._save_to_cache(test_data)
|
|
617
|
+
|
|
618
|
+
mock_makedirs.assert_called_once()
|
|
619
|
+
mock_file.assert_called_once()
|
|
620
|
+
|
|
621
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
622
|
+
@patch(f"{PATH}.boto3.Session")
|
|
623
|
+
@patch(f"{PATH}.os.makedirs")
|
|
624
|
+
def test_save_to_cache_io_error(self, mock_makedirs, mock_session_class, mock_mapper_class):
|
|
625
|
+
"""Test saving data to cache with IO error."""
|
|
626
|
+
mock_session = MagicMock()
|
|
627
|
+
mock_session.client.return_value = MagicMock()
|
|
628
|
+
mock_session_class.return_value = mock_session
|
|
629
|
+
|
|
630
|
+
test_data = [{"KeyId": "test-key"}]
|
|
631
|
+
|
|
632
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
633
|
+
|
|
634
|
+
with patch("builtins.open", side_effect=IOError("Permission denied")):
|
|
635
|
+
integration._save_to_cache(test_data)
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
class TestFetchKMSData:
|
|
639
|
+
"""Test cases for fetching KMS data."""
|
|
640
|
+
|
|
641
|
+
@patch("regscale.integrations.commercial.aws.inventory.resources.kms.KMSCollector")
|
|
642
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
643
|
+
@patch(f"{PATH}.boto3.Session")
|
|
644
|
+
def test_fetch_fresh_kms_data(self, mock_session_class, mock_mapper_class, mock_collector_class):
|
|
645
|
+
"""Test fetching fresh KMS data."""
|
|
646
|
+
mock_session = MagicMock()
|
|
647
|
+
mock_session.client.return_value = MagicMock()
|
|
648
|
+
mock_session_class.return_value = mock_session
|
|
649
|
+
|
|
650
|
+
test_data = [
|
|
651
|
+
{"KeyId": "key-1", "KeyState": "Enabled"},
|
|
652
|
+
{"KeyId": "key-2", "KeyState": "Enabled"},
|
|
653
|
+
]
|
|
654
|
+
|
|
655
|
+
mock_collector = MagicMock()
|
|
656
|
+
mock_collector.collect.return_value = {"Keys": test_data}
|
|
657
|
+
mock_collector_class.return_value = mock_collector
|
|
658
|
+
|
|
659
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123, region="us-east-1")
|
|
660
|
+
|
|
661
|
+
result = integration._fetch_fresh_kms_data()
|
|
662
|
+
|
|
663
|
+
assert len(result) == 2
|
|
664
|
+
assert result[0]["KeyId"] == "key-1"
|
|
665
|
+
mock_collector_class.assert_called_once()
|
|
666
|
+
|
|
667
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
668
|
+
@patch(f"{PATH}.boto3.Session")
|
|
669
|
+
def test_fetch_compliance_data_with_valid_cache(self, mock_session_class, mock_mapper_class):
|
|
670
|
+
"""Test fetching compliance data when cache is valid."""
|
|
671
|
+
mock_session = MagicMock()
|
|
672
|
+
mock_session.client.return_value = MagicMock()
|
|
673
|
+
mock_session_class.return_value = mock_session
|
|
674
|
+
|
|
675
|
+
test_data = [{"KeyId": "cached-key"}]
|
|
676
|
+
|
|
677
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
678
|
+
integration._is_cache_valid = Mock(return_value=True)
|
|
679
|
+
integration._load_cached_data = Mock(return_value=test_data)
|
|
680
|
+
|
|
681
|
+
result = integration.fetch_compliance_data()
|
|
682
|
+
|
|
683
|
+
assert result == test_data
|
|
684
|
+
assert integration.raw_kms_data == test_data
|
|
685
|
+
|
|
686
|
+
@patch("regscale.integrations.commercial.aws.inventory.resources.kms.KMSCollector")
|
|
687
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
688
|
+
@patch(f"{PATH}.boto3.Session")
|
|
689
|
+
def test_fetch_compliance_data_force_refresh(self, mock_session_class, mock_mapper_class, mock_collector_class):
|
|
690
|
+
"""Test fetching compliance data with force refresh."""
|
|
691
|
+
mock_session = MagicMock()
|
|
692
|
+
mock_session.client.return_value = MagicMock()
|
|
693
|
+
mock_session_class.return_value = mock_session
|
|
694
|
+
|
|
695
|
+
test_data = [{"KeyId": "fresh-key"}]
|
|
696
|
+
|
|
697
|
+
mock_collector = MagicMock()
|
|
698
|
+
mock_collector.collect.return_value = {"Keys": test_data}
|
|
699
|
+
mock_collector_class.return_value = mock_collector
|
|
700
|
+
|
|
701
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123, force_refresh=True)
|
|
702
|
+
integration._save_to_cache = Mock()
|
|
703
|
+
|
|
704
|
+
result = integration.fetch_compliance_data()
|
|
705
|
+
|
|
706
|
+
assert result == test_data
|
|
707
|
+
integration._save_to_cache.assert_called_once_with(test_data)
|
|
708
|
+
|
|
709
|
+
@patch("regscale.integrations.commercial.aws.inventory.resources.kms.KMSCollector")
|
|
710
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
711
|
+
@patch(f"{PATH}.boto3.Session")
|
|
712
|
+
def test_fetch_compliance_data_client_error(self, mock_session_class, mock_mapper_class, mock_collector_class):
|
|
713
|
+
"""Test fetching compliance data with ClientError."""
|
|
714
|
+
mock_session = MagicMock()
|
|
715
|
+
mock_session.client.return_value = MagicMock()
|
|
716
|
+
mock_session_class.return_value = mock_session
|
|
717
|
+
|
|
718
|
+
mock_collector = MagicMock()
|
|
719
|
+
mock_collector.collect.side_effect = ClientError(
|
|
720
|
+
{"Error": {"Code": "AccessDeniedException", "Message": "Access denied"}}, "ListKeys"
|
|
721
|
+
)
|
|
722
|
+
mock_collector_class.return_value = mock_collector
|
|
723
|
+
|
|
724
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123, force_refresh=True)
|
|
725
|
+
|
|
726
|
+
result = integration.fetch_compliance_data()
|
|
727
|
+
|
|
728
|
+
assert result == []
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
class TestComplianceItem:
|
|
732
|
+
"""Test cases for compliance item creation."""
|
|
733
|
+
|
|
734
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
735
|
+
@patch(f"{PATH}.boto3.Session")
|
|
736
|
+
def test_create_compliance_item(self, mock_session_class, mock_mapper_class):
|
|
737
|
+
"""Test creating a compliance item from raw data."""
|
|
738
|
+
mock_session = MagicMock()
|
|
739
|
+
mock_session.client.return_value = MagicMock()
|
|
740
|
+
mock_session_class.return_value = mock_session
|
|
741
|
+
|
|
742
|
+
mock_mapper = MagicMock()
|
|
743
|
+
mock_mapper.assess_key_compliance.return_value = {"SC-12": "PASS"}
|
|
744
|
+
mock_mapper_class.return_value = mock_mapper
|
|
745
|
+
|
|
746
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
747
|
+
|
|
748
|
+
raw_data = {"KeyId": "test-key", "KeyState": "Enabled"}
|
|
749
|
+
|
|
750
|
+
result = integration.create_compliance_item(raw_data)
|
|
751
|
+
|
|
752
|
+
assert isinstance(result, KMSComplianceItem)
|
|
753
|
+
assert result.resource_id == "test-key"
|
|
754
|
+
|
|
755
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
756
|
+
@patch(f"{PATH}.boto3.Session")
|
|
757
|
+
def test_map_resource_type_to_asset_type(self, mock_session_class, mock_mapper_class):
|
|
758
|
+
"""Test mapping resource type to asset type."""
|
|
759
|
+
mock_session = MagicMock()
|
|
760
|
+
mock_session.client.return_value = MagicMock()
|
|
761
|
+
mock_session_class.return_value = mock_session
|
|
762
|
+
|
|
763
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
764
|
+
|
|
765
|
+
mock_item = MagicMock()
|
|
766
|
+
result = integration._map_resource_type_to_asset_type(mock_item)
|
|
767
|
+
|
|
768
|
+
assert result == "AWS KMS Key"
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
class TestSyncCompliance:
|
|
772
|
+
"""Test cases for sync_compliance method."""
|
|
773
|
+
|
|
774
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
775
|
+
@patch(f"{PATH}.boto3.Session")
|
|
776
|
+
def test_sync_compliance_without_evidence(self, mock_session_class, mock_mapper_class):
|
|
777
|
+
"""Test sync_compliance without evidence collection."""
|
|
778
|
+
mock_session = MagicMock()
|
|
779
|
+
mock_session.client.return_value = MagicMock()
|
|
780
|
+
mock_session_class.return_value = mock_session
|
|
781
|
+
|
|
782
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123, collect_evidence=False)
|
|
783
|
+
|
|
784
|
+
with patch.object(integration.__class__.__bases__[0], "sync_compliance") as mock_super_sync:
|
|
785
|
+
integration.sync_compliance()
|
|
786
|
+
|
|
787
|
+
mock_super_sync.assert_called_once()
|
|
788
|
+
|
|
789
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
790
|
+
@patch(f"{PATH}.boto3.Session")
|
|
791
|
+
def test_sync_compliance_with_evidence(self, mock_session_class, mock_mapper_class):
|
|
792
|
+
"""Test sync_compliance with evidence collection."""
|
|
793
|
+
mock_session = MagicMock()
|
|
794
|
+
mock_session.client.return_value = MagicMock()
|
|
795
|
+
mock_session_class.return_value = mock_session
|
|
796
|
+
|
|
797
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123, collect_evidence=True)
|
|
798
|
+
integration._collect_kms_evidence = Mock()
|
|
799
|
+
|
|
800
|
+
with patch.object(integration.__class__.__bases__[0], "sync_compliance"):
|
|
801
|
+
integration.sync_compliance()
|
|
802
|
+
|
|
803
|
+
integration._collect_kms_evidence.assert_called_once()
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
class TestEvidenceCollection:
|
|
807
|
+
"""Test cases for evidence collection methods."""
|
|
808
|
+
|
|
809
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
810
|
+
@patch(f"{PATH}.boto3.Session")
|
|
811
|
+
@patch(f"{PATH}.get_current_datetime")
|
|
812
|
+
def test_collect_kms_evidence_as_attachments(self, mock_get_datetime, mock_session_class, mock_mapper_class):
|
|
813
|
+
"""Test collecting evidence as SSP attachments."""
|
|
814
|
+
mock_session = MagicMock()
|
|
815
|
+
mock_session.client.return_value = MagicMock()
|
|
816
|
+
mock_session_class.return_value = mock_session
|
|
817
|
+
|
|
818
|
+
mock_get_datetime.return_value = "2023-12-01"
|
|
819
|
+
|
|
820
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123, collect_evidence=True, evidence_as_attachments=True)
|
|
821
|
+
|
|
822
|
+
integration.raw_kms_data = [{"KeyId": "test-key"}]
|
|
823
|
+
integration._create_ssp_attachment = Mock()
|
|
824
|
+
|
|
825
|
+
integration._collect_kms_evidence()
|
|
826
|
+
|
|
827
|
+
integration._create_ssp_attachment.assert_called_once_with("2023-12-01")
|
|
828
|
+
|
|
829
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
830
|
+
@patch(f"{PATH}.boto3.Session")
|
|
831
|
+
@patch(f"{PATH}.get_current_datetime")
|
|
832
|
+
def test_collect_kms_evidence_as_records(self, mock_get_datetime, mock_session_class, mock_mapper_class):
|
|
833
|
+
"""Test collecting evidence as evidence records."""
|
|
834
|
+
mock_session = MagicMock()
|
|
835
|
+
mock_session.client.return_value = MagicMock()
|
|
836
|
+
mock_session_class.return_value = mock_session
|
|
837
|
+
|
|
838
|
+
mock_get_datetime.return_value = "2023-12-01"
|
|
839
|
+
|
|
840
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123, collect_evidence=True, evidence_as_attachments=False)
|
|
841
|
+
|
|
842
|
+
integration.raw_kms_data = [{"KeyId": "test-key"}]
|
|
843
|
+
integration._create_evidence_record = Mock()
|
|
844
|
+
|
|
845
|
+
integration._collect_kms_evidence()
|
|
846
|
+
|
|
847
|
+
integration._create_evidence_record.assert_called_once_with("2023-12-01")
|
|
848
|
+
|
|
849
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
850
|
+
@patch(f"{PATH}.boto3.Session")
|
|
851
|
+
def test_collect_kms_evidence_no_data(self, mock_session_class, mock_mapper_class):
|
|
852
|
+
"""Test collecting evidence when no data is available."""
|
|
853
|
+
mock_session = MagicMock()
|
|
854
|
+
mock_session.client.return_value = MagicMock()
|
|
855
|
+
mock_session_class.return_value = mock_session
|
|
856
|
+
|
|
857
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123, collect_evidence=True)
|
|
858
|
+
|
|
859
|
+
integration.raw_kms_data = []
|
|
860
|
+
|
|
861
|
+
integration._collect_kms_evidence()
|
|
862
|
+
|
|
863
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
864
|
+
@patch(f"{PATH}.boto3.Session")
|
|
865
|
+
@patch(f"{PATH}.Api")
|
|
866
|
+
@patch(f"{PATH}.File")
|
|
867
|
+
def test_create_ssp_attachment_success(
|
|
868
|
+
self, mock_file_class, mock_api_class, mock_session_class, mock_mapper_class
|
|
869
|
+
):
|
|
870
|
+
"""Test creating SSP attachment successfully."""
|
|
871
|
+
mock_session = MagicMock()
|
|
872
|
+
mock_session.client.return_value = MagicMock()
|
|
873
|
+
mock_session_class.return_value = mock_session
|
|
874
|
+
|
|
875
|
+
mock_mapper = MagicMock()
|
|
876
|
+
mock_mapper.assess_key_compliance.return_value = {"SC-12": "PASS"}
|
|
877
|
+
mock_mapper_class.return_value = mock_mapper
|
|
878
|
+
|
|
879
|
+
mock_api = MagicMock()
|
|
880
|
+
mock_api_class.return_value = mock_api
|
|
881
|
+
|
|
882
|
+
mock_file_class.upload_file_to_regscale.return_value = True
|
|
883
|
+
|
|
884
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123, region="us-east-1", account_id="123456789012")
|
|
885
|
+
integration.raw_kms_data = [{"KeyId": "test-key"}]
|
|
886
|
+
|
|
887
|
+
integration._create_ssp_attachment("2023-12-01")
|
|
888
|
+
|
|
889
|
+
mock_file_class.upload_file_to_regscale.assert_called_once()
|
|
890
|
+
call_args = mock_file_class.upload_file_to_regscale.call_args[1]
|
|
891
|
+
assert call_args["parent_id"] == 123
|
|
892
|
+
assert call_args["parent_module"] == "securityplans"
|
|
893
|
+
assert "kms_evidence_123456789012_" in call_args["file_name"]
|
|
894
|
+
assert call_args["file_name"].endswith(".jsonl.gz")
|
|
895
|
+
|
|
896
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
897
|
+
@patch(f"{PATH}.boto3.Session")
|
|
898
|
+
@patch(f"{PATH}.Api")
|
|
899
|
+
@patch(f"{PATH}.File")
|
|
900
|
+
def test_create_ssp_attachment_failure(
|
|
901
|
+
self, mock_file_class, mock_api_class, mock_session_class, mock_mapper_class
|
|
902
|
+
):
|
|
903
|
+
"""Test creating SSP attachment with failure."""
|
|
904
|
+
mock_session = MagicMock()
|
|
905
|
+
mock_session.client.return_value = MagicMock()
|
|
906
|
+
mock_session_class.return_value = mock_session
|
|
907
|
+
|
|
908
|
+
mock_mapper = MagicMock()
|
|
909
|
+
mock_mapper.assess_key_compliance.return_value = {}
|
|
910
|
+
mock_mapper_class.return_value = mock_mapper
|
|
911
|
+
|
|
912
|
+
mock_api = MagicMock()
|
|
913
|
+
mock_api_class.return_value = mock_api
|
|
914
|
+
|
|
915
|
+
mock_file_class.upload_file_to_regscale.return_value = False
|
|
916
|
+
|
|
917
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
918
|
+
integration.raw_kms_data = [{"KeyId": "test-key"}]
|
|
919
|
+
|
|
920
|
+
integration._create_ssp_attachment("2023-12-01")
|
|
921
|
+
|
|
922
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
923
|
+
@patch(f"{PATH}.boto3.Session")
|
|
924
|
+
@patch(f"{PATH}.Api")
|
|
925
|
+
@patch(f"{PATH}.File")
|
|
926
|
+
def test_create_ssp_attachment_exception(
|
|
927
|
+
self, mock_file_class, mock_api_class, mock_session_class, mock_mapper_class
|
|
928
|
+
):
|
|
929
|
+
"""Test creating SSP attachment with exception."""
|
|
930
|
+
mock_session = MagicMock()
|
|
931
|
+
mock_session.client.return_value = MagicMock()
|
|
932
|
+
mock_session_class.return_value = mock_session
|
|
933
|
+
|
|
934
|
+
mock_mapper = MagicMock()
|
|
935
|
+
mock_mapper.assess_key_compliance.side_effect = Exception("Test error")
|
|
936
|
+
mock_mapper_class.return_value = mock_mapper
|
|
937
|
+
|
|
938
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
939
|
+
integration.raw_kms_data = [{"KeyId": "test-key"}]
|
|
940
|
+
|
|
941
|
+
integration._create_ssp_attachment("2023-12-01")
|
|
942
|
+
|
|
943
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
944
|
+
@patch(f"{PATH}.boto3.Session")
|
|
945
|
+
@patch(f"{PATH}.Evidence")
|
|
946
|
+
def test_create_evidence_record_success(self, mock_evidence_class, mock_session_class, mock_mapper_class):
|
|
947
|
+
"""Test creating evidence record successfully."""
|
|
948
|
+
mock_session = MagicMock()
|
|
949
|
+
mock_session.client.return_value = MagicMock()
|
|
950
|
+
mock_session_class.return_value = mock_session
|
|
951
|
+
|
|
952
|
+
mock_mapper = MagicMock()
|
|
953
|
+
mock_mapper.assess_key_compliance.return_value = {"SC-12": "PASS"}
|
|
954
|
+
mock_mapper.get_control_description.return_value = "SC-12 description"
|
|
955
|
+
mock_mapper.get_mapped_controls.return_value = ["SC-12", "SC-13", "SC-28"]
|
|
956
|
+
mock_mapper_class.return_value = mock_mapper
|
|
957
|
+
|
|
958
|
+
mock_evidence = MagicMock()
|
|
959
|
+
mock_evidence.id = 999
|
|
960
|
+
mock_evidence_instance = MagicMock()
|
|
961
|
+
mock_evidence_instance.create.return_value = mock_evidence
|
|
962
|
+
mock_evidence_class.return_value = mock_evidence_instance
|
|
963
|
+
|
|
964
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123, evidence_frequency=60)
|
|
965
|
+
integration.raw_kms_data = [{"KeyId": "test-key"}]
|
|
966
|
+
integration._upload_evidence_file = Mock()
|
|
967
|
+
integration._link_evidence_to_ssp = Mock()
|
|
968
|
+
|
|
969
|
+
integration._create_evidence_record("2023-12-01")
|
|
970
|
+
|
|
971
|
+
mock_evidence_instance.create.assert_called_once()
|
|
972
|
+
integration._upload_evidence_file.assert_called_once_with(999, "2023-12-01")
|
|
973
|
+
integration._link_evidence_to_ssp.assert_called_once_with(999)
|
|
974
|
+
|
|
975
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
976
|
+
@patch(f"{PATH}.boto3.Session")
|
|
977
|
+
@patch(f"{PATH}.Evidence")
|
|
978
|
+
def test_create_evidence_record_creation_failure(self, mock_evidence_class, mock_session_class, mock_mapper_class):
|
|
979
|
+
"""Test creating evidence record when creation fails."""
|
|
980
|
+
mock_session = MagicMock()
|
|
981
|
+
mock_session.client.return_value = MagicMock()
|
|
982
|
+
mock_session_class.return_value = mock_session
|
|
983
|
+
|
|
984
|
+
mock_mapper = MagicMock()
|
|
985
|
+
mock_mapper.assess_key_compliance.return_value = {}
|
|
986
|
+
mock_mapper.get_mapped_controls.return_value = []
|
|
987
|
+
mock_mapper_class.return_value = mock_mapper
|
|
988
|
+
|
|
989
|
+
mock_evidence_instance = MagicMock()
|
|
990
|
+
mock_evidence_instance.create.return_value = None
|
|
991
|
+
mock_evidence_class.return_value = mock_evidence_instance
|
|
992
|
+
|
|
993
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
994
|
+
integration.raw_kms_data = []
|
|
995
|
+
|
|
996
|
+
integration._create_evidence_record("2023-12-01")
|
|
997
|
+
|
|
998
|
+
mock_evidence_instance.create.assert_called_once()
|
|
999
|
+
|
|
1000
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
1001
|
+
@patch(f"{PATH}.boto3.Session")
|
|
1002
|
+
@patch(f"{PATH}.Evidence")
|
|
1003
|
+
def test_create_evidence_record_exception(self, mock_evidence_class, mock_session_class, mock_mapper_class):
|
|
1004
|
+
"""Test creating evidence record with exception."""
|
|
1005
|
+
mock_session = MagicMock()
|
|
1006
|
+
mock_session.client.return_value = MagicMock()
|
|
1007
|
+
mock_session_class.return_value = mock_session
|
|
1008
|
+
|
|
1009
|
+
mock_mapper = MagicMock()
|
|
1010
|
+
mock_mapper.assess_key_compliance.side_effect = Exception("Test error")
|
|
1011
|
+
mock_mapper_class.return_value = mock_mapper
|
|
1012
|
+
|
|
1013
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
1014
|
+
integration.raw_kms_data = []
|
|
1015
|
+
|
|
1016
|
+
integration._create_evidence_record("2023-12-01")
|
|
1017
|
+
|
|
1018
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
1019
|
+
@patch(f"{PATH}.boto3.Session")
|
|
1020
|
+
def test_build_evidence_description(self, mock_session_class, mock_mapper_class):
|
|
1021
|
+
"""Test building evidence description."""
|
|
1022
|
+
mock_session = MagicMock()
|
|
1023
|
+
mock_session.client.return_value = MagicMock()
|
|
1024
|
+
mock_session_class.return_value = mock_session
|
|
1025
|
+
|
|
1026
|
+
mock_mapper = MagicMock()
|
|
1027
|
+
mock_mapper.assess_key_compliance.return_value = {"SC-12": "PASS", "SC-13": "FAIL", "SC-28": "PASS"}
|
|
1028
|
+
mock_mapper.get_control_description.side_effect = lambda x: f"{x} description"
|
|
1029
|
+
mock_mapper.get_mapped_controls.return_value = ["SC-12", "SC-13", "SC-28"]
|
|
1030
|
+
mock_mapper_class.return_value = mock_mapper
|
|
1031
|
+
|
|
1032
|
+
integration = AWSKMSEvidenceIntegration(
|
|
1033
|
+
plan_id=123, region="us-east-1", account_id="123456789012", tags={"Environment": "Production"}
|
|
1034
|
+
)
|
|
1035
|
+
integration.raw_kms_data = [
|
|
1036
|
+
{"KeyId": "key-1", "RotationEnabled": True, "KeyManager": "CUSTOMER"},
|
|
1037
|
+
{"KeyId": "key-2", "RotationEnabled": False, "KeyManager": "CUSTOMER"},
|
|
1038
|
+
]
|
|
1039
|
+
|
|
1040
|
+
result = integration._build_evidence_description("2023-12-01")
|
|
1041
|
+
|
|
1042
|
+
assert "AWS KMS Evidence" in result
|
|
1043
|
+
assert "2023-12-01" in result
|
|
1044
|
+
assert "us-east-1" in result
|
|
1045
|
+
assert "123456789012" in result
|
|
1046
|
+
assert "Environment=Production" in result
|
|
1047
|
+
assert "Total Keys" in result
|
|
1048
|
+
assert "SC-12" in result
|
|
1049
|
+
assert "SC-13" in result
|
|
1050
|
+
|
|
1051
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
1052
|
+
@patch(f"{PATH}.boto3.Session")
|
|
1053
|
+
@patch(f"{PATH}.Api")
|
|
1054
|
+
@patch(f"{PATH}.File")
|
|
1055
|
+
def test_upload_evidence_file_success(self, mock_file_class, mock_api_class, mock_session_class, mock_mapper_class):
|
|
1056
|
+
"""Test uploading evidence file successfully."""
|
|
1057
|
+
mock_session = MagicMock()
|
|
1058
|
+
mock_session.client.return_value = MagicMock()
|
|
1059
|
+
mock_session_class.return_value = mock_session
|
|
1060
|
+
|
|
1061
|
+
mock_mapper = MagicMock()
|
|
1062
|
+
mock_mapper.assess_key_compliance.return_value = {"SC-12": "PASS"}
|
|
1063
|
+
mock_mapper_class.return_value = mock_mapper
|
|
1064
|
+
|
|
1065
|
+
mock_api = MagicMock()
|
|
1066
|
+
mock_api_class.return_value = mock_api
|
|
1067
|
+
|
|
1068
|
+
mock_file_class.upload_file_to_regscale.return_value = True
|
|
1069
|
+
|
|
1070
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
1071
|
+
integration.raw_kms_data = [{"KeyId": "test-key"}]
|
|
1072
|
+
|
|
1073
|
+
integration._upload_evidence_file(999, "2023-12-01")
|
|
1074
|
+
|
|
1075
|
+
mock_file_class.upload_file_to_regscale.assert_called_once()
|
|
1076
|
+
call_args = mock_file_class.upload_file_to_regscale.call_args[1]
|
|
1077
|
+
assert call_args["parent_id"] == 999
|
|
1078
|
+
assert call_args["parent_module"] == "evidence"
|
|
1079
|
+
|
|
1080
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
1081
|
+
@patch(f"{PATH}.boto3.Session")
|
|
1082
|
+
@patch(f"{PATH}.Api")
|
|
1083
|
+
@patch(f"{PATH}.File")
|
|
1084
|
+
def test_upload_evidence_file_failure(self, mock_file_class, mock_api_class, mock_session_class, mock_mapper_class):
|
|
1085
|
+
"""Test uploading evidence file with failure."""
|
|
1086
|
+
mock_session = MagicMock()
|
|
1087
|
+
mock_session.client.return_value = MagicMock()
|
|
1088
|
+
mock_session_class.return_value = mock_session
|
|
1089
|
+
|
|
1090
|
+
mock_mapper = MagicMock()
|
|
1091
|
+
mock_mapper.assess_key_compliance.return_value = {}
|
|
1092
|
+
mock_mapper_class.return_value = mock_mapper
|
|
1093
|
+
|
|
1094
|
+
mock_api = MagicMock()
|
|
1095
|
+
mock_api_class.return_value = mock_api
|
|
1096
|
+
|
|
1097
|
+
mock_file_class.upload_file_to_regscale.return_value = False
|
|
1098
|
+
|
|
1099
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
1100
|
+
integration.raw_kms_data = []
|
|
1101
|
+
|
|
1102
|
+
integration._upload_evidence_file(999, "2023-12-01")
|
|
1103
|
+
|
|
1104
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
1105
|
+
@patch(f"{PATH}.boto3.Session")
|
|
1106
|
+
@patch(f"{PATH}.Api")
|
|
1107
|
+
@patch(f"{PATH}.File")
|
|
1108
|
+
def test_upload_evidence_file_exception(
|
|
1109
|
+
self, mock_file_class, mock_api_class, mock_session_class, mock_mapper_class
|
|
1110
|
+
):
|
|
1111
|
+
"""Test uploading evidence file with exception."""
|
|
1112
|
+
mock_session = MagicMock()
|
|
1113
|
+
mock_session.client.return_value = MagicMock()
|
|
1114
|
+
mock_session_class.return_value = mock_session
|
|
1115
|
+
|
|
1116
|
+
mock_mapper = MagicMock()
|
|
1117
|
+
mock_mapper.assess_key_compliance.side_effect = Exception("Test error")
|
|
1118
|
+
mock_mapper_class.return_value = mock_mapper
|
|
1119
|
+
|
|
1120
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
1121
|
+
integration.raw_kms_data = []
|
|
1122
|
+
|
|
1123
|
+
integration._upload_evidence_file(999, "2023-12-01")
|
|
1124
|
+
|
|
1125
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
1126
|
+
@patch(f"{PATH}.boto3.Session")
|
|
1127
|
+
@patch(f"{PATH}.EvidenceMapping")
|
|
1128
|
+
def test_link_evidence_to_ssp_success(self, mock_mapping_class, mock_session_class, mock_mapper_class):
|
|
1129
|
+
"""Test linking evidence to SSP successfully."""
|
|
1130
|
+
mock_session = MagicMock()
|
|
1131
|
+
mock_session.client.return_value = MagicMock()
|
|
1132
|
+
mock_session_class.return_value = mock_session
|
|
1133
|
+
|
|
1134
|
+
mock_mapping = MagicMock()
|
|
1135
|
+
mock_mapping_class.return_value = mock_mapping
|
|
1136
|
+
|
|
1137
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
1138
|
+
|
|
1139
|
+
integration._link_evidence_to_ssp(999)
|
|
1140
|
+
|
|
1141
|
+
mock_mapping_class.assert_called_once_with(evidenceID=999, mappedID=123, mappingType="securityplans")
|
|
1142
|
+
mock_mapping.create.assert_called_once()
|
|
1143
|
+
|
|
1144
|
+
@patch(f"{PATH}.KMSControlMapper")
|
|
1145
|
+
@patch(f"{PATH}.boto3.Session")
|
|
1146
|
+
@patch(f"{PATH}.EvidenceMapping")
|
|
1147
|
+
def test_link_evidence_to_ssp_failure(self, mock_mapping_class, mock_session_class, mock_mapper_class):
|
|
1148
|
+
"""Test linking evidence to SSP with failure."""
|
|
1149
|
+
mock_session = MagicMock()
|
|
1150
|
+
mock_session.client.return_value = MagicMock()
|
|
1151
|
+
mock_session_class.return_value = mock_session
|
|
1152
|
+
|
|
1153
|
+
mock_mapping = MagicMock()
|
|
1154
|
+
mock_mapping.create.side_effect = Exception("Test error")
|
|
1155
|
+
mock_mapping_class.return_value = mock_mapping
|
|
1156
|
+
|
|
1157
|
+
integration = AWSKMSEvidenceIntegration(plan_id=123)
|
|
1158
|
+
|
|
1159
|
+
integration._link_evidence_to_ssp(999)
|
|
1160
|
+
|
|
1161
|
+
|
|
1162
|
+
if __name__ == "__main__":
|
|
1163
|
+
pytest.main([__file__, "-v"])
|