regscale-cli 6.20.10.0__py3-none-any.whl → 6.21.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/application.py +12 -5
- regscale/core/app/internal/set_permissions.py +58 -27
- regscale/integrations/commercial/nessus/scanner.py +2 -0
- regscale/integrations/commercial/sonarcloud.py +35 -36
- regscale/integrations/commercial/synqly/ticketing.py +51 -0
- regscale/integrations/integration_override.py +15 -6
- regscale/integrations/scanner_integration.py +163 -35
- regscale/models/integration_models/amazon_models/inspector_scan.py +32 -57
- regscale/models/integration_models/aqua.py +92 -78
- regscale/models/integration_models/cisa_kev_data.json +47 -4
- regscale/models/integration_models/defenderimport.py +64 -59
- regscale/models/integration_models/ecr_models/ecr.py +100 -147
- regscale/models/integration_models/flat_file_importer/__init__.py +52 -38
- regscale/models/integration_models/ibm.py +29 -47
- regscale/models/integration_models/nexpose.py +156 -68
- regscale/models/integration_models/prisma.py +46 -66
- regscale/models/integration_models/qualys.py +99 -93
- regscale/models/integration_models/snyk.py +229 -158
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/integration_models/veracode.py +15 -20
- regscale/models/integration_models/xray.py +276 -82
- regscale/models/regscale_models/control_implementation.py +14 -12
- regscale/models/regscale_models/milestone.py +1 -1
- regscale/models/regscale_models/rbac.py +22 -0
- {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.0.0.dist-info}/METADATA +1 -1
- {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.0.0.dist-info}/RECORD +37 -36
- tests/fixtures/test_fixture.py +58 -2
- tests/regscale/core/test_app.py +5 -3
- tests/regscale/integrations/test_integration_mapping.py +522 -40
- tests/regscale/integrations/test_issue_due_date.py +1 -1
- tests/regscale/integrations/test_update_finding_dates.py +336 -0
- tests/regscale/models/test_asset.py +406 -50
- {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.0.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.0.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.0.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.0.0.dist-info}/top_level.txt +0 -0
|
@@ -2,8 +2,8 @@ from typing import List, Optional, Union
|
|
|
2
2
|
|
|
3
3
|
from regscale.core.app.logz import create_logger
|
|
4
4
|
from regscale.core.app.utils.app_utils import get_current_datetime
|
|
5
|
-
from regscale.integrations.scanner_integration import IntegrationFinding
|
|
6
|
-
from regscale.models import Asset,
|
|
5
|
+
from regscale.integrations.scanner_integration import IntegrationAsset, IntegrationFinding
|
|
6
|
+
from regscale.models import Asset, ImportValidater, IssueStatus, Vulnerability
|
|
7
7
|
from regscale.models.integration_models.flat_file_importer import FlatFileImporter
|
|
8
8
|
|
|
9
9
|
APP_NAME = "@app_name"
|
|
@@ -77,31 +77,26 @@ class Veracode(FlatFileImporter):
|
|
|
77
77
|
else:
|
|
78
78
|
name = self.mapping.get_value(dat, "Source", "")
|
|
79
79
|
account_id = str(self.mapping.get_value(dat, "ID", ""))
|
|
80
|
-
asset =
|
|
80
|
+
asset = IntegrationAsset(
|
|
81
81
|
**{
|
|
82
|
-
"id": 0,
|
|
83
82
|
"name": name,
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
83
|
+
"ip_address": "0.0.0.0",
|
|
84
|
+
"identifier": name,
|
|
85
|
+
"other_tracking_number": account_id,
|
|
87
86
|
"status": "Active (On Network)",
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"softwareVendor": "Veracode",
|
|
95
|
-
"softwareName": name,
|
|
96
|
-
"softwareVersion": version,
|
|
97
|
-
"systemAdministratorId": self.config["userId"],
|
|
98
|
-
"parentId": self.attributes.parent_id,
|
|
99
|
-
"parentModule": self.attributes.parent_module,
|
|
87
|
+
"asset_category": "Hardware",
|
|
88
|
+
"software_vendor": "Veracode",
|
|
89
|
+
"software_name": name,
|
|
90
|
+
"software_version": version,
|
|
91
|
+
"asset_type": "Other",
|
|
92
|
+
"scanning_tool": self.name,
|
|
100
93
|
}
|
|
101
94
|
)
|
|
102
95
|
return [asset]
|
|
103
96
|
|
|
104
|
-
def create_vuln(
|
|
97
|
+
def create_vuln(
|
|
98
|
+
self, dat: Optional[dict] = None, **kwargs
|
|
99
|
+
) -> Union[List[IntegrationFinding], List[IntegrationFinding]]:
|
|
105
100
|
"""
|
|
106
101
|
Create a RegScale vulnerability from a vulnerability in the Veracode export file
|
|
107
102
|
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
JFrog Xray Scan information
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import logging
|
|
6
|
+
import traceback
|
|
7
|
+
from typing import Callable, Iterator, Optional
|
|
6
8
|
|
|
7
9
|
from regscale.core.app.application import Application
|
|
8
10
|
from regscale.core.app.logz import create_logger
|
|
9
|
-
from regscale.core.app.utils.app_utils import epoch_to_datetime, get_current_datetime
|
|
10
|
-
from regscale.
|
|
11
|
+
from regscale.core.app.utils.app_utils import epoch_to_datetime, get_current_datetime, is_valid_fqdn
|
|
12
|
+
from regscale.exceptions import ValidationException
|
|
13
|
+
from regscale.integrations.scanner_integration import IntegrationAsset, IntegrationFinding
|
|
14
|
+
from regscale.models import ImportValidater
|
|
11
15
|
from regscale.models.integration_models.flat_file_importer import FlatFileImporter
|
|
12
|
-
from regscale.models.regscale_models
|
|
13
|
-
|
|
16
|
+
from regscale.models.regscale_models import IssueStatus, Vulnerability
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
14
19
|
|
|
15
20
|
|
|
16
21
|
class XRay(FlatFileImporter):
|
|
@@ -25,31 +30,46 @@ class XRay(FlatFileImporter):
|
|
|
25
30
|
def __init__(self, **kwargs):
|
|
26
31
|
self.name = kwargs.get("name")
|
|
27
32
|
regscale_ssp_id = kwargs.get("regscale_ssp_id")
|
|
28
|
-
|
|
29
|
-
self.
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
# Combine related attributes to reduce instance attribute count
|
|
34
|
+
self.scanner_config = {
|
|
35
|
+
"cvss3_score": "cvss_v3_score",
|
|
36
|
+
"vuln_title": "cve",
|
|
37
|
+
"required_headers": ["impacted_artifact"],
|
|
38
|
+
}
|
|
39
|
+
# Combine mapping-related attributes
|
|
40
|
+
self.mapping_config = {
|
|
41
|
+
"mapping_file": kwargs.get("mappings_path"),
|
|
42
|
+
"disable_mapping": kwargs.get("disable_mapping"),
|
|
43
|
+
}
|
|
35
44
|
self.validater = ImportValidater(
|
|
36
|
-
self.required_headers
|
|
45
|
+
self.scanner_config["required_headers"],
|
|
46
|
+
kwargs.get("file_path"),
|
|
47
|
+
self.mapping_config["mapping_file"],
|
|
48
|
+
self.mapping_config["disable_mapping"],
|
|
37
49
|
)
|
|
38
50
|
self.headers = self.validater.parsed_headers
|
|
39
51
|
self.mapping = self.validater.mapping
|
|
40
|
-
|
|
52
|
+
xray_logger = create_logger()
|
|
53
|
+
# set vuln count and asset count in constructor
|
|
54
|
+
vuln_count = 0
|
|
55
|
+
asset_count = 0
|
|
56
|
+
for dat in self.validater.data:
|
|
57
|
+
vuln_count += len(self.mapping.get_value(dat, "cves", []))
|
|
58
|
+
asset_count += 1
|
|
41
59
|
super().__init__(
|
|
42
|
-
logger=
|
|
60
|
+
logger=xray_logger,
|
|
43
61
|
app=Application(),
|
|
44
62
|
headers=None,
|
|
45
63
|
parent_id=regscale_ssp_id,
|
|
46
64
|
parent_module="securityplans",
|
|
47
65
|
asset_func=self.create_asset,
|
|
48
66
|
vuln_func=self.create_vuln,
|
|
67
|
+
vuln_count=vuln_count,
|
|
68
|
+
asset_count=asset_count,
|
|
49
69
|
**kwargs,
|
|
50
70
|
)
|
|
51
71
|
|
|
52
|
-
def create_asset(self, dat: Optional[dict] = None) -> Optional[
|
|
72
|
+
def create_asset(self, dat: Optional[dict] = None) -> Optional[IntegrationAsset]:
|
|
53
73
|
"""
|
|
54
74
|
Create an asset from a row in the Xray JSON file
|
|
55
75
|
|
|
@@ -59,77 +79,251 @@ class XRay(FlatFileImporter):
|
|
|
59
79
|
"""
|
|
60
80
|
|
|
61
81
|
if asset_name := self.mapping.get_value(dat, "impacted_artifact") if isinstance(dat, dict) else dat:
|
|
62
|
-
return
|
|
82
|
+
return IntegrationAsset(
|
|
63
83
|
**{
|
|
64
|
-
"id": 0,
|
|
65
84
|
"name": asset_name,
|
|
66
|
-
"
|
|
67
|
-
"
|
|
85
|
+
"ip_address": "0.0.0.0",
|
|
86
|
+
"identifier": asset_name,
|
|
87
|
+
"other_tracking_number": asset_name,
|
|
68
88
|
"status": "Active (On Network)",
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"assetType": "Other",
|
|
75
|
-
"fqdn": None,
|
|
76
|
-
"operatingSystem": "Linux",
|
|
77
|
-
"systemAdministratorId": self.config["userId"],
|
|
78
|
-
"parentId": self.attributes.parent_id,
|
|
79
|
-
"parentModule": self.attributes.parent_module,
|
|
89
|
+
"asset_category": "Hardware",
|
|
90
|
+
"asset_type": "Other",
|
|
91
|
+
"scanning_tool": self.name,
|
|
92
|
+
"fqdn": asset_name if is_valid_fqdn(asset_name) else None,
|
|
93
|
+
"operating_system": "Linux",
|
|
80
94
|
}
|
|
81
95
|
)
|
|
82
96
|
return None
|
|
83
97
|
|
|
84
|
-
def
|
|
98
|
+
def create_asset_from_name(self, asset_name: str) -> IntegrationAsset:
|
|
99
|
+
"""Create an IntegrationAsset from an asset name
|
|
100
|
+
|
|
101
|
+
:param str asset_name: The name of the asset
|
|
102
|
+
:return: IntegrationAsset object
|
|
103
|
+
:rtype: IntegrationAsset
|
|
85
104
|
"""
|
|
86
|
-
|
|
105
|
+
return IntegrationAsset(
|
|
106
|
+
**{
|
|
107
|
+
"name": asset_name,
|
|
108
|
+
"ip_address": "0.0.0.0",
|
|
109
|
+
"identifier": asset_name,
|
|
110
|
+
"other_tracking_number": asset_name,
|
|
111
|
+
"status": "Active (On Network)",
|
|
112
|
+
"asset_category": "Hardware",
|
|
113
|
+
"asset_type": "Other",
|
|
114
|
+
"scanning_tool": self.name,
|
|
115
|
+
"fqdn": asset_name if is_valid_fqdn(asset_name) else None,
|
|
116
|
+
"operating_system": "Linux",
|
|
117
|
+
}
|
|
118
|
+
)
|
|
87
119
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
120
|
+
def _extract_asset_name(self, data_item) -> Optional[str]:
|
|
121
|
+
"""Extract asset name from data item
|
|
122
|
+
|
|
123
|
+
:param data_item: Data item to extract asset name from
|
|
124
|
+
:return: Asset name if found, None otherwise
|
|
125
|
+
:rtype: Optional[str]
|
|
126
|
+
"""
|
|
127
|
+
if isinstance(data_item, dict):
|
|
128
|
+
return self.mapping.get_value(data_item, "impacted_artifact")
|
|
129
|
+
return data_item if data_item else None
|
|
130
|
+
|
|
131
|
+
def _process_list_data(self) -> Iterator[IntegrationAsset]:
|
|
132
|
+
"""Process list data and yield assets
|
|
133
|
+
|
|
134
|
+
:return: Iterator of IntegrationAsset objects
|
|
135
|
+
:rtype: Iterator[IntegrationAsset]
|
|
136
|
+
"""
|
|
137
|
+
for data_item in self.file_data:
|
|
138
|
+
if asset_name := self._extract_asset_name(data_item):
|
|
139
|
+
yield self.create_asset_from_name(asset_name)
|
|
140
|
+
|
|
141
|
+
def _process_dict_data(self) -> Iterator[IntegrationAsset]:
|
|
142
|
+
"""Process dict data and yield assets
|
|
143
|
+
|
|
144
|
+
:return: Iterator of IntegrationAsset objects
|
|
145
|
+
:rtype: Iterator[IntegrationAsset]
|
|
146
|
+
"""
|
|
147
|
+
if asset_name := self._extract_asset_name(self.file_data):
|
|
148
|
+
yield self.create_asset_from_name(asset_name)
|
|
149
|
+
|
|
150
|
+
def asset_generator(self) -> Iterator[IntegrationAsset]:
|
|
151
|
+
"""Generate IntegrationAsset objects from the data
|
|
152
|
+
|
|
153
|
+
:return: Iterator of IntegrationAsset objects
|
|
154
|
+
:rtype: Iterator[IntegrationAsset]
|
|
155
|
+
"""
|
|
156
|
+
if isinstance(self.file_data, list):
|
|
157
|
+
yield from self._process_list_data()
|
|
158
|
+
elif isinstance(self.file_data, dict):
|
|
159
|
+
yield from self._process_dict_data()
|
|
160
|
+
|
|
161
|
+
def process_assets(self, func: Callable) -> None:
|
|
162
|
+
"""
|
|
163
|
+
Process the assets in the data and create an iterator of IntegrationAsset objects
|
|
164
|
+
|
|
165
|
+
:param Callable func: Function to create asset (not used)
|
|
166
|
+
:return: None
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
# Set the assets as an iterator directly
|
|
170
|
+
self.data["assets"] = self.asset_generator()
|
|
171
|
+
self.integration_assets = self.data["assets"]
|
|
172
|
+
|
|
173
|
+
def create_vuln(self, _dat: Optional[dict] = None, **kwargs) -> Iterator[IntegrationFinding]:
|
|
174
|
+
"""
|
|
175
|
+
Fetches findings from the processed json files
|
|
176
|
+
|
|
177
|
+
:param Optional[dict] _dat: Data row from JSON file (unused, kept for compatibility)
|
|
178
|
+
:param **kwargs: Additional keyword arguments including index
|
|
179
|
+
:return: A list of findings
|
|
180
|
+
:rtype: Iterator[IntegrationFinding]
|
|
181
|
+
"""
|
|
182
|
+
if findings := self.fetch_findings(**kwargs):
|
|
183
|
+
yield from findings
|
|
184
|
+
|
|
185
|
+
def _validate_cve_data(self, cve_data) -> bool:
|
|
186
|
+
"""Validate CVE data structure
|
|
187
|
+
|
|
188
|
+
:param cve_data: CVE data to validate
|
|
189
|
+
:return: True if valid, False otherwise
|
|
190
|
+
:rtype: bool
|
|
191
|
+
"""
|
|
192
|
+
if not isinstance(cve_data, list):
|
|
193
|
+
logger.warning("CVE data is not a list, skipping vulnerability creation")
|
|
194
|
+
return False
|
|
195
|
+
return True
|
|
196
|
+
|
|
197
|
+
def _get_valid_cve_data(self, cve_data: list) -> list:
|
|
198
|
+
"""Filter CVE data to only include valid entries
|
|
199
|
+
|
|
200
|
+
:param list cve_data: Raw CVE data
|
|
201
|
+
:return: Filtered CVE data with actual CVE IDs
|
|
202
|
+
:rtype: list
|
|
203
|
+
"""
|
|
204
|
+
return [c for c in cve_data if c.get("cve")]
|
|
205
|
+
|
|
206
|
+
def _determine_severity_from_cve(self, cve_dat: dict) -> str:
|
|
207
|
+
"""Determine severity from CVE data
|
|
208
|
+
|
|
209
|
+
:param dict cve_dat: CVE data dictionary
|
|
210
|
+
:return: Severity string
|
|
211
|
+
:rtype: str
|
|
212
|
+
"""
|
|
213
|
+
cvss3_score = cve_dat.get("cvss_v3_score", 0.0)
|
|
214
|
+
if cve_dat.get(self.scanner_config["cvss3_score"]):
|
|
215
|
+
return Vulnerability.determine_cvss3_severity_text(float(cvss3_score))
|
|
216
|
+
return "low"
|
|
217
|
+
|
|
218
|
+
def _extract_plugin_id(self, data_item: dict) -> int:
|
|
219
|
+
"""Extract plugin ID from issue ID
|
|
220
|
+
|
|
221
|
+
:param dict data_item: Data item containing issue information
|
|
222
|
+
:return: Plugin ID as integer
|
|
223
|
+
:rtype: int
|
|
224
|
+
"""
|
|
225
|
+
issue_id = self.mapping.get_value(data_item, "issue_id", "Xray-0000")
|
|
226
|
+
try:
|
|
227
|
+
if len(issue_id) > 5:
|
|
228
|
+
return int(issue_id[5:])
|
|
229
|
+
return 0
|
|
230
|
+
except (ValueError, TypeError):
|
|
231
|
+
logger.warning("Could not parse plugin_id from issue_id: %s", issue_id)
|
|
232
|
+
return 0
|
|
233
|
+
|
|
234
|
+
def _get_title_base(self, data_item: dict) -> str:
|
|
235
|
+
"""Get title base for the finding
|
|
236
|
+
|
|
237
|
+
:param dict data_item: Data item containing issue information
|
|
238
|
+
:return: Title base string
|
|
239
|
+
:rtype: str
|
|
240
|
+
"""
|
|
241
|
+
return self.mapping.get_value(data_item, "issue_id") or self.mapping.get_value(
|
|
242
|
+
data_item, "summary", f"XRay Vulnerability from Import {get_current_datetime()}"
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
def _create_finding_from_cve(self, data_item: dict, asset_name: str, cve_dat: dict) -> IntegrationFinding:
|
|
246
|
+
"""Create a single finding from CVE data
|
|
247
|
+
|
|
248
|
+
:param dict data_item: Data item containing vulnerability information
|
|
249
|
+
:param str asset_name: Asset name for the finding
|
|
250
|
+
:param dict cve_dat: CVE data dictionary
|
|
251
|
+
:return: IntegrationFinding object
|
|
252
|
+
:rtype: IntegrationFinding
|
|
253
|
+
"""
|
|
254
|
+
cve = cve_dat.get("cve")
|
|
255
|
+
cvss3_score = cve_dat.get("cvss_v3_score", 0.0)
|
|
256
|
+
severity = self._determine_severity_from_cve(cve_dat)
|
|
257
|
+
plugin_id = self._extract_plugin_id(data_item)
|
|
258
|
+
title_base = self._get_title_base(data_item)
|
|
259
|
+
|
|
260
|
+
return IntegrationFinding(
|
|
261
|
+
title=f"{title_base} on asset {asset_name}",
|
|
262
|
+
description=self.mapping.get_value(data_item, "summary"),
|
|
263
|
+
severity=self.determine_severity(severity),
|
|
264
|
+
status=IssueStatus.Open.value,
|
|
265
|
+
cvss_v3_score=cvss3_score,
|
|
266
|
+
cvss_v3_vector=cve_dat.get("cvss_v3_vector", ""),
|
|
267
|
+
plugin_name=self.mapping.get_value(data_item, "issue_id", "XRay"),
|
|
268
|
+
plugin_id=plugin_id,
|
|
269
|
+
asset_identifier=asset_name,
|
|
270
|
+
cve=cve,
|
|
271
|
+
first_seen=epoch_to_datetime(self.create_epoch),
|
|
272
|
+
last_seen=self.scan_date,
|
|
273
|
+
scan_date=self.scan_date,
|
|
274
|
+
category="Software",
|
|
275
|
+
control_labels=[],
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
def _create_findings_from_data_item(self, data_item: dict, asset_name: str) -> Iterator[IntegrationFinding]:
|
|
279
|
+
"""Create findings from a single data item
|
|
280
|
+
|
|
281
|
+
:param dict data_item: The data item containing vulnerability information
|
|
282
|
+
:param str asset_name: The asset name for the finding
|
|
283
|
+
:yields: IntegrationFinding objects
|
|
284
|
+
"""
|
|
285
|
+
cve_data = self.mapping.get_value(data_item, "cves", [])
|
|
286
|
+
if not self._validate_cve_data(cve_data):
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
valid_cve_data = self._get_valid_cve_data(cve_data)
|
|
290
|
+
for cve_dat in valid_cve_data:
|
|
291
|
+
yield self._create_finding_from_cve(data_item, asset_name, cve_dat)
|
|
292
|
+
|
|
293
|
+
def _process_list_findings(self) -> Iterator[IntegrationFinding]:
|
|
294
|
+
"""Process findings from list data
|
|
295
|
+
|
|
296
|
+
:yields: IntegrationFinding objects
|
|
297
|
+
"""
|
|
298
|
+
for data_item in self.file_data:
|
|
299
|
+
if isinstance(data_item, list):
|
|
300
|
+
continue
|
|
301
|
+
asset_name = self._extract_asset_name(data_item)
|
|
302
|
+
if asset_name:
|
|
303
|
+
yield from self._create_findings_from_data_item(data_item, asset_name)
|
|
304
|
+
|
|
305
|
+
def _process_dict_findings(self) -> Iterator[IntegrationFinding]:
|
|
306
|
+
"""Process findings from dict data
|
|
307
|
+
|
|
308
|
+
:yields: IntegrationFinding objects
|
|
309
|
+
"""
|
|
310
|
+
asset_name = self._extract_asset_name(self.file_data)
|
|
311
|
+
if asset_name:
|
|
312
|
+
yield from self._create_findings_from_data_item(self.file_data, asset_name)
|
|
313
|
+
|
|
314
|
+
def fetch_findings(self, **_) -> Iterator[IntegrationFinding]:
|
|
315
|
+
"""
|
|
316
|
+
Fetch findings from Xray scan data.
|
|
317
|
+
|
|
318
|
+
:raises ValidationException: If there is an error fetching/parsing findings
|
|
319
|
+
:yields: Iterator[IntegrationFinding]
|
|
320
|
+
"""
|
|
321
|
+
try:
|
|
322
|
+
if isinstance(self.file_data, list):
|
|
323
|
+
yield from self._process_list_findings()
|
|
324
|
+
elif isinstance(self.file_data, dict):
|
|
325
|
+
yield from self._process_dict_findings()
|
|
326
|
+
except Exception as exc:
|
|
327
|
+
error_message = traceback.format_exc()
|
|
328
|
+
logger.error("Error fetching findings: %s", error_message)
|
|
329
|
+
raise ValidationException(f"Error fetching findings: {error_message}") from exc
|
|
@@ -70,6 +70,7 @@ class ControlImplementation(RegScaleModel):
|
|
|
70
70
|
|
|
71
71
|
_module_slug = "controlImplementation"
|
|
72
72
|
_module_string = "controls"
|
|
73
|
+
_unique_fields = [["controlID", "parentId", "parentModule"]]
|
|
73
74
|
_get_objects_for_list = True
|
|
74
75
|
|
|
75
76
|
controlOwnerId: str = Field(default_factory=RegScaleModel.get_user_id)
|
|
@@ -248,18 +249,19 @@ class ControlImplementation(RegScaleModel):
|
|
|
248
249
|
return response.json()
|
|
249
250
|
return None
|
|
250
251
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
252
|
+
# Removed for now, will need to be added back once platform changes are made
|
|
253
|
+
# def find_by_unique(self, **kwargs: dict) -> Optional["ControlImplementation"]:
|
|
254
|
+
# """
|
|
255
|
+
# Find an object by unique query.
|
|
256
|
+
#
|
|
257
|
+
# :param dict **kwargs: The unique query parameters
|
|
258
|
+
# :return: The object or None if not found
|
|
259
|
+
# :rtype: Optional[ControlImplementation]
|
|
260
|
+
# """
|
|
261
|
+
#
|
|
262
|
+
# for instance in self.get_by_security_control_id(security_control_id=self.controlID):
|
|
263
|
+
# return instance
|
|
264
|
+
# return None
|
|
263
265
|
|
|
264
266
|
def _get_status_enum(self) -> List["ControlImplementationStatus"]:
|
|
265
267
|
"""
|
|
@@ -24,7 +24,7 @@ class Milestone(RegScaleModel):
|
|
|
24
24
|
completed: Optional[bool] = False
|
|
25
25
|
dateCompleted: Optional[str] = Field(default_factory=get_current_datetime)
|
|
26
26
|
notes: Optional[str] = ""
|
|
27
|
-
parentID: Optional[
|
|
27
|
+
parentID: Optional[int] = None
|
|
28
28
|
parentModule: str = ""
|
|
29
29
|
|
|
30
30
|
@staticmethod
|
|
@@ -28,6 +28,7 @@ class RBAC(RegScaleModel):
|
|
|
28
28
|
add="/api/{model_slug}/add/{moduleId}/{parentId}/{groupId}/{permissionType}",
|
|
29
29
|
public="/api/{model_slug}/public/{moduleId}/{parentId}/{public}",
|
|
30
30
|
none_standard_delete="/api/{model_slug}/{moduleId}/{parentId}/{rbacId}",
|
|
31
|
+
reset="/api/{model_slug}/reset/{moduleId}/{parentId}",
|
|
31
32
|
)
|
|
32
33
|
|
|
33
34
|
@classmethod
|
|
@@ -129,3 +130,24 @@ class RBAC(RegScaleModel):
|
|
|
129
130
|
else:
|
|
130
131
|
cls.log_response_error(response=response)
|
|
131
132
|
return False
|
|
133
|
+
|
|
134
|
+
@classmethod
|
|
135
|
+
def reset(cls, module_id: int, parent_id: int) -> bool:
|
|
136
|
+
"""
|
|
137
|
+
Proliferates the RBAC entry to all its children
|
|
138
|
+
|
|
139
|
+
:param int module_id: The ID of the module
|
|
140
|
+
:param int parent_id: The ID of the parent
|
|
141
|
+
:return: True if the RBAC entry was added, False otherwise
|
|
142
|
+
:rtype: bool
|
|
143
|
+
"""
|
|
144
|
+
response = cls._get_api_handler().get(
|
|
145
|
+
endpoint=cls.get_endpoint("reset").format(
|
|
146
|
+
model_slug=cls._module_slug, moduleId=module_id, parentId=parent_id
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
if response and response.ok:
|
|
150
|
+
return True
|
|
151
|
+
else:
|
|
152
|
+
cls.log_response_error(response=response)
|
|
153
|
+
return False
|