regscale-cli 6.17.0.0__py3-none-any.whl → 6.19.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 (48) hide show
  1. regscale/__init__.py +1 -1
  2. regscale/core/app/api.py +5 -0
  3. regscale/core/login.py +3 -0
  4. regscale/integrations/api_paginator.py +932 -0
  5. regscale/integrations/api_paginator_example.py +348 -0
  6. regscale/integrations/commercial/__init__.py +11 -10
  7. regscale/integrations/commercial/burp.py +4 -0
  8. regscale/integrations/commercial/{qualys.py → qualys/__init__.py} +756 -105
  9. regscale/integrations/commercial/qualys/scanner.py +1051 -0
  10. regscale/integrations/commercial/qualys/variables.py +21 -0
  11. regscale/integrations/commercial/sicura/api.py +1 -0
  12. regscale/integrations/commercial/stigv2/click_commands.py +36 -8
  13. regscale/integrations/commercial/stigv2/stig_integration.py +63 -9
  14. regscale/integrations/commercial/tenablev2/__init__.py +9 -0
  15. regscale/integrations/commercial/tenablev2/authenticate.py +23 -2
  16. regscale/integrations/commercial/tenablev2/commands.py +779 -0
  17. regscale/integrations/commercial/tenablev2/jsonl_scanner.py +1999 -0
  18. regscale/integrations/commercial/tenablev2/sc_scanner.py +600 -0
  19. regscale/integrations/commercial/tenablev2/scanner.py +7 -5
  20. regscale/integrations/commercial/tenablev2/utils.py +21 -4
  21. regscale/integrations/commercial/tenablev2/variables.py +4 -0
  22. regscale/integrations/jsonl_scanner_integration.py +523 -142
  23. regscale/integrations/scanner_integration.py +102 -26
  24. regscale/integrations/transformer/__init__.py +17 -0
  25. regscale/integrations/transformer/data_transformer.py +445 -0
  26. regscale/integrations/transformer/mappings/__init__.py +8 -0
  27. regscale/integrations/variables.py +2 -0
  28. regscale/models/__init__.py +5 -2
  29. regscale/models/integration_models/cisa_kev_data.json +63 -7
  30. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  31. regscale/models/regscale_models/asset.py +5 -2
  32. regscale/models/regscale_models/file.py +5 -2
  33. regscale/regscale.py +3 -1
  34. {regscale_cli-6.17.0.0.dist-info → regscale_cli-6.19.0.0.dist-info}/METADATA +1 -1
  35. {regscale_cli-6.17.0.0.dist-info → regscale_cli-6.19.0.0.dist-info}/RECORD +47 -31
  36. tests/regscale/core/test_version.py +22 -0
  37. tests/regscale/integrations/__init__.py +0 -0
  38. tests/regscale/integrations/test_api_paginator.py +597 -0
  39. tests/regscale/integrations/test_integration_mapping.py +60 -0
  40. tests/regscale/integrations/test_issue_creation.py +317 -0
  41. tests/regscale/integrations/test_issue_due_date.py +46 -0
  42. tests/regscale/integrations/transformer/__init__.py +0 -0
  43. tests/regscale/integrations/transformer/test_data_transformer.py +850 -0
  44. regscale/integrations/commercial/tenablev2/click.py +0 -1637
  45. {regscale_cli-6.17.0.0.dist-info → regscale_cli-6.19.0.0.dist-info}/LICENSE +0 -0
  46. {regscale_cli-6.17.0.0.dist-info → regscale_cli-6.19.0.0.dist-info}/WHEEL +0 -0
  47. {regscale_cli-6.17.0.0.dist-info → regscale_cli-6.19.0.0.dist-info}/entry_points.txt +0 -0
  48. {regscale_cli-6.17.0.0.dist-info → regscale_cli-6.19.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,317 @@
1
+ import unittest
2
+ from typing import List, Iterator
3
+ from unittest.mock import MagicMock
4
+
5
+ import freezegun
6
+ import pytest
7
+
8
+ from regscale.core.utils.date import date_str, get_day_increment
9
+ from regscale.integrations.scanner_integration import (
10
+ ScannerIntegration,
11
+ ScannerIntegrationType,
12
+ IntegrationAsset,
13
+ IntegrationFinding,
14
+ )
15
+ from regscale.integrations.variables import ScannerVariables
16
+ from regscale.models import regscale_models
17
+ from regscale.models.regscale_models.regscale_model import RegScaleModel
18
+
19
+ TEST_TIME = "2024-08-06 14:17:06"
20
+
21
+
22
+ def create_test_assets():
23
+ """
24
+ Create test assets.
25
+ """
26
+ for i in range(1, 3):
27
+ regscale_models.Asset(
28
+ id=i,
29
+ name=f"New Asset {i}",
30
+ otherTrackingNumber=f"new_asset{i}",
31
+ assetType="Server",
32
+ assetCategory="Physical",
33
+ assetOwnerId="test_owner",
34
+ status="Active (On Network)",
35
+ parentModule="securityplans",
36
+ parentId=1,
37
+ ).create()
38
+
39
+
40
+ class TestScannerIntegration(ScannerIntegration):
41
+ """
42
+ A concrete implementation of ScannerIntegration for testing purposes.
43
+ """
44
+
45
+ title = "Test Scanner"
46
+ asset_identifier_field = "otherTrackingNumber"
47
+ type = ScannerIntegrationType.VULNERABILITY
48
+
49
+ def fetch_assets(self, *args, **kwargs) -> Iterator[IntegrationAsset]:
50
+ """
51
+ Fetch mock assets for testing.
52
+
53
+ :param args: Variable length argument list
54
+ :param kwargs: Arbitrary keyword arguments
55
+ :return: An iterator of IntegrationAsset objects
56
+ """
57
+ assets = [
58
+ IntegrationAsset(
59
+ identifier=f"new_asset{i}",
60
+ name=f"New Asset {i}",
61
+ asset_type="Server",
62
+ asset_category="Physical",
63
+ software_inventory=[],
64
+ ports_and_protocols=[],
65
+ source_data={"key": "value"},
66
+ asset_owner_id="test_owner",
67
+ url="http://example.com",
68
+ )
69
+ for i in range(1, 3)
70
+ ]
71
+ return iter(assets)
72
+
73
+ def fetch_findings(self, *args, **kwargs) -> List[IntegrationFinding]:
74
+ """
75
+ Fetch mock findings for testing.
76
+
77
+ :rtype: List[IntegrationFinding]
78
+ :return: A list of IntegrationFinding objects
79
+ """
80
+ return [
81
+ IntegrationFinding(
82
+ title=f"Test Finding {i}",
83
+ asset_identifier=f"new_asset{i}",
84
+ description="This is a test finding description",
85
+ external_id=f"FINDING-00{i}",
86
+ remediation="Apply the latest security patch",
87
+ control_labels=[],
88
+ category="Vulnerability",
89
+ severity=regscale_models.IssueSeverity.High,
90
+ status=regscale_models.IssueStatus.Open,
91
+ cve="CVE-2023-12345",
92
+ first_seen=TEST_TIME,
93
+ last_seen=TEST_TIME,
94
+ plugin_name="Testing Issue Creation",
95
+ )
96
+ for i in range(1, 3)
97
+ ]
98
+
99
+ @staticmethod
100
+ def mark_asset_inactive(asset: regscale_models.Asset) -> None:
101
+ """
102
+ Mark an asset as inactive.
103
+
104
+ :param regscale_models.Asset asset: The asset to mark as inactive
105
+ """
106
+ asset.status = "Inactive"
107
+ asset.save()
108
+
109
+
110
+ @pytest.fixture(autouse=True)
111
+ def setup_scanner(mock_regscale_models) -> TestScannerIntegration:
112
+ """
113
+ Fixture to set up a TestScannerIntegration instance for testing.
114
+
115
+ :param MagicMock mock_regscale_models: Mocked RegScaleModel
116
+ :rtype: TestScannerIntegration
117
+ :return: A configured TestScannerIntegration instance
118
+ """
119
+ RegScaleModel.clear_cache()
120
+ create_test_assets()
121
+ scanner = TestScannerIntegration(plan_id=1)
122
+ scanner.regscale_version = "5.64.0"
123
+ return scanner
124
+
125
+
126
+ class TestVulnerabilityScanning:
127
+ """
128
+ Test class for vulnerability scanning functionality.
129
+ """
130
+
131
+ def test_sync_assets(self, mock_regscale_models, setup_scanner):
132
+ """
133
+ Test the sync_assets method of the scanner.
134
+
135
+ :param MagicMock mock_regscale_models: Mocked RegScaleModel
136
+ :param TestScannerIntegration setup_scanner: Configured TestScannerIntegration instance
137
+ """
138
+ # COVERED: The sync_assets method calls instance.update_regscale_assets(assets=assets),
139
+ # which handles asset creation for non-existent assets.
140
+ scanner = setup_scanner
141
+
142
+ # Call the sync_assets method
143
+ processed_assets_count = scanner.sync_assets(plan_id=1)
144
+
145
+ # Verify that two assets were processed
146
+ assert processed_assets_count == 2
147
+
148
+ assets: List[regscale_models.Asset] = regscale_models.Asset.get_all_by_parent(
149
+ parent_id=1, parent_module=regscale_models.SecurityPlan.get_module_string()
150
+ )
151
+ assert len(assets) == 2
152
+ created_asset = assets[0]
153
+
154
+ # Check that the asset was created with the correct values
155
+ assert created_asset == regscale_models.Asset(
156
+ id=created_asset.id,
157
+ name="New Asset 1",
158
+ otherTrackingNumber="new_asset1",
159
+ assetType="Server",
160
+ assetCategory="Physical",
161
+ assetOwnerId="test_owner",
162
+ status="Active (On Network)",
163
+ parentModule="securityplans",
164
+ parentId=created_asset.parentId,
165
+ )
166
+
167
+ @freezegun.freeze_time(TEST_TIME)
168
+ @pytest.mark.parametrize(
169
+ "issue_creation,vulnerability_creation,expected_issue_count,expected_poam,expected_second_asset_issues",
170
+ [
171
+ ("PerAsset", "IssueCreation", 2, False, 1),
172
+ ("Consolidated", "PoamCreation", 1, True, 0),
173
+ ],
174
+ )
175
+ def test_sync_findings(
176
+ self,
177
+ mock_regscale_models,
178
+ setup_scanner,
179
+ issue_creation,
180
+ vulnerability_creation,
181
+ expected_issue_count,
182
+ expected_poam,
183
+ expected_second_asset_issues,
184
+ ):
185
+ """
186
+ Test the sync_findings method with different configurations.
187
+
188
+ :param MagicMock mock_regscale_models: Mocked RegScaleModel
189
+ :param TestScannerIntegration setup_scanner: Configured TestScannerIntegration instance
190
+ :param str issue_creation: The issue creation strategy
191
+ :param str vulnerability_creation: The vulnerability creation strategy
192
+ :param int expected_issue_count: The expected number of issues created
193
+ :param bool expected_poam: Whether the created issue should be a POAM
194
+ :param int expected_second_asset_issues: The expected number of issues for the second asset
195
+ """
196
+ # COVERED: This test checks the creation of a scan history per import,
197
+ # creation of vulnerabilities for each finding as a child of the affected asset,
198
+ # and creation of issues per unique asset / vulnerability pair.
199
+ ScannerVariables.issueCreation = issue_creation
200
+ ScannerVariables.vulnerabilityCreation = vulnerability_creation
201
+ scanner = setup_scanner
202
+
203
+ # Call the sync_findings method
204
+ processed_findings_count = scanner.sync_findings(plan_id=1)
205
+
206
+ # Verify that two findings were processed
207
+ assert processed_findings_count == 2
208
+
209
+ # Check that a scan history was created
210
+ scan_histories: List[regscale_models.ScanHistory] = regscale_models.ScanHistory.get_all_by_parent(
211
+ parent_id=1, parent_module=regscale_models.SecurityPlan.get_module_string()
212
+ )
213
+ assert len(scan_histories) == 1
214
+ created_scan_history = scan_histories[0]
215
+
216
+ assert created_scan_history == regscale_models.ScanHistory(
217
+ id=created_scan_history.id,
218
+ scanDate=created_scan_history.scanDate,
219
+ scanningTool="Test Scanner",
220
+ parentModule="securityplans",
221
+ parentId=1,
222
+ )
223
+
224
+ # Check that a vulnerability was created
225
+ vulnerabilities: List[regscale_models.Vulnerability] = regscale_models.Vulnerability.get_all_by_parent(
226
+ parent_id=1, parent_module=regscale_models.SecurityPlan.get_module_string()
227
+ )
228
+ assert len(vulnerabilities) == 1
229
+ for vulnerability in vulnerabilities:
230
+ if vulnerability.title == "Test Finding 2":
231
+ created_vulnerability = vulnerability
232
+ break
233
+
234
+ assert created_vulnerability == regscale_models.Vulnerability( # type: ignore
235
+ id=created_vulnerability.id,
236
+ title="Test Finding 2",
237
+ description="This is a test finding description",
238
+ severity=regscale_models.VulnerabilitySeverity.High,
239
+ status=regscale_models.VulnerabilityStatus.Open,
240
+ parent_id=1,
241
+ parent_module=regscale_models.SecurityPlan.get_module_string(),
242
+ dns="unknown",
243
+ cve="CVE-2023-12345",
244
+ plugInName="CVE-2023-12345",
245
+ plugInText="",
246
+ scan_id=created_scan_history.id,
247
+ last_seen=TEST_TIME,
248
+ first_seen=TEST_TIME,
249
+ plug_in_name="CVE-2023-12345",
250
+ plug_in_text="",
251
+ )
252
+
253
+ # Check that issues were created as expected
254
+ total_issues = 0
255
+ assets: List[regscale_models.Asset] = regscale_models.Asset.get_all_by_parent(
256
+ parent_id=1, parent_module=regscale_models.SecurityPlan.get_module_string()
257
+ )
258
+ for asset in assets:
259
+ issues: List[regscale_models.Issue] = regscale_models.Issue.get_all_by_parent(
260
+ parent_id=asset.id, parent_module="assets"
261
+ )
262
+ total_issues += len(issues)
263
+
264
+ assert total_issues == expected_issue_count
265
+
266
+ # Get the first created issue for further checks
267
+ first_asset_issues: List[regscale_models.Issue] = (
268
+ regscale_models.Issue.get_all_by_parent(parent_id=assets[0].id, parent_module="assets") if assets else []
269
+ )
270
+ created_issue = first_asset_issues[0] if first_asset_issues else None
271
+
272
+ expected_asset_identifier = "new_asset1\nnew_asset2" if issue_creation == "Consolidated" else "new_asset1"
273
+ expected_other_identifier = (
274
+ "1:CVE-2023-12345" if issue_creation == "Consolidated" else "1:CVE-2023-12345:new_asset1"
275
+ )
276
+
277
+ if created_issue:
278
+ assert created_issue == regscale_models.Issue(
279
+ id=created_issue.id,
280
+ title="Test Finding 1",
281
+ severityLevel=regscale_models.IssueSeverity.High,
282
+ issueOwnerId=scanner.get_assessor_id(),
283
+ dueDate=date_str(get_day_increment(TEST_TIME, 60)),
284
+ identification="Vulnerability Assessment",
285
+ sourceReport="Test Scanner",
286
+ description="This is a test finding description",
287
+ status="Open",
288
+ securityPlanId=1,
289
+ cve="CVE-2023-12345",
290
+ assetIdentifier=expected_asset_identifier,
291
+ dateFirstDetected=TEST_TIME,
292
+ parentId=assets[0].id,
293
+ parentModule="assets",
294
+ dateLastUpdated=TEST_TIME,
295
+ securityChecks="FINDING-001",
296
+ recommendedActions="",
297
+ isPoam=expected_poam,
298
+ otherIdentifier=expected_other_identifier,
299
+ remediationDescription="",
300
+ vulnerabilityId=created_vulnerability.id,
301
+ )
302
+
303
+ # Check issues for the second asset
304
+ second_asset_issues: List[regscale_models.Issue] = regscale_models.Issue.get_all_by_parent(
305
+ parent_id=assets[1].id, parent_module="assets"
306
+ )
307
+ assert len(second_asset_issues) == expected_second_asset_issues
308
+
309
+ # NOTE: To fully confirm coverage of all test cases, additional tests may be needed to check:
310
+ # - Closing of vulnerabilities that no longer exist in the scan
311
+ # - Updating of existing issues when findings change
312
+ # - Closing of issues that are no longer in the scan
313
+ # - Handling of different vulnerability creation and issue creation configurations
314
+
315
+
316
+ if __name__ == "__main__":
317
+ unittest.main()
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env python
2
+
3
+ import pytest
4
+
5
+ from regscale.integrations.scanner_integration import issue_due_date
6
+ from regscale.models import regscale_models
7
+
8
+
9
+ def test_issue_due_date():
10
+ # Test data
11
+ created_date = "2023-01-01"
12
+ severity = regscale_models.IssueSeverity.High
13
+ title = "test_integration"
14
+ config = {"issues": {"test_integration": {"high": 1, "moderate": 2, "low": 3}}}
15
+
16
+ # Expected due date
17
+ expected_due_date = "2023-01-02"
18
+
19
+ # Call the function
20
+ result = issue_due_date(severity, created_date, title=title, config=config)
21
+
22
+ # Assert the result
23
+ assert result == expected_due_date
24
+
25
+ # Test with low value
26
+ severity = regscale_models.IssueSeverity.Low
27
+ expected_due_date = "2023-01-04"
28
+ result = issue_due_date(severity, created_date, title=title, config=config)
29
+ assert result == expected_due_date
30
+
31
+ # Test with defaults
32
+ severity = regscale_models.IssueSeverity.Moderate
33
+ expected_due_date = "2023-07-30"
34
+ result = issue_due_date(severity, created_date)
35
+ assert result == expected_due_date
36
+
37
+ # Test with no matching title in config
38
+ severity = regscale_models.IssueSeverity.Low
39
+ title = "Nonexistent_Integration"
40
+ expected_due_date = "2023-12-31" # 364 days is the default
41
+ result = issue_due_date(severity=severity, created_date=created_date, title=title, config=config)
42
+ assert result == expected_due_date
43
+
44
+
45
+ if __name__ == "__main__":
46
+ pytest.main()
File without changes