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.

Files changed (112) hide show
  1. regscale/_version.py +1 -1
  2. regscale/core/app/utils/app_utils.py +11 -2
  3. regscale/dev/cli.py +26 -0
  4. regscale/dev/version.py +72 -0
  5. regscale/integrations/commercial/__init__.py +15 -1
  6. regscale/integrations/commercial/amazon/amazon/__init__.py +0 -0
  7. regscale/integrations/commercial/amazon/amazon/common.py +204 -0
  8. regscale/integrations/commercial/amazon/common.py +48 -58
  9. regscale/integrations/commercial/aws/audit_manager_compliance.py +2671 -0
  10. regscale/integrations/commercial/aws/cli.py +3093 -55
  11. regscale/integrations/commercial/aws/cloudtrail_control_mappings.py +333 -0
  12. regscale/integrations/commercial/aws/cloudtrail_evidence.py +501 -0
  13. regscale/integrations/commercial/aws/cloudwatch_control_mappings.py +357 -0
  14. regscale/integrations/commercial/aws/cloudwatch_evidence.py +490 -0
  15. regscale/integrations/commercial/aws/config_compliance.py +914 -0
  16. regscale/integrations/commercial/aws/conformance_pack_mappings.py +198 -0
  17. regscale/integrations/commercial/aws/evidence_generator.py +283 -0
  18. regscale/integrations/commercial/aws/guardduty_control_mappings.py +340 -0
  19. regscale/integrations/commercial/aws/guardduty_evidence.py +1053 -0
  20. regscale/integrations/commercial/aws/iam_control_mappings.py +368 -0
  21. regscale/integrations/commercial/aws/iam_evidence.py +574 -0
  22. regscale/integrations/commercial/aws/inventory/__init__.py +223 -22
  23. regscale/integrations/commercial/aws/inventory/base.py +107 -5
  24. regscale/integrations/commercial/aws/inventory/resources/audit_manager.py +513 -0
  25. regscale/integrations/commercial/aws/inventory/resources/cloudtrail.py +315 -0
  26. regscale/integrations/commercial/aws/inventory/resources/cloudtrail_logs_metadata.py +476 -0
  27. regscale/integrations/commercial/aws/inventory/resources/cloudwatch.py +191 -0
  28. regscale/integrations/commercial/aws/inventory/resources/compute.py +66 -9
  29. regscale/integrations/commercial/aws/inventory/resources/config.py +464 -0
  30. regscale/integrations/commercial/aws/inventory/resources/containers.py +74 -9
  31. regscale/integrations/commercial/aws/inventory/resources/database.py +106 -31
  32. regscale/integrations/commercial/aws/inventory/resources/guardduty.py +286 -0
  33. regscale/integrations/commercial/aws/inventory/resources/iam.py +470 -0
  34. regscale/integrations/commercial/aws/inventory/resources/inspector.py +476 -0
  35. regscale/integrations/commercial/aws/inventory/resources/integration.py +175 -61
  36. regscale/integrations/commercial/aws/inventory/resources/kms.py +447 -0
  37. regscale/integrations/commercial/aws/inventory/resources/networking.py +103 -67
  38. regscale/integrations/commercial/aws/inventory/resources/s3.py +394 -0
  39. regscale/integrations/commercial/aws/inventory/resources/security.py +268 -72
  40. regscale/integrations/commercial/aws/inventory/resources/securityhub.py +473 -0
  41. regscale/integrations/commercial/aws/inventory/resources/storage.py +53 -29
  42. regscale/integrations/commercial/aws/inventory/resources/systems_manager.py +657 -0
  43. regscale/integrations/commercial/aws/inventory/resources/vpc.py +655 -0
  44. regscale/integrations/commercial/aws/kms_control_mappings.py +288 -0
  45. regscale/integrations/commercial/aws/kms_evidence.py +879 -0
  46. regscale/integrations/commercial/aws/ocsf/__init__.py +7 -0
  47. regscale/integrations/commercial/aws/ocsf/constants.py +115 -0
  48. regscale/integrations/commercial/aws/ocsf/mapper.py +435 -0
  49. regscale/integrations/commercial/aws/org_control_mappings.py +286 -0
  50. regscale/integrations/commercial/aws/org_evidence.py +666 -0
  51. regscale/integrations/commercial/aws/s3_control_mappings.py +356 -0
  52. regscale/integrations/commercial/aws/s3_evidence.py +632 -0
  53. regscale/integrations/commercial/aws/scanner.py +851 -206
  54. regscale/integrations/commercial/aws/security_hub.py +319 -0
  55. regscale/integrations/commercial/aws/session_manager.py +282 -0
  56. regscale/integrations/commercial/aws/ssm_control_mappings.py +291 -0
  57. regscale/integrations/commercial/aws/ssm_evidence.py +492 -0
  58. regscale/integrations/compliance_integration.py +308 -38
  59. regscale/integrations/due_date_handler.py +3 -0
  60. regscale/integrations/scanner_integration.py +399 -84
  61. regscale/models/integration_models/cisa_kev_data.json +34 -4
  62. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  63. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +17 -9
  64. regscale/models/regscale_models/assessment.py +2 -1
  65. regscale/models/regscale_models/control_objective.py +74 -5
  66. regscale/models/regscale_models/file.py +2 -0
  67. regscale/models/regscale_models/issue.py +2 -5
  68. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/METADATA +1 -1
  69. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/RECORD +112 -33
  70. tests/regscale/integrations/commercial/aws/__init__.py +0 -0
  71. tests/regscale/integrations/commercial/aws/test_audit_manager_compliance.py +1304 -0
  72. tests/regscale/integrations/commercial/aws/test_audit_manager_evidence_aggregation.py +341 -0
  73. tests/regscale/integrations/commercial/aws/test_aws_audit_manager_collector.py +1155 -0
  74. tests/regscale/integrations/commercial/aws/test_aws_cloudtrail_collector.py +534 -0
  75. tests/regscale/integrations/commercial/aws/test_aws_config_collector.py +400 -0
  76. tests/regscale/integrations/commercial/aws/test_aws_guardduty_collector.py +315 -0
  77. tests/regscale/integrations/commercial/aws/test_aws_iam_collector.py +458 -0
  78. tests/regscale/integrations/commercial/aws/test_aws_inspector_collector.py +353 -0
  79. tests/regscale/integrations/commercial/aws/test_aws_inventory_integration.py +530 -0
  80. tests/regscale/integrations/commercial/aws/test_aws_kms_collector.py +919 -0
  81. tests/regscale/integrations/commercial/aws/test_aws_s3_collector.py +722 -0
  82. tests/regscale/integrations/commercial/aws/test_aws_scanner_integration.py +722 -0
  83. tests/regscale/integrations/commercial/aws/test_aws_securityhub_collector.py +792 -0
  84. tests/regscale/integrations/commercial/aws/test_aws_systems_manager_collector.py +918 -0
  85. tests/regscale/integrations/commercial/aws/test_aws_vpc_collector.py +996 -0
  86. tests/regscale/integrations/commercial/aws/test_cli_evidence.py +431 -0
  87. tests/regscale/integrations/commercial/aws/test_cloudtrail_control_mappings.py +452 -0
  88. tests/regscale/integrations/commercial/aws/test_cloudtrail_evidence.py +788 -0
  89. tests/regscale/integrations/commercial/aws/test_config_compliance.py +298 -0
  90. tests/regscale/integrations/commercial/aws/test_conformance_pack_mappings.py +200 -0
  91. tests/regscale/integrations/commercial/aws/test_evidence_generator.py +386 -0
  92. tests/regscale/integrations/commercial/aws/test_guardduty_control_mappings.py +564 -0
  93. tests/regscale/integrations/commercial/aws/test_guardduty_evidence.py +1041 -0
  94. tests/regscale/integrations/commercial/aws/test_iam_control_mappings.py +718 -0
  95. tests/regscale/integrations/commercial/aws/test_iam_evidence.py +1375 -0
  96. tests/regscale/integrations/commercial/aws/test_kms_control_mappings.py +656 -0
  97. tests/regscale/integrations/commercial/aws/test_kms_evidence.py +1163 -0
  98. tests/regscale/integrations/commercial/aws/test_ocsf_mapper.py +370 -0
  99. tests/regscale/integrations/commercial/aws/test_org_control_mappings.py +546 -0
  100. tests/regscale/integrations/commercial/aws/test_org_evidence.py +1240 -0
  101. tests/regscale/integrations/commercial/aws/test_s3_control_mappings.py +672 -0
  102. tests/regscale/integrations/commercial/aws/test_s3_evidence.py +987 -0
  103. tests/regscale/integrations/commercial/aws/test_scanner_evidence.py +373 -0
  104. tests/regscale/integrations/commercial/aws/test_security_hub_config_filtering.py +539 -0
  105. tests/regscale/integrations/commercial/aws/test_session_manager.py +516 -0
  106. tests/regscale/integrations/commercial/aws/test_ssm_control_mappings.py +588 -0
  107. tests/regscale/integrations/commercial/aws/test_ssm_evidence.py +735 -0
  108. tests/regscale/integrations/commercial/test_aws.py +55 -56
  109. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/LICENSE +0 -0
  110. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/WHEEL +0 -0
  111. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/entry_points.txt +0 -0
  112. {regscale_cli-6.27.3.0.dist-info → regscale_cli-6.28.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,315 @@
1
+ """Unit tests for AWS GuardDuty collector."""
2
+
3
+ import unittest
4
+ from unittest.mock import MagicMock, patch
5
+
6
+ import pytest
7
+ from botocore.exceptions import ClientError
8
+
9
+ from regscale.integrations.commercial.aws.inventory.resources.guardduty import GuardDutyCollector
10
+
11
+
12
+ class TestGuardDutyCollector(unittest.TestCase):
13
+ """Test cases for GuardDutyCollector."""
14
+
15
+ def setUp(self):
16
+ """Set up test fixtures."""
17
+ self.mock_session = MagicMock()
18
+ self.region = "us-east-1"
19
+ self.account_id = "123456789012"
20
+ self.collector = GuardDutyCollector(self.mock_session, self.region, self.account_id)
21
+
22
+ def test_init(self):
23
+ """Test GuardDutyCollector initialization."""
24
+ assert self.collector.session == self.mock_session
25
+ assert self.collector.region == self.region
26
+ assert self.collector.account_id == self.account_id
27
+
28
+ def test_init_without_account_id(self):
29
+ """Test GuardDutyCollector initialization without account ID."""
30
+ collector = GuardDutyCollector(self.mock_session, self.region)
31
+ assert collector.account_id is None
32
+
33
+ @patch("regscale.integrations.commercial.aws.inventory.resources.guardduty.logger")
34
+ def test_collect_success(self, mock_logger):
35
+ """Test successful collection of GuardDuty resources."""
36
+ # Setup mock client
37
+ mock_client = MagicMock()
38
+ self.mock_session.client.return_value = mock_client
39
+
40
+ detector_id = "test-detector-123"
41
+ mock_client.list_detectors.return_value = {"DetectorIds": [detector_id]}
42
+
43
+ mock_client.get_detector.return_value = {
44
+ "Status": "ENABLED",
45
+ "ServiceRole": "arn:aws:iam::123456789012:role/service-role",
46
+ "AccountId": self.account_id,
47
+ "ResponseMetadata": {"RequestId": "test"},
48
+ }
49
+
50
+ mock_client.list_findings.return_value = {"FindingIds": ["finding-1", "finding-2"]}
51
+
52
+ mock_client.get_findings.return_value = {
53
+ "Findings": [
54
+ {
55
+ "Id": "finding-1",
56
+ "Type": "UnauthorizedAccess:IAMUser/MaliciousIPCaller.Custom",
57
+ "Severity": 8.0,
58
+ "AccountId": self.account_id,
59
+ },
60
+ {"Id": "finding-2", "Type": "Recon:EC2/PortProbeUnprotectedPort", "Severity": 5.0},
61
+ ]
62
+ }
63
+
64
+ mock_client.list_members.return_value = {"Members": []}
65
+
66
+ # Execute
67
+ result = self.collector.collect()
68
+
69
+ # Verify
70
+ assert "Detectors" in result
71
+ assert "Findings" in result
72
+ assert "Members" in result
73
+ assert len(result["Detectors"]) == 1
74
+ assert len(result["Findings"]) == 2
75
+ assert result["Detectors"][0]["DetectorId"] == detector_id
76
+ assert result["Detectors"][0]["Region"] == self.region
77
+ assert "ResponseMetadata" not in result["Detectors"][0]
78
+
79
+ @patch("regscale.integrations.commercial.aws.inventory.resources.guardduty.logger")
80
+ def test_collect_filters_by_account_id(self, mock_logger):
81
+ """Test that collection filters detectors by account ID."""
82
+ # Setup mock client
83
+ mock_client = MagicMock()
84
+ self.mock_session.client.return_value = mock_client
85
+
86
+ mock_client.list_detectors.return_value = {"DetectorIds": ["detector-1", "detector-2"]}
87
+
88
+ # Return different account IDs for each detector
89
+ mock_client.get_detector.side_effect = [
90
+ {"Status": "ENABLED", "AccountId": self.account_id, "ServiceRole": "arn"},
91
+ {"Status": "ENABLED", "AccountId": "999999999999", "ServiceRole": "arn"},
92
+ ]
93
+
94
+ mock_client.list_findings.return_value = {"FindingIds": []}
95
+ mock_client.list_members.return_value = {"Members": []}
96
+
97
+ # Execute
98
+ result = self.collector.collect()
99
+
100
+ # Verify - should only have one detector (the matching account)
101
+ assert len(result["Detectors"]) == 1
102
+ assert result["Detectors"][0]["AccountId"] == self.account_id
103
+
104
+ @patch("regscale.integrations.commercial.aws.inventory.resources.guardduty.logger")
105
+ def test_collect_no_account_filter(self, mock_logger):
106
+ """Test collection without account ID filter."""
107
+ # Create collector without account ID
108
+ collector = GuardDutyCollector(self.mock_session, self.region)
109
+
110
+ # Setup mock client
111
+ mock_client = MagicMock()
112
+ self.mock_session.client.return_value = mock_client
113
+
114
+ mock_client.list_detectors.return_value = {"DetectorIds": ["detector-1", "detector-2"]}
115
+
116
+ mock_client.get_detector.side_effect = [
117
+ {"Status": "ENABLED", "AccountId": "111111111111"},
118
+ {"Status": "ENABLED", "AccountId": "222222222222"},
119
+ ]
120
+
121
+ mock_client.list_findings.return_value = {"FindingIds": []}
122
+ mock_client.list_members.return_value = {"Members": []}
123
+
124
+ # Execute
125
+ result = collector.collect()
126
+
127
+ # Verify - should have both detectors
128
+ assert len(result["Detectors"]) == 2
129
+
130
+ @patch("regscale.integrations.commercial.aws.inventory.resources.guardduty.logger")
131
+ def test_collect_handles_client_error(self, mock_logger):
132
+ """Test collection handles ClientError."""
133
+ # Setup mock client
134
+ mock_client = MagicMock()
135
+ self.mock_session.client.return_value = mock_client
136
+
137
+ # Simulate ClientError
138
+ error_response = {"Error": {"Code": "InternalError", "Message": "Internal error"}}
139
+ mock_client.list_detectors.side_effect = ClientError(error_response, "list_detectors")
140
+
141
+ # Execute
142
+ result = self.collector.collect()
143
+
144
+ # Verify
145
+ assert result["Detectors"] == []
146
+
147
+ @patch("regscale.integrations.commercial.aws.inventory.resources.guardduty.logger")
148
+ def test_collect_handles_unexpected_error(self, mock_logger):
149
+ """Test collection handles unexpected errors."""
150
+ # Setup mock client
151
+ mock_client = MagicMock()
152
+ self.mock_session.client.return_value = mock_client
153
+
154
+ # Simulate unexpected error
155
+ mock_client.list_detectors.side_effect = Exception("Unexpected error")
156
+
157
+ # Execute
158
+ result = self.collector.collect()
159
+
160
+ # Verify
161
+ assert result["Detectors"] == []
162
+ mock_logger.error.assert_called()
163
+
164
+ def test_list_detectors_success(self):
165
+ """Test successful listing of detectors."""
166
+ mock_client = MagicMock()
167
+ mock_client.list_detectors.return_value = {"DetectorIds": ["detector-1", "detector-2"]}
168
+
169
+ result = self.collector._list_detectors(mock_client)
170
+
171
+ assert len(result) == 2
172
+ assert "detector-1" in result
173
+
174
+ def test_list_detectors_access_denied(self):
175
+ """Test listing detectors with access denied."""
176
+ mock_client = MagicMock()
177
+ error_response = {"Error": {"Code": "AccessDeniedException", "Message": "Access denied"}}
178
+ mock_client.list_detectors.side_effect = ClientError(error_response, "list_detectors")
179
+
180
+ result = self.collector._list_detectors(mock_client)
181
+
182
+ assert result == []
183
+
184
+ def test_get_detector_success(self):
185
+ """Test successful detector retrieval."""
186
+ mock_client = MagicMock()
187
+ detector_id = "test-detector"
188
+ mock_client.get_detector.return_value = {
189
+ "Status": "ENABLED",
190
+ "ServiceRole": "arn:aws:iam::123456789012:role/service-role",
191
+ "ResponseMetadata": {"RequestId": "test"},
192
+ }
193
+
194
+ result = self.collector._get_detector(mock_client, detector_id)
195
+
196
+ assert result is not None
197
+ assert result["Status"] == "ENABLED"
198
+ assert "ResponseMetadata" not in result
199
+
200
+ def test_get_detector_error(self):
201
+ """Test detector retrieval with error."""
202
+ mock_client = MagicMock()
203
+ detector_id = "test-detector"
204
+ error_response = {"Error": {"Code": "BadRequestException", "Message": "Bad request"}}
205
+ mock_client.get_detector.side_effect = ClientError(error_response, "get_detector")
206
+
207
+ result = self.collector._get_detector(mock_client, detector_id)
208
+
209
+ assert result is None
210
+
211
+ def test_list_and_get_findings_success(self):
212
+ """Test successful findings retrieval."""
213
+ mock_client = MagicMock()
214
+ detector_id = "test-detector"
215
+
216
+ mock_client.list_findings.return_value = {"FindingIds": ["finding-1", "finding-2"]}
217
+ mock_client.get_findings.return_value = {
218
+ "Findings": [{"Id": "finding-1", "Severity": 8.0}, {"Id": "finding-2", "Severity": 5.0}]
219
+ }
220
+
221
+ result = self.collector._list_and_get_findings(mock_client, detector_id)
222
+
223
+ assert len(result) == 2
224
+ assert result[0]["Region"] == self.region
225
+ assert result[0]["DetectorId"] == detector_id
226
+
227
+ def test_list_and_get_findings_no_findings(self):
228
+ """Test findings retrieval with no findings."""
229
+ mock_client = MagicMock()
230
+ detector_id = "test-detector"
231
+ mock_client.list_findings.return_value = {"FindingIds": []}
232
+
233
+ result = self.collector._list_and_get_findings(mock_client, detector_id)
234
+
235
+ assert result == []
236
+ mock_client.get_findings.assert_not_called()
237
+
238
+ def test_list_and_get_findings_access_denied(self):
239
+ """Test findings retrieval with access denied."""
240
+ mock_client = MagicMock()
241
+ detector_id = "test-detector"
242
+ error_response = {"Error": {"Code": "AccessDeniedException", "Message": "Access denied"}}
243
+ mock_client.list_findings.side_effect = ClientError(error_response, "list_findings")
244
+
245
+ result = self.collector._list_and_get_findings(mock_client, detector_id)
246
+
247
+ assert result == []
248
+
249
+ def test_list_and_get_findings_with_max_limit(self):
250
+ """Test findings retrieval respects max_findings parameter."""
251
+ mock_client = MagicMock()
252
+ detector_id = "test-detector"
253
+ max_findings = 10
254
+
255
+ mock_client.list_findings.return_value = {"FindingIds": ["finding-1"]}
256
+ mock_client.get_findings.return_value = {"Findings": [{"Id": "finding-1"}]}
257
+
258
+ self.collector._list_and_get_findings(mock_client, detector_id, max_findings=max_findings)
259
+
260
+ # Verify MaxResults was passed
261
+ call_args = mock_client.list_findings.call_args[1]
262
+ assert call_args["MaxResults"] == max_findings
263
+
264
+ def test_list_members_success(self):
265
+ """Test successful members listing."""
266
+ mock_client = MagicMock()
267
+ detector_id = "test-detector"
268
+ mock_client.list_members.return_value = {
269
+ "Members": [{"AccountId": "111111111111", "Email": "test@example.com"}]
270
+ }
271
+
272
+ result = self.collector._list_members(mock_client, detector_id)
273
+
274
+ assert len(result) == 1
275
+ assert result[0]["Region"] == self.region
276
+ assert result[0]["DetectorId"] == detector_id
277
+
278
+ def test_list_members_access_denied(self):
279
+ """Test members listing with access denied."""
280
+ mock_client = MagicMock()
281
+ detector_id = "test-detector"
282
+ error_response = {"Error": {"Code": "AccessDeniedException", "Message": "Access denied"}}
283
+ mock_client.list_members.side_effect = ClientError(error_response, "list_members")
284
+
285
+ result = self.collector._list_members(mock_client, detector_id)
286
+
287
+ assert result == []
288
+
289
+ def test_list_members_other_error(self):
290
+ """Test members listing with other error."""
291
+ mock_client = MagicMock()
292
+ detector_id = "test-detector"
293
+ error_response = {"Error": {"Code": "InternalError", "Message": "Internal error"}}
294
+ mock_client.list_members.side_effect = ClientError(error_response, "list_members")
295
+
296
+ result = self.collector._list_members(mock_client, detector_id)
297
+
298
+ assert result == []
299
+
300
+ def test_matches_account_id_with_matching_id(self):
301
+ """Test account ID matching with matching ID."""
302
+ assert self.collector._matches_account_id(self.account_id) is True
303
+
304
+ def test_matches_account_id_with_non_matching_id(self):
305
+ """Test account ID matching with non-matching ID."""
306
+ assert self.collector._matches_account_id("999999999999") is False
307
+
308
+ def test_matches_account_id_without_filter(self):
309
+ """Test account ID matching without account filter."""
310
+ collector = GuardDutyCollector(self.mock_session, self.region)
311
+ assert collector._matches_account_id("999999999999") is True
312
+
313
+
314
+ if __name__ == "__main__":
315
+ pytest.main([__file__, "-v"])