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
|
@@ -3,13 +3,13 @@ Snyk Scan information
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from datetime import datetime
|
|
6
|
-
from typing import Optional, Union
|
|
6
|
+
from typing import List, Optional, Union
|
|
7
7
|
|
|
8
8
|
from regscale.core.app.application import Application
|
|
9
9
|
from regscale.core.app.logz import create_logger
|
|
10
|
-
from regscale.core.app.utils.app_utils import epoch_to_datetime,
|
|
10
|
+
from regscale.core.app.utils.app_utils import epoch_to_datetime, is_valid_fqdn
|
|
11
11
|
from regscale.integrations.scanner_integration import IntegrationAsset, IntegrationFinding
|
|
12
|
-
from regscale.models import Asset, AssetCategory, AssetStatus, AssetType, ImportValidater,
|
|
12
|
+
from regscale.models import Asset, AssetCategory, AssetStatus, AssetType, ImportValidater, IssueSeverity, IssueStatus
|
|
13
13
|
from regscale.models.integration_models.flat_file_importer import FlatFileImporter
|
|
14
14
|
|
|
15
15
|
|
|
@@ -19,42 +19,60 @@ class Snyk(FlatFileImporter):
|
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
def __init__(self, **kwargs):
|
|
22
|
-
|
|
23
|
-
self.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
22
|
+
# Group related attributes to reduce instance attribute count
|
|
23
|
+
self.scanner_config = {
|
|
24
|
+
"name": kwargs.get("name"),
|
|
25
|
+
"auto_fixable": "AUTOFIXABLE",
|
|
26
|
+
"fmt": "%Y-%m-%d",
|
|
27
|
+
"dt_format": "%Y-%m-%d %H:%M:%S",
|
|
28
|
+
"not_implemented_error": "Unsupported file type for Snyk integration. Only XLSX and JSON are supported.",
|
|
29
|
+
}
|
|
30
|
+
self.mapping_config = {
|
|
31
|
+
"mapping_file": kwargs.get("mappings_path"),
|
|
32
|
+
"disable_mapping": kwargs.get("disable_mapping"),
|
|
33
|
+
}
|
|
34
|
+
self.file_config = {
|
|
35
|
+
"file_type": kwargs.get("file_type", ""),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# Set up file-specific configurations
|
|
39
|
+
if "json" in self.file_config["file_type"]:
|
|
40
|
+
self.file_specific_config = {
|
|
41
|
+
"project_name": "projectName",
|
|
42
|
+
"issue_severity": "severity",
|
|
43
|
+
"vuln_title": "title",
|
|
44
|
+
"required_headers": ["projectName", "vulnerabilities"],
|
|
45
|
+
}
|
|
35
46
|
else:
|
|
36
|
-
self.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
self.file_specific_config = {
|
|
48
|
+
"project_name": "PROJECT_NAME",
|
|
49
|
+
"issue_severity": "ISSUE_SEVERITY",
|
|
50
|
+
"vuln_title": "PROBLEM_TITLE",
|
|
51
|
+
"required_headers": [
|
|
52
|
+
"PROJECT_NAME",
|
|
53
|
+
"ISSUE_SEVERITY",
|
|
54
|
+
"PROBLEM_TITLE",
|
|
55
|
+
self.scanner_config["auto_fixable"],
|
|
56
|
+
],
|
|
57
|
+
}
|
|
58
|
+
|
|
47
59
|
self.validater = ImportValidater(
|
|
48
|
-
self.required_headers
|
|
60
|
+
self.file_specific_config["required_headers"],
|
|
61
|
+
kwargs.get("file_path"),
|
|
62
|
+
self.mapping_config["mapping_file"],
|
|
63
|
+
self.mapping_config["disable_mapping"],
|
|
49
64
|
)
|
|
50
65
|
self.headers = self.validater.parsed_headers
|
|
51
66
|
self.mapping = self.validater.mapping
|
|
52
|
-
|
|
67
|
+
|
|
68
|
+
# Set counts based on file type
|
|
69
|
+
if "json" in self.file_config["file_type"]:
|
|
53
70
|
asset_count = 1
|
|
54
71
|
vuln_count = len(self.mapping.get_value(self.validater.data, "vulnerabilities", []))
|
|
55
72
|
else:
|
|
56
73
|
asset_count = None
|
|
57
74
|
vuln_count = None
|
|
75
|
+
|
|
58
76
|
logger = create_logger()
|
|
59
77
|
self.logger = logger
|
|
60
78
|
super().__init__(
|
|
@@ -77,10 +95,12 @@ class Snyk(FlatFileImporter):
|
|
|
77
95
|
:return: The first seen date as a string
|
|
78
96
|
:rtype: str
|
|
79
97
|
"""
|
|
98
|
+
epoch_time = epoch_to_datetime(self.create_epoch, self.scanner_config["fmt"])
|
|
99
|
+
datetime_obj = datetime.strptime(epoch_time, self.scanner_config["dt_format"])
|
|
80
100
|
return datetime.combine(
|
|
81
|
-
|
|
101
|
+
datetime_obj,
|
|
82
102
|
self.mapping.get_value(dat, "FIRST_INTRODUCED", datetime.now().time()),
|
|
83
|
-
).strftime(self.dt_format)
|
|
103
|
+
).strftime(self.scanner_config["dt_format"])
|
|
84
104
|
|
|
85
105
|
def create_asset(self, dat: Optional[dict] = None) -> Union[Asset, IntegrationAsset]:
|
|
86
106
|
"""
|
|
@@ -92,41 +112,41 @@ class Snyk(FlatFileImporter):
|
|
|
92
112
|
"""
|
|
93
113
|
if "json" in self.attributes.file_type:
|
|
94
114
|
return self._parse_json_asset(data=dat)
|
|
95
|
-
|
|
115
|
+
if "xlsx" in self.attributes.file_type:
|
|
96
116
|
return self._parse_xlsx_asset(dat)
|
|
97
|
-
|
|
98
|
-
raise NotImplementedError(self.not_implemented_error)
|
|
117
|
+
raise NotImplementedError(self.scanner_config["not_implemented_error"])
|
|
99
118
|
|
|
100
|
-
def
|
|
119
|
+
def _create_asset(self, project_name: str) -> IntegrationAsset:
|
|
101
120
|
"""
|
|
102
|
-
|
|
121
|
+
Helper function to create an IntegrationAsset with common attributes
|
|
103
122
|
|
|
104
|
-
:param
|
|
105
|
-
:return:
|
|
106
|
-
:rtype:
|
|
123
|
+
:param str project_name: The project name to extract hostname from
|
|
124
|
+
:return: IntegrationAsset object
|
|
125
|
+
:rtype: IntegrationAsset
|
|
107
126
|
"""
|
|
108
|
-
name = self.extract_host(
|
|
127
|
+
name = self.extract_host(project_name)
|
|
109
128
|
valid_name = is_valid_fqdn(name)
|
|
110
|
-
return
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
"bLatestScan": True,
|
|
119
|
-
"bAuthenticatedScan": True,
|
|
120
|
-
"scanningTool": self.name,
|
|
121
|
-
"assetOwnerId": self.config["userId"],
|
|
122
|
-
"assetType": "Other",
|
|
123
|
-
"fqdn": name if valid_name else None,
|
|
124
|
-
"systemAdministratorId": self.config["userId"],
|
|
125
|
-
"parentId": self.attributes.parent_id,
|
|
126
|
-
"parentModule": self.attributes.parent_module,
|
|
127
|
-
}
|
|
129
|
+
return IntegrationAsset(
|
|
130
|
+
scanning_tool=self.scanner_config["name"],
|
|
131
|
+
identifier=name,
|
|
132
|
+
name=name,
|
|
133
|
+
status=AssetStatus.Active,
|
|
134
|
+
asset_category=AssetCategory.Software,
|
|
135
|
+
asset_type=AssetType.Other,
|
|
136
|
+
fqdn=name if valid_name else None,
|
|
128
137
|
)
|
|
129
138
|
|
|
139
|
+
def _parse_xlsx_asset(self, dat: Optional[dict] = None) -> IntegrationAsset:
|
|
140
|
+
"""
|
|
141
|
+
Create an asset from a row in the Snyk XLSX file
|
|
142
|
+
|
|
143
|
+
:param Optional[dict] dat: Data row from XLSX file, defaults to None
|
|
144
|
+
:return: IntegrationAsset object
|
|
145
|
+
:rtype: IntegrationAsset
|
|
146
|
+
"""
|
|
147
|
+
project_name = self.mapping.get_value(dat, self.file_specific_config["project_name"])
|
|
148
|
+
return self._create_asset(project_name)
|
|
149
|
+
|
|
130
150
|
def _parse_json_asset(self, **kwargs) -> IntegrationAsset:
|
|
131
151
|
"""
|
|
132
152
|
Parse assets from Snyk json scan data.
|
|
@@ -135,134 +155,185 @@ class Snyk(FlatFileImporter):
|
|
|
135
155
|
:rtype: IntegrationAsset
|
|
136
156
|
"""
|
|
137
157
|
data = kwargs.pop("data")
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return IntegrationAsset(
|
|
141
|
-
identifier=name,
|
|
142
|
-
name=name,
|
|
143
|
-
status=AssetStatus.Active,
|
|
144
|
-
asset_category=AssetCategory.Software,
|
|
145
|
-
is_latest_scan=True,
|
|
146
|
-
is_authenticated_scan=True,
|
|
147
|
-
scanning_tool=self.name,
|
|
148
|
-
asset_type=AssetType.Other,
|
|
149
|
-
fqdn=name if valid_name else None,
|
|
150
|
-
system_administrator_id=self.config["userId"],
|
|
151
|
-
parent_id=self.attributes.parent_id,
|
|
152
|
-
parent_module=self.attributes.parent_module,
|
|
153
|
-
)
|
|
158
|
+
project_name = self.mapping.get_value(data, self.file_specific_config["project_name"])
|
|
159
|
+
return self._create_asset(project_name)
|
|
154
160
|
|
|
155
161
|
def create_vuln(
|
|
156
162
|
self, dat: Optional[dict] = None, **kwargs
|
|
157
|
-
) -> Optional[Union[
|
|
163
|
+
) -> Optional[Union[List[IntegrationFinding], IntegrationFinding]]:
|
|
158
164
|
"""
|
|
159
165
|
Create a vulnerability from a row in the Snyk file
|
|
160
166
|
|
|
161
167
|
:param Optional[dict] dat: Data row from XLSX or JSON file, defaults to None
|
|
162
168
|
:raises TypeError: If dat is not a dictionary
|
|
163
169
|
:return: RegScale Vulnerability object if xlsx or list of IntegrationFindings if JSON
|
|
164
|
-
:rtype: Optional[Union[
|
|
170
|
+
:rtype: Optional[Union[List[IntegrationFinding], Vulnerability]]
|
|
165
171
|
"""
|
|
166
172
|
if "json" in self.attributes.file_type:
|
|
167
173
|
return self._parse_json_findings(**kwargs)
|
|
168
|
-
|
|
169
|
-
|
|
174
|
+
if "xlsx" in self.attributes.file_type:
|
|
175
|
+
if isinstance(dat, dict):
|
|
176
|
+
return self._parse_xlsx_finding(dat, **kwargs)
|
|
177
|
+
if isinstance(dat, list):
|
|
178
|
+
findings = []
|
|
179
|
+
for finding in dat:
|
|
180
|
+
findings.extend(self._parse_xlsx_finding(finding, **kwargs))
|
|
181
|
+
return findings
|
|
182
|
+
raise NotImplementedError(self.scanner_config["not_implemented_error"])
|
|
183
|
+
|
|
184
|
+
def _create_finding(self, finding_data: dict) -> IntegrationFinding:
|
|
185
|
+
"""
|
|
186
|
+
Helper function to create an IntegrationFinding with common attributes
|
|
187
|
+
|
|
188
|
+
:param dict finding_data: Dictionary containing finding data
|
|
189
|
+
:return: IntegrationFinding object
|
|
190
|
+
:rtype: IntegrationFinding
|
|
191
|
+
"""
|
|
192
|
+
if finding_data.get("title") is None:
|
|
193
|
+
finding_data["title"] = f"{finding_data['description']} on asset {finding_data['hostname']}"
|
|
194
|
+
|
|
195
|
+
return IntegrationFinding(
|
|
196
|
+
title=finding_data["title"],
|
|
197
|
+
description=finding_data["description"],
|
|
198
|
+
severity=finding_data["severity"],
|
|
199
|
+
status=IssueStatus.Open.value,
|
|
200
|
+
plugin_name=finding_data["description"],
|
|
201
|
+
plugin_id=finding_data.get("plugin_id"),
|
|
202
|
+
recommendation_for_mitigation=finding_data["solution"],
|
|
203
|
+
plugin_text=self.mapping.get_value(finding_data["dat"], self.file_specific_config["vuln_title"]),
|
|
204
|
+
asset_identifier=finding_data["hostname"],
|
|
205
|
+
cve=finding_data["cve"],
|
|
206
|
+
cvss_score=finding_data.get("cvss_score"),
|
|
207
|
+
first_seen=self.determine_first_seen(finding_data["dat"]),
|
|
208
|
+
last_seen=self.scan_date,
|
|
209
|
+
scan_date=self.scan_date,
|
|
210
|
+
dns=finding_data["hostname"],
|
|
211
|
+
vpr_score=finding_data.get("vpr_score"),
|
|
212
|
+
remediation=finding_data["solution"],
|
|
213
|
+
category="Software",
|
|
214
|
+
control_labels=[],
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
def _parse_xlsx_finding(self, dat: Optional[dict] = None, **_) -> List[IntegrationFinding]:
|
|
218
|
+
"""
|
|
219
|
+
Create a list of IntegrationFinding objects from a row in the Snyk xlsx file
|
|
220
|
+
|
|
221
|
+
:param Optional[dict] dat: Data row from XLSX file, defaults to None
|
|
222
|
+
:return: A list of IntegrationFinding objects
|
|
223
|
+
:rtype: List[IntegrationFinding]
|
|
224
|
+
"""
|
|
225
|
+
if not dat:
|
|
226
|
+
return []
|
|
227
|
+
|
|
228
|
+
findings: List[IntegrationFinding] = []
|
|
229
|
+
severity = self.determine_severity(
|
|
230
|
+
self.mapping.get_value(dat, self.file_specific_config["issue_severity"]).lower()
|
|
231
|
+
)
|
|
232
|
+
hostname = self.extract_host(self.mapping.get_value(dat, self.file_specific_config["project_name"]))
|
|
233
|
+
description = self.mapping.get_value(dat, self.file_specific_config["vuln_title"])
|
|
234
|
+
solution = self.mapping.get_value(dat, self.scanner_config["auto_fixable"])
|
|
235
|
+
cves = self.mapping.get_value(dat, "CVE", [])
|
|
236
|
+
|
|
237
|
+
if cves:
|
|
238
|
+
for cve in cves:
|
|
239
|
+
finding_data = {
|
|
240
|
+
"dat": dat,
|
|
241
|
+
"hostname": hostname,
|
|
242
|
+
"description": description,
|
|
243
|
+
"severity": severity,
|
|
244
|
+
"solution": solution,
|
|
245
|
+
"cve": cve,
|
|
246
|
+
}
|
|
247
|
+
findings.append(self._create_finding(finding_data))
|
|
170
248
|
else:
|
|
171
|
-
|
|
249
|
+
finding_data = {
|
|
250
|
+
"dat": dat,
|
|
251
|
+
"hostname": hostname,
|
|
252
|
+
"description": description,
|
|
253
|
+
"severity": severity,
|
|
254
|
+
"solution": solution,
|
|
255
|
+
"cve": "",
|
|
256
|
+
}
|
|
257
|
+
findings.append(self._create_finding(finding_data))
|
|
258
|
+
return findings
|
|
172
259
|
|
|
173
|
-
def
|
|
260
|
+
def _extract_json_finding_data(self, dat: dict) -> dict:
|
|
174
261
|
"""
|
|
175
|
-
|
|
262
|
+
Extract common finding data from JSON vulnerability data
|
|
176
263
|
|
|
177
|
-
:param
|
|
178
|
-
:return:
|
|
179
|
-
:rtype:
|
|
264
|
+
:param dict dat: The vulnerability data
|
|
265
|
+
:return: Dictionary containing extracted finding data
|
|
266
|
+
:rtype: dict
|
|
180
267
|
"""
|
|
181
|
-
|
|
182
|
-
severity = self.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
tenantsId=0,
|
|
207
|
-
title=f"{description} on asset {asset.name}",
|
|
208
|
-
description=description,
|
|
209
|
-
plugInText=self.mapping.get_value(dat, self.vuln_title),
|
|
210
|
-
createdById=config["userId"],
|
|
211
|
-
lastUpdatedById=config["userId"],
|
|
212
|
-
dateCreated=get_current_datetime(),
|
|
213
|
-
extra_data={"solution": solution},
|
|
214
|
-
)
|
|
215
|
-
return regscale_vuln
|
|
216
|
-
|
|
217
|
-
def _parse_json_findings(self, **kwargs) -> list[IntegrationFinding]:
|
|
268
|
+
severity_key = self.file_specific_config["issue_severity"]
|
|
269
|
+
severity = self.determine_snyk_severity(dat.get(severity_key, "Low").lower())
|
|
270
|
+
project_name = self.file_specific_config["project_name"]
|
|
271
|
+
hostname = self.extract_host(self.mapping.get_value(dat, project_name)) or self.extract_host(
|
|
272
|
+
self.mapping.get_value(self.validater.data, project_name)
|
|
273
|
+
)
|
|
274
|
+
vuln_title = self.file_specific_config["vuln_title"]
|
|
275
|
+
description = self.mapping.get_value(dat, "description") or self.mapping.get_value(dat, vuln_title)
|
|
276
|
+
solution = self.mapping.get_value(dat, self.scanner_config["auto_fixable"])
|
|
277
|
+
|
|
278
|
+
# if auto fixable is not available, check for upgradeable or patchable, this is for .json files
|
|
279
|
+
if not solution:
|
|
280
|
+
upgradeable = self.mapping.get_value(dat, "isUpgradeable", False)
|
|
281
|
+
patchable = self.mapping.get_value(dat, "isPatchable", False)
|
|
282
|
+
if upgradeable or patchable:
|
|
283
|
+
solution = "Upgrade or patch the vulnerable component."
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
"severity": severity,
|
|
287
|
+
"hostname": hostname,
|
|
288
|
+
"description": description,
|
|
289
|
+
"solution": solution,
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
def _parse_json_findings(self, **kwargs) -> List[IntegrationFinding]:
|
|
218
293
|
"""
|
|
219
|
-
Create a
|
|
294
|
+
Create a list of IntegrationFinding objects from the Snyk json file
|
|
220
295
|
|
|
221
296
|
:return: List of IntegrationFinding objects
|
|
222
|
-
:rtype:
|
|
297
|
+
:rtype: List[IntegrationFinding]
|
|
223
298
|
"""
|
|
224
299
|
findings = []
|
|
225
300
|
vulns = self.mapping.get_value(kwargs.get("data", self.validater.data), "vulnerabilities", [])
|
|
226
301
|
if not vulns:
|
|
227
302
|
return findings
|
|
303
|
+
|
|
228
304
|
for dat in vulns:
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
self.mapping.get_value(self.validater.data, self.project_name)
|
|
232
|
-
)
|
|
233
|
-
description = self.mapping.get_value(dat, "description") or self.mapping.get_value(dat, self.vuln_title)
|
|
234
|
-
solution = self.mapping.get_value(dat, self.auto_fixable)
|
|
235
|
-
# if auto fixable is not available, check for upgradeable or patchable, this is for .json files
|
|
236
|
-
if not solution:
|
|
237
|
-
upgradeable = self.mapping.get_value(dat, "isUpgradeable", False)
|
|
238
|
-
patchable = self.mapping.get_value(dat, "isPatchable", False)
|
|
239
|
-
if upgradeable or patchable:
|
|
240
|
-
solution = "Upgrade or patch the vulnerable component."
|
|
241
|
-
cves = ", ".join(self.mapping.get_value(dat, "CVE", ""))
|
|
305
|
+
finding_data = self._extract_json_finding_data(dat)
|
|
306
|
+
cves = self.mapping.get_value(dat, "CVE", [])
|
|
242
307
|
if not cves:
|
|
243
|
-
cves =
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
308
|
+
cves = dat.get("identifiers", {}).get("CVE", [])
|
|
309
|
+
|
|
310
|
+
# Handle multiple CVEs or single CVE
|
|
311
|
+
if cves:
|
|
312
|
+
for cve in cves:
|
|
313
|
+
finding_data.update(
|
|
314
|
+
{
|
|
315
|
+
"dat": dat,
|
|
316
|
+
"cve": cve,
|
|
317
|
+
"title": dat.get("title") or finding_data["description"],
|
|
318
|
+
"plugin_id": dat.get("id"),
|
|
319
|
+
"cvss_score": dat.get("cvssScore"),
|
|
320
|
+
"vpr_score": None,
|
|
321
|
+
}
|
|
322
|
+
)
|
|
323
|
+
findings.append(self._create_finding(finding_data))
|
|
324
|
+
else:
|
|
325
|
+
finding_data.update(
|
|
326
|
+
{
|
|
327
|
+
"dat": dat,
|
|
328
|
+
"cve": "",
|
|
329
|
+
"title": dat.get("title") or finding_data["description"],
|
|
330
|
+
"plugin_id": dat.get("id"),
|
|
331
|
+
"cvss_score": dat.get("cvssScore"),
|
|
332
|
+
"vpr_score": None,
|
|
333
|
+
}
|
|
264
334
|
)
|
|
265
|
-
|
|
335
|
+
findings.append(self._create_finding(finding_data))
|
|
336
|
+
|
|
266
337
|
return findings
|
|
267
338
|
|
|
268
339
|
@staticmethod
|