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
|
@@ -10,11 +10,10 @@ from typing import Any, List, Optional
|
|
|
10
10
|
from regscale.core.app.application import Application
|
|
11
11
|
from regscale.core.app.logz import create_logger
|
|
12
12
|
from regscale.core.app.utils.app_utils import epoch_to_datetime, get_current_datetime, is_valid_fqdn
|
|
13
|
+
from regscale.integrations.scanner_integration import IntegrationAsset, IntegrationFinding
|
|
13
14
|
from regscale.models import ImportValidater
|
|
14
15
|
from regscale.models.integration_models.flat_file_importer import FlatFileImporter
|
|
15
|
-
from regscale.models.regscale_models import SoftwareInventory
|
|
16
|
-
from regscale.models.regscale_models.asset import Asset
|
|
17
|
-
from regscale.models.regscale_models.vulnerability import Vulnerability
|
|
16
|
+
from regscale.models.regscale_models import Asset, IssueSeverity, IssueStatus, SoftwareInventory, Vulnerability
|
|
18
17
|
|
|
19
18
|
FIX_STATUS = "Fix Status"
|
|
20
19
|
VULNERABILITY_ID = "Vulnerability ID"
|
|
@@ -38,7 +37,7 @@ class Prisma(FlatFileImporter):
|
|
|
38
37
|
|
|
39
38
|
def __init__(self, **kwargs):
|
|
40
39
|
self.name = kwargs.get("name")
|
|
41
|
-
regscale_ssp_id = kwargs.get("
|
|
40
|
+
regscale_ssp_id = kwargs.get("object_id")
|
|
42
41
|
self.image_name = "Id"
|
|
43
42
|
self.vuln_title = CVE_ID
|
|
44
43
|
self.cvss3_score = "CVSS"
|
|
@@ -63,94 +62,75 @@ class Prisma(FlatFileImporter):
|
|
|
63
62
|
ignore_validation=True,
|
|
64
63
|
**kwargs,
|
|
65
64
|
)
|
|
66
|
-
self.create_software_inventory()
|
|
65
|
+
# self.create_software_inventory()
|
|
67
66
|
|
|
68
|
-
def create_asset(self, dat: Optional[dict] = None) ->
|
|
67
|
+
def create_asset(self, dat: Optional[dict] = None) -> IntegrationAsset:
|
|
69
68
|
"""
|
|
70
69
|
Create an asset from a row in the Prisma csv file
|
|
71
70
|
|
|
72
71
|
:param Optional[dict] dat: Data row from CSV file, defaults to None
|
|
73
|
-
:return: RegScale
|
|
74
|
-
:rtype:
|
|
72
|
+
:return: RegScale IntegrationAsset object
|
|
73
|
+
:rtype: IntegrationAsset
|
|
75
74
|
"""
|
|
76
75
|
hostname = self.mapping.get_value(dat, "Hostname")
|
|
77
76
|
distro = self.mapping.get_value(dat, "Distro")
|
|
78
77
|
|
|
79
|
-
return
|
|
78
|
+
return IntegrationAsset(
|
|
80
79
|
**{
|
|
81
|
-
"id": 0,
|
|
82
80
|
"name": hostname,
|
|
83
|
-
"
|
|
84
|
-
"
|
|
81
|
+
"ip_address": self.mapping.get_value(dat, "IP Address"),
|
|
82
|
+
"identifier": hostname,
|
|
83
|
+
"other_tracking_number": hostname,
|
|
85
84
|
"status": "Active (On Network)",
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"scanningTool": self.name,
|
|
90
|
-
"assetOwnerId": self.config["userId"],
|
|
91
|
-
"assetType": "Other",
|
|
85
|
+
"asset_category": "Hardware",
|
|
86
|
+
"asset_type": "Other",
|
|
87
|
+
"scanning_tool": self.name,
|
|
92
88
|
"fqdn": hostname if is_valid_fqdn(hostname) else None,
|
|
93
|
-
"
|
|
94
|
-
"systemAdministratorId": self.config["userId"],
|
|
95
|
-
"parentId": self.attributes.parent_id,
|
|
96
|
-
"parentModule": self.attributes.parent_module,
|
|
89
|
+
"operating_system": Asset.find_os(distro),
|
|
97
90
|
}
|
|
98
91
|
)
|
|
99
92
|
|
|
100
|
-
def create_vuln(self, dat: Optional[dict] = None, **kwargs) -> Optional[
|
|
93
|
+
def create_vuln(self, dat: Optional[dict] = None, **kwargs) -> Optional[IntegrationFinding]:
|
|
101
94
|
"""
|
|
102
|
-
Create a
|
|
95
|
+
Create a IntegrationFinding from a row in the Prisma csv file
|
|
103
96
|
|
|
104
97
|
:param Optional[dict] dat: Data row from CSV file, defaults to None
|
|
105
|
-
:return: RegScale
|
|
106
|
-
:rtype: Optional[
|
|
98
|
+
:return: RegScale IntegrationFinding object or None
|
|
99
|
+
:rtype: Optional[IntegrationFinding]
|
|
107
100
|
"""
|
|
108
101
|
cvss3_score = self.mapping.get_value(dat, self.cvss3_score)
|
|
109
102
|
hostname: str = self.mapping.get_value(dat, "Hostname")
|
|
110
|
-
distro: str = self.mapping.get_value(dat, "Distro")
|
|
111
103
|
cve: str = self.mapping.get_value(dat, CVE_ID)
|
|
112
104
|
description: str = self.mapping.get_value(dat, "Description")
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
lastSeen=epoch_to_datetime(self.create_epoch),
|
|
127
|
-
firstSeen=epoch_to_datetime(self.create_epoch),
|
|
128
|
-
daysOpen=None,
|
|
129
|
-
dns=hostname,
|
|
130
|
-
mitigated=None,
|
|
131
|
-
operatingSystem=(Asset.find_os(distro) if Asset.find_os(distro) else None),
|
|
105
|
+
regscale_finding: Optional[IntegrationFinding] = None
|
|
106
|
+
severity = (
|
|
107
|
+
self.determine_severity(Vulnerability.determine_cvss3_severity_text(float(cvss3_score)))
|
|
108
|
+
if cvss3_score
|
|
109
|
+
else IssueSeverity.NotAssigned
|
|
110
|
+
)
|
|
111
|
+
seen = epoch_to_datetime(self.create_epoch)
|
|
112
|
+
if dat:
|
|
113
|
+
return IntegrationFinding(
|
|
114
|
+
control_labels=[], # Add an empty list for control_labels
|
|
115
|
+
title=self.mapping.get_value(dat, self.vuln_title),
|
|
116
|
+
description=description,
|
|
117
|
+
cve=cve.upper(),
|
|
132
118
|
severity=severity,
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
),
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
lastUpdatedById=config["userId"],
|
|
147
|
-
dateCreated=get_current_datetime(),
|
|
148
|
-
extra_data={
|
|
149
|
-
"solution": self.mapping.get_value(dat, FIX_STATUS),
|
|
150
|
-
"proof": self.mapping.get_value(dat, FIX_STATUS),
|
|
151
|
-
},
|
|
119
|
+
asset_identifier=hostname,
|
|
120
|
+
plugin_name=self.mapping.get_value(dat, self.vuln_title),
|
|
121
|
+
plugin_id=self.mapping.get_value(dat, VULNERABILITY_ID),
|
|
122
|
+
cvss_v3_score=cvss3_score or 0.0,
|
|
123
|
+
plugin_text=description[:255],
|
|
124
|
+
remediation=self.mapping.get_value(dat, "Solution"),
|
|
125
|
+
category="Hardware",
|
|
126
|
+
status=IssueStatus.Open,
|
|
127
|
+
first_seen=seen,
|
|
128
|
+
scan_date=seen,
|
|
129
|
+
vulnerability_type="Vulnerability Scan",
|
|
130
|
+
baseline=f"{self.name} Host",
|
|
131
|
+
recommendation_for_mitigation=self.mapping.get_value(dat, FIX_STATUS),
|
|
152
132
|
)
|
|
153
|
-
return
|
|
133
|
+
return regscale_finding
|
|
154
134
|
|
|
155
135
|
def create_software_inventory(self) -> List[SoftwareInventory]:
|
|
156
136
|
"""
|
|
@@ -9,7 +9,7 @@ import logging
|
|
|
9
9
|
import re
|
|
10
10
|
from calendar import firstweekday
|
|
11
11
|
from datetime import datetime
|
|
12
|
-
from typing import Any, Iterator,
|
|
12
|
+
from typing import Any, Iterator, List, Optional, TextIO, TypeVar, Union
|
|
13
13
|
|
|
14
14
|
from openpyxl.reader.excel import load_workbook
|
|
15
15
|
from pandas import Timestamp
|
|
@@ -17,20 +17,21 @@ from pandas import Timestamp
|
|
|
17
17
|
from regscale.core.app import create_logger
|
|
18
18
|
from regscale.core.app.application import Application
|
|
19
19
|
from regscale.core.app.utils.app_utils import get_current_datetime
|
|
20
|
-
from regscale.core.app.utils.parser_utils import
|
|
20
|
+
from regscale.core.app.utils.parser_utils import safe_float, safe_int
|
|
21
21
|
from regscale.core.utils.date import date_str, datetime_str
|
|
22
|
-
from regscale.integrations.scanner_integration import IntegrationAsset
|
|
22
|
+
from regscale.integrations.scanner_integration import IntegrationAsset, IntegrationFinding
|
|
23
23
|
from regscale.models import (
|
|
24
24
|
Asset,
|
|
25
25
|
AssetCategory,
|
|
26
|
+
AssetStatus,
|
|
26
27
|
AssetType,
|
|
27
28
|
ImportValidater,
|
|
28
29
|
IssueSeverity,
|
|
30
|
+
IssueStatus,
|
|
29
31
|
SecurityPlan,
|
|
30
32
|
Vulnerability,
|
|
31
|
-
VulnerabilityStatus,
|
|
32
|
-
AssetStatus,
|
|
33
33
|
VulnerabilitySeverity,
|
|
34
|
+
VulnerabilityStatus,
|
|
34
35
|
)
|
|
35
36
|
from regscale.models.integration_models.flat_file_importer import FlatFileImporter
|
|
36
37
|
|
|
@@ -50,6 +51,18 @@ FQDN = "FQDN"
|
|
|
50
51
|
IMAGE_ID_FIELD = "IMAGE ID"
|
|
51
52
|
CVE_ID_FIELD = "CVE ID"
|
|
52
53
|
|
|
54
|
+
SEVERITY_MAP = {
|
|
55
|
+
"critical": IssueSeverity.Critical,
|
|
56
|
+
"high": IssueSeverity.High,
|
|
57
|
+
"medium": IssueSeverity.Moderate,
|
|
58
|
+
"moderate": IssueSeverity.Moderate,
|
|
59
|
+
"low": IssueSeverity.Low,
|
|
60
|
+
"informational": IssueSeverity.NotAssigned,
|
|
61
|
+
"none": IssueSeverity.NotAssigned,
|
|
62
|
+
"info": IssueSeverity.NotAssigned,
|
|
63
|
+
"unknown": IssueSeverity.NotAssigned,
|
|
64
|
+
}
|
|
65
|
+
|
|
53
66
|
|
|
54
67
|
class Qualys(FlatFileImporter):
|
|
55
68
|
"""Qualys Scan information"""
|
|
@@ -94,36 +107,30 @@ class Qualys(FlatFileImporter):
|
|
|
94
107
|
# header is line# 11
|
|
95
108
|
# start self.file_data from line #12
|
|
96
109
|
|
|
97
|
-
def create_asset(self, dat: Optional[dict] = None) -> Optional[
|
|
110
|
+
def create_asset(self, dat: Optional[dict] = None) -> Optional[IntegrationAsset]:
|
|
98
111
|
"""
|
|
99
|
-
Create an asset from a row in the Qualys file
|
|
112
|
+
Create an integration asset from a row in the Qualys file
|
|
100
113
|
|
|
101
114
|
:param Optional[dict] dat: Data row from CSV file
|
|
102
|
-
:return: RegScale
|
|
103
|
-
:rtype: Optional[
|
|
115
|
+
:return: RegScale IntegrationAsset object or None
|
|
116
|
+
:rtype: Optional[IntegrationAsset]
|
|
104
117
|
"""
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
"operatingSystem": Asset.find_os(self.mapping.get_value(dat, OS)),
|
|
122
|
-
"operatingSystemVersion": self.mapping.get_value(dat, OS),
|
|
123
|
-
"systemAdministratorId": self.attributes.app.config["userId"],
|
|
124
|
-
"parentId": self.attributes.parent_id,
|
|
125
|
-
"parentModule": self.attributes.parent_module,
|
|
126
|
-
}
|
|
118
|
+
qid = str(self.mapping.get_value(dat, QG_HOST_ID))
|
|
119
|
+
return IntegrationAsset(
|
|
120
|
+
name=self.mapping.get_value(dat, DNS),
|
|
121
|
+
ip_address=self.mapping.get_value(dat, IP),
|
|
122
|
+
status=AssetStatus.Active.value,
|
|
123
|
+
cpu=0,
|
|
124
|
+
ram=0,
|
|
125
|
+
asset_category="Hardware",
|
|
126
|
+
identifier=qid, # UUID from Qualys Host ID
|
|
127
|
+
other_tracking_number=qid,
|
|
128
|
+
scanning_tool="Qualys",
|
|
129
|
+
asset_owner_id=self.attributes.app.config["userId"],
|
|
130
|
+
asset_type="Other",
|
|
131
|
+
fqdn=self.mapping.get_value(dat, FQDN),
|
|
132
|
+
operating_system=Asset.find_os(self.mapping.get_value(dat, OS)),
|
|
133
|
+
os_version=self.mapping.get_value(dat, OS),
|
|
127
134
|
)
|
|
128
135
|
|
|
129
136
|
def _convert_datetime_to_str(self, input_date: Union[Timestamp, datetime]) -> str:
|
|
@@ -140,59 +147,43 @@ class Qualys(FlatFileImporter):
|
|
|
140
147
|
return input_date
|
|
141
148
|
return input_date.strftime(self.dt_format)
|
|
142
149
|
|
|
143
|
-
def create_vuln(self, dat: Optional[dict] = None, **kwargs) -> Optional[
|
|
150
|
+
def create_vuln(self, dat: Optional[dict] = None, **kwargs) -> Optional[IntegrationFinding]:
|
|
144
151
|
"""
|
|
145
|
-
Create a
|
|
152
|
+
Create a finding from a row in the Qualys file
|
|
146
153
|
|
|
147
154
|
:param Optional[dict] dat: Data row from CSV file, defaults to None
|
|
148
|
-
:
|
|
149
|
-
:rtype: Optional[Vulnerability]
|
|
155
|
+
:rtype: IntegrationFinding
|
|
150
156
|
"""
|
|
151
157
|
from regscale.integrations.commercial.qualys import map_qualys_severity_to_regscale
|
|
152
158
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
distro: str = self.mapping.get_value(dat, OS)
|
|
159
|
+
finding: Optional[IntegrationFinding] = None
|
|
160
|
+
qid = str(self.mapping.get_value(dat, QG_HOST_ID))
|
|
156
161
|
cve: str = self.mapping.get_value(dat, CVE_ID)
|
|
157
162
|
description: str = self.mapping.get_value(dat, "Threat")
|
|
158
163
|
title = self.mapping.get_value(dat, self.vuln_title)
|
|
159
|
-
regscale_vuln = None
|
|
160
164
|
severity = self.mapping.get_value(dat, SEVERITY)
|
|
161
165
|
regscale_severity = map_qualys_severity_to_regscale(int(severity))[1]
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
last_seen = self._convert_datetime_to_str(self.mapping.get_value(dat, "Last Detected"))
|
|
166
|
-
first_seen = self._convert_datetime_to_str(self.mapping.get_value(dat, "First Detected"))
|
|
167
|
-
|
|
168
|
-
if dat and asset_match:
|
|
169
|
-
regscale_vuln = Vulnerability(
|
|
170
|
-
id=0,
|
|
171
|
-
scanId=0, # set later
|
|
172
|
-
parentId=asset.id,
|
|
173
|
-
parentModule="assets",
|
|
174
|
-
ipAddress=self.mapping.get_value(dat, IP),
|
|
175
|
-
lastSeen=last_seen,
|
|
176
|
-
firstSeen=first_seen,
|
|
177
|
-
daysOpen=None,
|
|
178
|
-
dns=self.mapping.get_value(dat, DNS, other_id),
|
|
179
|
-
mitigated=None,
|
|
180
|
-
operatingSystem=(Asset.find_os(distro) if Asset.find_os(distro) else None),
|
|
181
|
-
severity=regscale_severity,
|
|
182
|
-
plugInName=self.mapping.get_value(dat, self.vuln_title),
|
|
183
|
-
plugInId=self.mapping.get_value(dat, "QID"),
|
|
184
|
-
cve=cve,
|
|
185
|
-
vprScore=None,
|
|
186
|
-
cvsSv3BaseScore=self.extract_float(self.mapping.get_value(dat, "CVSS3.1 Base", 0.0)),
|
|
187
|
-
tenantsId=0,
|
|
166
|
+
if dat:
|
|
167
|
+
finding = IntegrationFinding(
|
|
168
|
+
control_labels=[], # Add an empty list for control_labels
|
|
188
169
|
title=title,
|
|
189
170
|
description=description,
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
171
|
+
ip_address="0.0.0.0",
|
|
172
|
+
cve=cve,
|
|
173
|
+
severity=regscale_severity,
|
|
174
|
+
asset_identifier=qid,
|
|
175
|
+
plugin_name=description,
|
|
176
|
+
plugin_id=str(self.mapping.get_value(dat, "QID")),
|
|
177
|
+
cvss_v3_score=self.extract_float(self.mapping.get_value(dat, "CVSS3.1 Base", 0.0)),
|
|
178
|
+
plugin_text=title,
|
|
179
|
+
category="Hardware",
|
|
180
|
+
status=IssueStatus.Open,
|
|
181
|
+
first_seen=self._convert_datetime_to_str(self.mapping.get_value(dat, "First Detected")),
|
|
182
|
+
last_seen=self._convert_datetime_to_str(self.mapping.get_value(dat, "Last Detected")),
|
|
183
|
+
vulnerability_type="Vulnerability Scan",
|
|
184
|
+
baseline=f"{self.name} Host",
|
|
194
185
|
)
|
|
195
|
-
return
|
|
186
|
+
return finding
|
|
196
187
|
|
|
197
188
|
@staticmethod
|
|
198
189
|
def extract_float(s: Union[str, float, int]) -> Optional[float]:
|
|
@@ -230,6 +221,7 @@ class QualysContainerScansImporter(FlatFileImporter): # (ScannerIntegration):
|
|
|
230
221
|
last_seen_field = "UPDATED"
|
|
231
222
|
container_id = "IMAGE UUID"
|
|
232
223
|
records = []
|
|
224
|
+
title = "Qualys Container Scanner Export Integration" # Add explicit title for source report identification
|
|
233
225
|
|
|
234
226
|
def __init__(self, **kwargs: dict):
|
|
235
227
|
self.asset_identifier_field = "otherTrackingNumber"
|
|
@@ -278,26 +270,25 @@ class QualysContainerScansImporter(FlatFileImporter): # (ScannerIntegration):
|
|
|
278
270
|
**kwargs,
|
|
279
271
|
)
|
|
280
272
|
|
|
281
|
-
def create_asset(self, row: Optional[dict] = None, **kwargs) -> Optional[
|
|
273
|
+
def create_asset(self, row: Optional[dict] = None, **kwargs) -> Optional[IntegrationAsset]:
|
|
282
274
|
"""
|
|
283
275
|
Fetch assets from the Qualys CSV file
|
|
284
276
|
|
|
285
|
-
:return:
|
|
286
|
-
:rtype:
|
|
277
|
+
:return: IntegrationAsset object
|
|
278
|
+
:rtype: Optional[IntegrationAsset]
|
|
287
279
|
"""
|
|
288
280
|
bad_ids = ["", "0", "None", "Unknown"]
|
|
289
281
|
if self.mapping.get_value(row, self.container_id) in bad_ids:
|
|
290
282
|
return None
|
|
291
283
|
max_length = 450 # max length of asset name
|
|
292
|
-
|
|
284
|
+
container_id = self.mapping.get_value(row, self.container_id)
|
|
285
|
+
asset = IntegrationAsset(
|
|
293
286
|
name=self.mapping.get_value(row, self.asset_notes_field)[:max_length] or "Unknown",
|
|
294
287
|
notes=self.mapping.get_value(row, self.asset_notes_field),
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
parentId=safe_int(self.plan_id),
|
|
300
|
-
parentModule=SecurityPlan.get_module_slug(),
|
|
288
|
+
identifier=container_id,
|
|
289
|
+
other_tracking_number=container_id,
|
|
290
|
+
asset_type=AssetType.VM.value,
|
|
291
|
+
asset_category=AssetCategory.Software.value,
|
|
301
292
|
status=AssetStatus.Active,
|
|
302
293
|
)
|
|
303
294
|
return asset
|
|
@@ -316,34 +307,44 @@ class QualysContainerScansImporter(FlatFileImporter): # (ScannerIntegration):
|
|
|
316
307
|
)
|
|
317
308
|
return date_str(date_obj_value, self.dt_format)
|
|
318
309
|
|
|
319
|
-
def create_vuln(self, row: Optional[dict] = None, **kwargs) ->
|
|
310
|
+
def create_vuln(self, row: Optional[dict] = None, **kwargs) -> Iterator[IntegrationFinding]:
|
|
320
311
|
"""
|
|
321
312
|
Fetch vulnerabilities from the Qualys CSV file
|
|
322
313
|
|
|
323
314
|
:return: Iterator of IntegrationFinding objects
|
|
324
|
-
:rtype:
|
|
315
|
+
:rtype: Iterator[IntegrationFinding]
|
|
325
316
|
"""
|
|
326
|
-
|
|
317
|
+
findings: List[IntegrationFinding] = []
|
|
318
|
+
title = self.mapping.get_value(row, self.title_field)
|
|
319
|
+
description = self.mapping.get_value(row, self.threat_field)
|
|
320
|
+
qid = str(self.mapping.get_value(row, self.container_id))
|
|
327
321
|
if self.mapping.get_value(row, CVE_ID_FIELD) and isinstance(self.mapping.get_value(row, CVE_ID_FIELD), str):
|
|
328
322
|
for cve_id in self.mapping.get_value(row, CVE_ID_FIELD, "").split(","):
|
|
329
|
-
finding =
|
|
323
|
+
finding = IntegrationFinding(
|
|
324
|
+
control_labels=[], # Add an empty list for control_labels
|
|
330
325
|
title=self.mapping.get_value(row, self.title_field),
|
|
331
326
|
description=self.mapping.get_value(row, self.threat_field),
|
|
332
327
|
cve=cve_id,
|
|
333
328
|
severity=severity_to_regscale(self.mapping.get_value(row, self.severity_field)),
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
329
|
+
asset_identifier=qid,
|
|
330
|
+
plugin_name=description,
|
|
331
|
+
plugin_id=str(self.mapping.get_value(dat, "QID")),
|
|
332
|
+
cvss_v3_score=safe_float(self.mapping.get_value(row, self.cve_v3_base, 0.0)),
|
|
333
|
+
vpr_score=safe_float(self.mapping.get_value(row, self.cve_base, 0.0)),
|
|
334
|
+
plugin_text=title,
|
|
335
|
+
category="Hardware",
|
|
336
|
+
status=IssueStatus.Open,
|
|
337
|
+
first_seen=self.handle_integration_date(
|
|
338
338
|
self.mapping.get_value(row, self.first_seen_field, get_current_datetime())
|
|
339
339
|
),
|
|
340
|
-
|
|
340
|
+
last_seen=self.handle_integration_date(
|
|
341
341
|
self.mapping.get_value(row, self.last_seen_field, get_current_datetime())
|
|
342
342
|
),
|
|
343
|
-
|
|
344
|
-
|
|
343
|
+
vulnerability_type="Vulnerability Scan",
|
|
344
|
+
baseline=f"{self.name} Host",
|
|
345
345
|
)
|
|
346
|
-
|
|
346
|
+
findings.append(finding)
|
|
347
|
+
yield from findings
|
|
347
348
|
|
|
348
349
|
|
|
349
350
|
class QualysWasScansImporter(FlatFileImporter):
|
|
@@ -365,6 +366,7 @@ class QualysWasScansImporter(FlatFileImporter):
|
|
|
365
366
|
container_id = "ID"
|
|
366
367
|
row_key = "VULNERABILITY"
|
|
367
368
|
records = []
|
|
369
|
+
title = "Qualys WAS Scanner Export Integration" # Add explicit title for source report identification
|
|
368
370
|
|
|
369
371
|
def __init__(self, **kwargs: dict):
|
|
370
372
|
self.asset_identifier_field = "otherTrackingNumber"
|
|
@@ -561,6 +563,8 @@ class QualysWasScansImporter(FlatFileImporter):
|
|
|
561
563
|
:rtype: Vulnerability
|
|
562
564
|
"""
|
|
563
565
|
# additional_fields = ["Title", "Severity Level", "CVSS Base", "CWE", "Solution"]
|
|
566
|
+
asset_identifier = self.mapping.get_value(row, self.container_id)
|
|
567
|
+
|
|
564
568
|
finding = Vulnerability(
|
|
565
569
|
title=row.get("Title", self.mapping.get_value(row, "Url")),
|
|
566
570
|
description=row.get("Solution"),
|
|
@@ -576,7 +580,8 @@ class QualysWasScansImporter(FlatFileImporter):
|
|
|
576
580
|
self.mapping.get_value(row, self.last_seen_field, get_current_datetime())
|
|
577
581
|
),
|
|
578
582
|
plugInName=row.get("CWE"),
|
|
579
|
-
dns=
|
|
583
|
+
dns=asset_identifier, # Use consistent asset identifier
|
|
584
|
+
assetIdentifier=asset_identifier, # Add explicit asset identifier
|
|
580
585
|
)
|
|
581
586
|
return finding
|
|
582
587
|
|
|
@@ -588,6 +593,7 @@ class QualysPolicyScansImporter(FlatFileImporter):
|
|
|
588
593
|
|
|
589
594
|
plan_id = 0
|
|
590
595
|
records = []
|
|
596
|
+
title = "Qualys Policy Scanner Export Integration" # Add explicit title for source report identification
|
|
591
597
|
|
|
592
598
|
def __init__(self, **kwargs: dict):
|
|
593
599
|
self.asset_identifier_field = "qualysId"
|