regscale-cli 6.18.0.0__py3-none-any.whl → 6.19.0.1__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/__init__.py +1 -1
- regscale/integrations/api_paginator.py +932 -0
- regscale/integrations/api_paginator_example.py +348 -0
- regscale/integrations/commercial/__init__.py +11 -10
- regscale/integrations/commercial/{qualys.py → qualys/__init__.py} +756 -105
- regscale/integrations/commercial/qualys/scanner.py +1051 -0
- regscale/integrations/commercial/qualys/variables.py +21 -0
- regscale/integrations/commercial/sicura/api.py +1 -0
- regscale/integrations/commercial/stigv2/click_commands.py +36 -8
- regscale/integrations/commercial/stigv2/stig_integration.py +63 -9
- regscale/integrations/commercial/tenablev2/__init__.py +9 -0
- regscale/integrations/commercial/tenablev2/authenticate.py +23 -2
- regscale/integrations/commercial/tenablev2/commands.py +779 -0
- regscale/integrations/commercial/tenablev2/jsonl_scanner.py +1999 -0
- regscale/integrations/commercial/tenablev2/sc_scanner.py +600 -0
- regscale/integrations/commercial/tenablev2/scanner.py +7 -5
- regscale/integrations/commercial/tenablev2/utils.py +21 -4
- regscale/integrations/commercial/tenablev2/variables.py +4 -0
- regscale/integrations/jsonl_scanner_integration.py +523 -142
- regscale/integrations/scanner_integration.py +102 -26
- regscale/integrations/transformer/__init__.py +17 -0
- regscale/integrations/transformer/data_transformer.py +445 -0
- regscale/integrations/transformer/mappings/__init__.py +8 -0
- regscale/integrations/variables.py +2 -0
- regscale/models/__init__.py +5 -2
- regscale/models/integration_models/cisa_kev_data.json +6 -6
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/regscale_models/asset.py +5 -2
- regscale/models/regscale_models/file.py +5 -2
- regscale/models/regscale_models/group.py +2 -1
- regscale/models/regscale_models/user_group.py +1 -1
- regscale/regscale.py +3 -1
- {regscale_cli-6.18.0.0.dist-info → regscale_cli-6.19.0.1.dist-info}/METADATA +1 -1
- {regscale_cli-6.18.0.0.dist-info → regscale_cli-6.19.0.1.dist-info}/RECORD +46 -30
- tests/regscale/core/test_version.py +22 -0
- tests/regscale/integrations/__init__.py +0 -0
- tests/regscale/integrations/test_api_paginator.py +597 -0
- tests/regscale/integrations/test_integration_mapping.py +60 -0
- tests/regscale/integrations/test_issue_creation.py +317 -0
- tests/regscale/integrations/test_issue_due_date.py +46 -0
- tests/regscale/integrations/transformer/__init__.py +0 -0
- tests/regscale/integrations/transformer/test_data_transformer.py +850 -0
- regscale/integrations/commercial/tenablev2/click.py +0 -1641
- {regscale_cli-6.18.0.0.dist-info → regscale_cli-6.19.0.1.dist-info}/LICENSE +0 -0
- {regscale_cli-6.18.0.0.dist-info → regscale_cli-6.19.0.1.dist-info}/WHEEL +0 -0
- {regscale_cli-6.18.0.0.dist-info → regscale_cli-6.19.0.1.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.18.0.0.dist-info → regscale_cli-6.19.0.1.dist-info}/top_level.txt +0 -0
|
@@ -50,11 +50,11 @@ def get_thread_workers_max() -> int:
|
|
|
50
50
|
def issue_due_date(
|
|
51
51
|
severity: regscale_models.IssueSeverity,
|
|
52
52
|
created_date: str,
|
|
53
|
-
critical: int =
|
|
54
|
-
high: int =
|
|
55
|
-
moderate: int =
|
|
56
|
-
low: int =
|
|
57
|
-
title: str = "",
|
|
53
|
+
critical: Optional[int] = None,
|
|
54
|
+
high: Optional[int] = None,
|
|
55
|
+
moderate: Optional[int] = None,
|
|
56
|
+
low: Optional[int] = None,
|
|
57
|
+
title: Optional[str] = "",
|
|
58
58
|
config: Optional[Dict[str, Dict]] = None,
|
|
59
59
|
) -> str:
|
|
60
60
|
"""
|
|
@@ -62,15 +62,24 @@ def issue_due_date(
|
|
|
62
62
|
|
|
63
63
|
:param regscale_models.IssueSeverity severity: The severity of the issue.
|
|
64
64
|
:param str created_date: The creation date of the issue.
|
|
65
|
-
:param int critical: Days until due for high severity issues.
|
|
66
|
-
:param int high: Days until due for high severity issues.
|
|
67
|
-
:param int moderate: Days until due for moderate severity issues.
|
|
68
|
-
:param int low: Days until due for low severity issues.
|
|
69
|
-
:param str title: The title of the Integration.
|
|
70
|
-
:param Dict[str, Dict] config: Configuration options for the due date calculation.
|
|
65
|
+
:param Optional[int] critical: Days until due for high severity issues.
|
|
66
|
+
:param Optional[int] high: Days until due for high severity issues.
|
|
67
|
+
:param Optional[int] moderate: Days until due for moderate severity issues.
|
|
68
|
+
:param Optional[int] low: Days until due for low severity issues.
|
|
69
|
+
:param Optional[str] title: The title of the Integration.
|
|
70
|
+
:param Optional[Dict[str, Dict]] config: Configuration options for the due date calculation.
|
|
71
71
|
:return: The due date for the issue.
|
|
72
72
|
:rtype: str
|
|
73
73
|
"""
|
|
74
|
+
if critical is None:
|
|
75
|
+
critical = ScannerVariables.issueDueDates.get("critical", 30)
|
|
76
|
+
if high is None:
|
|
77
|
+
high = ScannerVariables.issueDueDates.get("high", 60)
|
|
78
|
+
if moderate is None:
|
|
79
|
+
moderate = ScannerVariables.issueDueDates.get("moderate", 120)
|
|
80
|
+
if low is None:
|
|
81
|
+
low = ScannerVariables.issueDueDates.get("low", 364)
|
|
82
|
+
|
|
74
83
|
if config is None:
|
|
75
84
|
config = {}
|
|
76
85
|
|
|
@@ -452,6 +461,21 @@ class IntegrationFinding:
|
|
|
452
461
|
vpr_score: Optional[float] = None
|
|
453
462
|
|
|
454
463
|
def __post_init__(self):
|
|
464
|
+
"""Validate and adjust types after initialization."""
|
|
465
|
+
# Set default date values if empty
|
|
466
|
+
if not self.first_seen:
|
|
467
|
+
self.first_seen = get_current_datetime()
|
|
468
|
+
if not self.last_seen:
|
|
469
|
+
self.last_seen = get_current_datetime()
|
|
470
|
+
if not self.scan_date:
|
|
471
|
+
self.scan_date = get_current_datetime()
|
|
472
|
+
|
|
473
|
+
# Validate the values of the dataclass
|
|
474
|
+
if not self.title:
|
|
475
|
+
self.title = "Unknown Issue"
|
|
476
|
+
if not self.description:
|
|
477
|
+
self.description = "No description provided"
|
|
478
|
+
|
|
455
479
|
if self.plugin_name is None:
|
|
456
480
|
self.plugin_name = self.cve or self.title
|
|
457
481
|
if self.plugin_id is None:
|
|
@@ -593,7 +617,7 @@ class ScannerIntegration(ABC):
|
|
|
593
617
|
# Close Outdated Findings
|
|
594
618
|
close_outdated_findings = True
|
|
595
619
|
|
|
596
|
-
def __init__(self, plan_id: int, tenant_id: int = 1, **kwargs):
|
|
620
|
+
def __init__(self, plan_id: int, tenant_id: int = 1, is_component: bool = False, **kwargs):
|
|
597
621
|
"""
|
|
598
622
|
Initialize the ScannerIntegration.
|
|
599
623
|
|
|
@@ -607,6 +631,7 @@ class ScannerIntegration(ABC):
|
|
|
607
631
|
logger.debug(f"RegScale Version: {self.regscale_version}")
|
|
608
632
|
self.plan_id: int = plan_id
|
|
609
633
|
self.tenant_id: int = tenant_id
|
|
634
|
+
self.is_component: bool = is_component
|
|
610
635
|
self.components: ThreadSafeList[Any] = ThreadSafeList()
|
|
611
636
|
self.asset_map_by_identifier: ThreadSafeDict[str, regscale_models.Asset] = ThreadSafeDict()
|
|
612
637
|
self.software_to_create: ThreadSafeList[regscale_models.SoftwareInventory] = ThreadSafeList()
|
|
@@ -759,7 +784,9 @@ class ScannerIntegration(ABC):
|
|
|
759
784
|
"""
|
|
760
785
|
if self.options_map_assets_to_components:
|
|
761
786
|
# Fetches the asset map directly using a specified key field.
|
|
762
|
-
return regscale_models.Asset.get_map(
|
|
787
|
+
return regscale_models.Asset.get_map(
|
|
788
|
+
plan_id=self.plan_id, key_field=self.asset_identifier_field, is_component=self.is_component
|
|
789
|
+
)
|
|
763
790
|
else:
|
|
764
791
|
# Constructs the asset map by fetching all assets under the plan and using the asset identifier field as
|
|
765
792
|
# the key.
|
|
@@ -767,10 +794,27 @@ class ScannerIntegration(ABC):
|
|
|
767
794
|
getattr(x, self.asset_identifier_field): x
|
|
768
795
|
for x in regscale_models.Asset.get_all_by_parent(
|
|
769
796
|
parent_id=self.plan_id,
|
|
770
|
-
parent_module=
|
|
797
|
+
parent_module=(
|
|
798
|
+
regscale_models.Component.get_module_string()
|
|
799
|
+
if self.is_component
|
|
800
|
+
else regscale_models.SecurityPlan.get_module_string()
|
|
801
|
+
),
|
|
771
802
|
)
|
|
772
803
|
}
|
|
773
804
|
|
|
805
|
+
def get_issues_map(self) -> dict[int, regscale_models.Issue]:
|
|
806
|
+
"""
|
|
807
|
+
Gets the issues map
|
|
808
|
+
|
|
809
|
+
:return: The issues map
|
|
810
|
+
:rtype: dict[int, regscale_models.Issue]
|
|
811
|
+
"""
|
|
812
|
+
all_issues = regscale_models.Issue.get_all_by_parent(
|
|
813
|
+
parent_id=self.plan_id,
|
|
814
|
+
parent_module=regscale_models.SecurityPlan.get_module_string(),
|
|
815
|
+
)
|
|
816
|
+
return {issue.integrationFindingId: issue for issue in all_issues}
|
|
817
|
+
|
|
774
818
|
@abstractmethod
|
|
775
819
|
def fetch_findings(self, *args, **kwargs) -> Iterator[IntegrationFinding]:
|
|
776
820
|
"""
|
|
@@ -882,10 +926,17 @@ class ScannerIntegration(ABC):
|
|
|
882
926
|
"""
|
|
883
927
|
if any(self.components):
|
|
884
928
|
return self.components
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
929
|
+
if self.is_component:
|
|
930
|
+
components: List[regscale_models.Component] = [
|
|
931
|
+
regscale_models.Component.get_object(
|
|
932
|
+
object_id=self.plan_id,
|
|
933
|
+
)
|
|
934
|
+
]
|
|
935
|
+
else:
|
|
936
|
+
components: List[regscale_models.Component] = regscale_models.Component.get_all_by_parent(
|
|
937
|
+
parent_id=self.plan_id,
|
|
938
|
+
parent_module=regscale_models.SecurityPlan.get_module_string(),
|
|
939
|
+
)
|
|
889
940
|
self.components = ThreadSafeList(components)
|
|
890
941
|
return self.components
|
|
891
942
|
|
|
@@ -986,7 +1037,7 @@ class ScannerIntegration(ABC):
|
|
|
986
1037
|
componentOwnerId=self.get_assessor_id(),
|
|
987
1038
|
).get_or_create()
|
|
988
1039
|
self.components.append(component)
|
|
989
|
-
if component.securityPlansId:
|
|
1040
|
+
if component.securityPlansId and not self.is_component:
|
|
990
1041
|
component_mapping = regscale_models.ComponentMapping(
|
|
991
1042
|
componentId=component.id,
|
|
992
1043
|
securityPlanId=self.plan_id,
|
|
@@ -1038,7 +1089,7 @@ class ScannerIntegration(ABC):
|
|
|
1038
1089
|
parentId=component.id if component else self.plan_id,
|
|
1039
1090
|
parentModule=(
|
|
1040
1091
|
regscale_models.Component.get_module_string()
|
|
1041
|
-
if component
|
|
1092
|
+
if component or self.is_component
|
|
1042
1093
|
else regscale_models.SecurityPlan.get_module_string()
|
|
1043
1094
|
),
|
|
1044
1095
|
assetType=asset.asset_type,
|
|
@@ -1222,6 +1273,10 @@ class ScannerIntegration(ABC):
|
|
|
1222
1273
|
assets_processed = self._process_assets(assets, loading_assets)
|
|
1223
1274
|
|
|
1224
1275
|
self._perform_batch_operations(self.asset_progress)
|
|
1276
|
+
if self.num_assets_to_process and self.asset_progress.tasks[loading_assets].completed != float(
|
|
1277
|
+
self.num_assets_to_process
|
|
1278
|
+
):
|
|
1279
|
+
self.asset_progress.update(loading_assets, completed=self.num_assets_to_process)
|
|
1225
1280
|
|
|
1226
1281
|
return assets_processed
|
|
1227
1282
|
|
|
@@ -1537,7 +1592,11 @@ class ScannerIntegration(ABC):
|
|
|
1537
1592
|
|
|
1538
1593
|
# Update all fields
|
|
1539
1594
|
issue.parentId = self.plan_id
|
|
1540
|
-
issue.parentModule =
|
|
1595
|
+
issue.parentModule = (
|
|
1596
|
+
regscale_models.Component.get_module_string()
|
|
1597
|
+
if self.is_component
|
|
1598
|
+
else regscale_models.SecurityPlan.get_module_string()
|
|
1599
|
+
)
|
|
1541
1600
|
issue.vulnerabilityId = finding.vulnerability_id
|
|
1542
1601
|
issue.title = issue_title
|
|
1543
1602
|
issue.dateCreated = finding.date_created
|
|
@@ -1549,7 +1608,7 @@ class ScannerIntegration(ABC):
|
|
|
1549
1608
|
)
|
|
1550
1609
|
issue.severityLevel = finding.severity
|
|
1551
1610
|
issue.issueOwnerId = self.assessor_id
|
|
1552
|
-
issue.securityPlanId = self.plan_id
|
|
1611
|
+
issue.securityPlanId = self.plan_id if not self.is_component else None
|
|
1553
1612
|
issue.identification = "Vulnerability Assessment"
|
|
1554
1613
|
issue.dateFirstDetected = finding.first_seen
|
|
1555
1614
|
issue.dueDate = finding.due_date
|
|
@@ -1907,6 +1966,15 @@ class ScannerIntegration(ABC):
|
|
|
1907
1966
|
self.log_error("1. Asset not found for identifier %s", identifier)
|
|
1908
1967
|
return asset
|
|
1909
1968
|
|
|
1969
|
+
def get_issue_by_integration_finding_id(self, integration_finding_id: str) -> Optional[regscale_models.Issue]:
|
|
1970
|
+
"""
|
|
1971
|
+
Gets an issue by its integration finding ID
|
|
1972
|
+
|
|
1973
|
+
:param str integration_finding_id: The integration finding ID
|
|
1974
|
+
:return: The issue
|
|
1975
|
+
"""
|
|
1976
|
+
return self.issues_map.get(integration_finding_id)
|
|
1977
|
+
|
|
1910
1978
|
def process_checklist(self, finding: IntegrationFinding) -> int:
|
|
1911
1979
|
"""
|
|
1912
1980
|
Processes a single checklist item based on the provided finding.
|
|
@@ -2103,7 +2171,11 @@ class ScannerIntegration(ABC):
|
|
|
2103
2171
|
"""
|
|
2104
2172
|
scan_history = regscale_models.ScanHistory(
|
|
2105
2173
|
parentId=self.plan_id,
|
|
2106
|
-
parentModule=
|
|
2174
|
+
parentModule=(
|
|
2175
|
+
regscale_models.Component.get_module_string()
|
|
2176
|
+
if self.is_component
|
|
2177
|
+
else regscale_models.SecurityPlan.get_module_string()
|
|
2178
|
+
),
|
|
2107
2179
|
scanningTool=self.title,
|
|
2108
2180
|
scanDate=self.scan_date if self.scan_date else get_current_datetime(),
|
|
2109
2181
|
createdById=self.assessor_id,
|
|
@@ -2224,7 +2296,11 @@ class ScannerIntegration(ABC):
|
|
|
2224
2296
|
description=finding.description,
|
|
2225
2297
|
dateLastUpdated=finding.date_last_updated,
|
|
2226
2298
|
parentId=self.plan_id,
|
|
2227
|
-
parentModule=
|
|
2299
|
+
parentModule=(
|
|
2300
|
+
regscale_models.Component.get_module_string()
|
|
2301
|
+
if self.is_component
|
|
2302
|
+
else regscale_models.SecurityPlan.get_module_string()
|
|
2303
|
+
),
|
|
2228
2304
|
dns=asset.fqdn or "unknown",
|
|
2229
2305
|
status=regscale_models.VulnerabilityStatus.Open,
|
|
2230
2306
|
ipAddress=finding.ip_address or asset.ipAddress or "",
|
|
@@ -2253,7 +2329,7 @@ class ScannerIntegration(ABC):
|
|
|
2253
2329
|
vulnerabilityId=vulnerability.id,
|
|
2254
2330
|
assetId=asset.id,
|
|
2255
2331
|
scanId=scan_history.id,
|
|
2256
|
-
securityPlansId=self.plan_id,
|
|
2332
|
+
securityPlansId=self.plan_id if not self.is_component else None,
|
|
2257
2333
|
createdById=self.assessor_id,
|
|
2258
2334
|
tenantsId=self.tenant_id,
|
|
2259
2335
|
isPublic=True,
|
|
@@ -2712,7 +2788,7 @@ class ScannerIntegration(ABC):
|
|
|
2712
2788
|
APIHandler().log_api_summary()
|
|
2713
2789
|
created_count = instance._results.get("assets", {}).get("created_count", 0)
|
|
2714
2790
|
updated_count = instance._results.get("assets", {}).get("updated_count", 0)
|
|
2715
|
-
dedupe_count = assets_processed - (created_count + updated_count)
|
|
2791
|
+
dedupe_count = (instance.num_assets_to_process or assets_processed) - (created_count + updated_count)
|
|
2716
2792
|
# Ensure dedupe_count is always a positive value
|
|
2717
2793
|
dedupe_count = dedupe_count if dedupe_count >= 0 else dedupe_count * -1
|
|
2718
2794
|
logger.info(
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Transformer package for RegScale integrations.
|
|
5
|
+
|
|
6
|
+
This package provides data transformation capabilities for RegScale integrations,
|
|
7
|
+
enabling the conversion of external data formats into IntegrationAsset and
|
|
8
|
+
IntegrationFinding objects.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from regscale.integrations.transformer.data_transformer import DataTransformer, DataMapping, TENABLE_SC_MAPPING
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"DataTransformer",
|
|
15
|
+
"DataMapping",
|
|
16
|
+
"TENABLE_SC_MAPPING",
|
|
17
|
+
]
|