regscale-cli 6.20.10.0__py3-none-any.whl → 6.21.1.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/__init__.py +1 -2
- regscale/integrations/commercial/amazon/common.py +79 -2
- regscale/integrations/commercial/aws/cli.py +183 -9
- regscale/integrations/commercial/aws/scanner.py +544 -9
- regscale/integrations/commercial/cpe.py +18 -1
- 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/commercial/tenablev2/jsonl_scanner.py +2 -1
- regscale/integrations/commercial/wizv2/async_client.py +10 -3
- regscale/integrations/commercial/wizv2/click.py +102 -26
- regscale/integrations/commercial/wizv2/constants.py +249 -1
- regscale/integrations/commercial/wizv2/issue.py +2 -2
- regscale/integrations/commercial/wizv2/parsers.py +3 -2
- regscale/integrations/commercial/wizv2/policy_compliance.py +1858 -0
- regscale/integrations/commercial/wizv2/scanner.py +15 -21
- regscale/integrations/commercial/wizv2/utils.py +258 -85
- regscale/integrations/commercial/wizv2/variables.py +4 -3
- regscale/integrations/compliance_integration.py +1455 -0
- regscale/integrations/integration_override.py +15 -6
- regscale/integrations/public/fedramp/fedramp_five.py +1 -1
- regscale/integrations/public/fedramp/markdown_parser.py +7 -1
- regscale/integrations/scanner_integration.py +193 -37
- regscale/models/app_models/__init__.py +1 -0
- 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 +117 -5
- 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/{integrations/commercial/wizv2/models.py → models/integration_models/wizv2.py} +4 -12
- regscale/models/integration_models/xray.py +276 -82
- regscale/models/regscale_models/control_implementation.py +14 -12
- regscale/models/regscale_models/file.py +4 -0
- regscale/models/regscale_models/issue.py +123 -0
- regscale/models/regscale_models/milestone.py +1 -1
- regscale/models/regscale_models/rbac.py +22 -0
- regscale/models/regscale_models/regscale_model.py +4 -2
- regscale/models/regscale_models/security_plan.py +1 -1
- regscale/utils/graphql_client.py +3 -1
- {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/METADATA +9 -9
- {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/RECORD +64 -60
- tests/fixtures/test_fixture.py +58 -2
- tests/regscale/core/test_app.py +5 -3
- tests/regscale/core/test_version_regscale.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/integrations/test_wiz_policy_compliance_affected_controls.py +154 -0
- tests/regscale/models/test_asset.py +406 -50
- {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/top_level.txt +0 -0
|
@@ -2,39 +2,45 @@
|
|
|
2
2
|
ECR Scan information
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from typing import Any, List, Optional, Sequence, Union
|
|
5
|
+
from typing import List, Optional, Union
|
|
8
6
|
|
|
9
7
|
from regscale.core.app.application import Application
|
|
10
8
|
from regscale.core.app.logz import create_logger
|
|
11
|
-
from regscale.
|
|
12
|
-
from regscale.exceptions import ValidationException
|
|
9
|
+
from regscale.integrations.scanner_integration import IntegrationAsset, IntegrationFinding
|
|
13
10
|
from regscale.models import ImportValidater
|
|
14
11
|
from regscale.models.integration_models.flat_file_importer import FlatFileImporter
|
|
12
|
+
from regscale.models.regscale_models import AssetStatus, IssueSeverity, IssueStatus
|
|
15
13
|
from regscale.models.regscale_models.asset import Asset
|
|
16
|
-
from regscale.models.regscale_models.vulnerability import Vulnerability
|
|
17
14
|
|
|
18
15
|
|
|
19
16
|
class ECR(FlatFileImporter):
|
|
20
17
|
"""ECR Scan information"""
|
|
21
18
|
|
|
22
19
|
def __init__(self, **kwargs):
|
|
23
|
-
|
|
24
|
-
self.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
20
|
+
# Group related attributes to reduce instance attribute count
|
|
21
|
+
self.scanner_config = {
|
|
22
|
+
"name": kwargs.get("name"),
|
|
23
|
+
"vuln_title": "name",
|
|
24
|
+
"fmt": "%m/%d/%y",
|
|
25
|
+
"dt_format": "%Y-%m-%d %H:%M:%S",
|
|
26
|
+
"image_name": "Name",
|
|
27
|
+
}
|
|
28
|
+
self.mapping_config = {
|
|
29
|
+
"mapping_file": kwargs.get("mappings_path"),
|
|
30
|
+
"disable_mapping": kwargs.get("disable_mapping"),
|
|
31
|
+
}
|
|
32
|
+
self.file_config = {
|
|
33
|
+
"file_type": kwargs.get("file_type"),
|
|
34
|
+
"raw_dict": {},
|
|
35
|
+
}
|
|
36
|
+
self.required_headers = [self.scanner_config["image_name"]]
|
|
37
|
+
keys = ["imageScanFindings", "findings"] if self.file_config["file_type"] == ".json" else None
|
|
36
38
|
self.validater = ImportValidater(
|
|
37
|
-
self.required_headers,
|
|
39
|
+
self.required_headers,
|
|
40
|
+
kwargs.get("file_path"),
|
|
41
|
+
self.mapping_config["mapping_file"],
|
|
42
|
+
self.mapping_config["disable_mapping"],
|
|
43
|
+
keys=keys,
|
|
38
44
|
)
|
|
39
45
|
self.headers = self.validater.parsed_headers
|
|
40
46
|
self.mapping = self.validater.mapping
|
|
@@ -48,92 +54,55 @@ class ECR(FlatFileImporter):
|
|
|
48
54
|
**kwargs,
|
|
49
55
|
)
|
|
50
56
|
|
|
51
|
-
def
|
|
52
|
-
self,
|
|
53
|
-
) -> tuple[Optional[Sequence[str]], Union[dict, list[Any]]]:
|
|
57
|
+
def create_asset(self, dat: Optional[dict] = None) -> IntegrationAsset:
|
|
54
58
|
"""
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
:raises ValidationException: If the headers in the csv/xlsx file do not match the expected headers
|
|
58
|
-
:return: Tuple of header and data from csv file
|
|
59
|
-
:rtype: tuple[Optional[Sequence[str]], Union[dict, list[Any]]]
|
|
60
|
-
"""
|
|
61
|
-
header: Optional[Sequence[str]] = []
|
|
62
|
-
data: dict = {}
|
|
63
|
-
with open(self.attributes.file_path, encoding="utf-8") as file:
|
|
64
|
-
if file.name.endswith(".csv"):
|
|
65
|
-
data, header = self.convert_csv_to_dict(file)
|
|
66
|
-
elif file.name.endswith(".json"):
|
|
67
|
-
try:
|
|
68
|
-
# Filter possible null values
|
|
69
|
-
self.raw_dict = json.load(file)
|
|
70
|
-
if not isinstance(self.raw_dict, dict):
|
|
71
|
-
raise ValidationException(
|
|
72
|
-
f"Invalid JSON file. Must be a dictionary, it is {type(self.raw_dict)}"
|
|
73
|
-
)
|
|
74
|
-
data = self.raw_dict.get("imageScanFindings", {}).get("findings", [])
|
|
75
|
-
except json.JSONDecodeError:
|
|
76
|
-
raise ValidationException("Invalid JSON file. Encountered a JSONDecodeError.")
|
|
77
|
-
else:
|
|
78
|
-
raise ValidationException(
|
|
79
|
-
f"Unsupported file type. Must be a .csv or .json file.\nProvided: {self.file_type}"
|
|
80
|
-
)
|
|
81
|
-
return header, data
|
|
82
|
-
|
|
83
|
-
def create_asset(self, dat: Optional[dict] = None) -> Asset:
|
|
84
|
-
"""
|
|
85
|
-
Create an asset from a row in the ECR file
|
|
59
|
+
Create an integration asset from a row in the ECR file
|
|
86
60
|
|
|
87
61
|
:param Optional[dict] dat: Data row from file, defaults to None
|
|
88
|
-
:return: RegScale
|
|
89
|
-
:rtype:
|
|
62
|
+
:return: RegScale IntegrationAsset object
|
|
63
|
+
:rtype: IntegrationAsset
|
|
90
64
|
"""
|
|
91
65
|
name = self.mapping.get_value(dat, "Name") or self.mapping.get_value(dat, "name")
|
|
92
|
-
if repository_name := self.mapping.get_value(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
)
|
|
66
|
+
if repository_name := self.mapping.get_value(
|
|
67
|
+
dat, "repositoryName", self.file_config["raw_dict"].get("repositoryName", "")
|
|
68
|
+
):
|
|
69
|
+
image_id_data = self.file_config["raw_dict"].get("imageId", {}).get("imageDigest", "").split(":")
|
|
70
|
+
if image_id_data and len(image_id_data) > 1:
|
|
96
71
|
image_id = image_id_data[1]
|
|
97
72
|
else:
|
|
98
73
|
image_id = image_id_data[0]
|
|
99
74
|
name = f"{repository_name}:{image_id}"
|
|
100
75
|
|
|
101
76
|
# Check if string has a forward slash
|
|
102
|
-
return
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
"bLatestScan": True,
|
|
114
|
-
"bAuthenticatedScan": True,
|
|
115
|
-
"scanningTool": self.name,
|
|
116
|
-
"assetOwnerId": self.config["userId"],
|
|
117
|
-
"assetType": "Other",
|
|
118
|
-
"fqdn": name if is_valid_fqdn(name) else None,
|
|
119
|
-
"systemAdministratorId": self.config["userId"],
|
|
120
|
-
"parentId": self.attributes.parent_id,
|
|
121
|
-
"parentModule": self.attributes.parent_module,
|
|
122
|
-
}
|
|
77
|
+
return IntegrationAsset(
|
|
78
|
+
identifier=name,
|
|
79
|
+
name=name,
|
|
80
|
+
ip_address="0.0.0.0",
|
|
81
|
+
cpu=0,
|
|
82
|
+
ram=0,
|
|
83
|
+
scanning_tool=self.scanner_config["name"],
|
|
84
|
+
status=AssetStatus.Active.value,
|
|
85
|
+
asset_type="Other",
|
|
86
|
+
asset_category="Software",
|
|
87
|
+
operating_system="Linux",
|
|
123
88
|
)
|
|
124
89
|
|
|
125
|
-
def create_vuln(
|
|
90
|
+
def create_vuln(
|
|
91
|
+
self, dat: Optional[dict] = None, **_kwargs
|
|
92
|
+
) -> Union[IntegrationFinding, List[IntegrationFinding], None]:
|
|
126
93
|
"""
|
|
127
|
-
Create a
|
|
94
|
+
Create a finding from a row in the ECR csv file
|
|
128
95
|
|
|
129
96
|
:param Optional[dict] dat: Data row from file, defaults to None
|
|
130
|
-
:return: RegScale
|
|
131
|
-
:rtype: Union[
|
|
97
|
+
:return: RegScale IntegrationFinding object, a list of RegScale IntegrationFinding objects or None
|
|
98
|
+
:rtype: Union[IntegrationFinding, List[IntegrationFinding], None]
|
|
132
99
|
"""
|
|
133
|
-
vulns: List[
|
|
100
|
+
vulns: List[IntegrationFinding] = []
|
|
134
101
|
hostname = dat.get("Name") or dat.get("name")
|
|
135
|
-
if repository_name := self.mapping.get_value(
|
|
136
|
-
|
|
102
|
+
if repository_name := self.mapping.get_value(
|
|
103
|
+
dat, "repositoryName", self.file_config["raw_dict"].get("repositoryName", "")
|
|
104
|
+
):
|
|
105
|
+
image_id_data = self.file_config["raw_dict"].get("imageId", {}).get("imageDigest", "").split(":")
|
|
137
106
|
if len(image_id_data) > 1:
|
|
138
107
|
image_id = image_id_data[1]
|
|
139
108
|
else:
|
|
@@ -147,89 +116,73 @@ class ECR(FlatFileImporter):
|
|
|
147
116
|
return single_vuln
|
|
148
117
|
return vulns
|
|
149
118
|
|
|
150
|
-
def
|
|
151
|
-
"""
|
|
152
|
-
Get the asset by hostname
|
|
153
|
-
|
|
154
|
-
:param str hostname: The hostname
|
|
155
|
-
:return: The asset if found, otherwise None
|
|
156
|
-
:rtype: Optional[Asset]
|
|
157
|
-
"""
|
|
158
|
-
asset_match = [asset for asset in self.data["assets"] if asset.name == hostname]
|
|
159
|
-
return asset_match[0] if asset_match else None
|
|
160
|
-
|
|
161
|
-
def create_vulnerability_object(
|
|
162
|
-
self, asset: Asset, hostname: str, cve: str, severity: str, description: str
|
|
163
|
-
) -> Vulnerability:
|
|
119
|
+
def create_finding(self, finding_data: dict) -> IntegrationFinding:
|
|
164
120
|
"""
|
|
165
|
-
Create
|
|
121
|
+
Create an IntegrationFinding from finding data
|
|
166
122
|
|
|
167
|
-
:param
|
|
168
|
-
:
|
|
169
|
-
:
|
|
170
|
-
:param str severity: The severity
|
|
171
|
-
:param str description: The description
|
|
172
|
-
:return: The vulnerability
|
|
173
|
-
:rtype: Vulnerability
|
|
123
|
+
:param dict finding_data: Dictionary containing finding data
|
|
124
|
+
:return: The IntegrationFinding
|
|
125
|
+
:rtype: IntegrationFinding
|
|
174
126
|
"""
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
tenantsId=0,
|
|
193
|
-
title=f"{cve} on asset {asset.name}",
|
|
194
|
-
description=cve,
|
|
195
|
-
plugInText=description,
|
|
196
|
-
createdById=config["userId"],
|
|
197
|
-
lastUpdatedById=config["userId"],
|
|
198
|
-
dateCreated=get_current_datetime(),
|
|
127
|
+
title = f"{finding_data['cve']} on asset {finding_data['hostname']}"
|
|
128
|
+
return IntegrationFinding(
|
|
129
|
+
title=title,
|
|
130
|
+
dns=finding_data["hostname"],
|
|
131
|
+
description=finding_data["cve"],
|
|
132
|
+
severity=self.determine_severity(finding_data["severity"]),
|
|
133
|
+
status=IssueStatus.Open.value,
|
|
134
|
+
plugin_name=finding_data["cve"],
|
|
135
|
+
plugin_id=finding_data["cve"],
|
|
136
|
+
plugin_text=finding_data["description"],
|
|
137
|
+
asset_identifier=finding_data["hostname"],
|
|
138
|
+
cve=finding_data["cve"],
|
|
139
|
+
first_seen=self.scan_date,
|
|
140
|
+
last_seen=self.scan_date,
|
|
141
|
+
scan_date=self.scan_date,
|
|
142
|
+
category="Software",
|
|
143
|
+
control_labels=[],
|
|
199
144
|
)
|
|
200
145
|
|
|
201
|
-
def process_csv_vulns(self, dat: dict, hostname: str) -> Optional[
|
|
146
|
+
def process_csv_vulns(self, dat: dict, hostname: str) -> Optional[IntegrationFinding]:
|
|
202
147
|
"""
|
|
203
148
|
Process the CSV findings from the ECR scan
|
|
204
149
|
|
|
205
150
|
:param dict dat: The data from the ECR scan
|
|
206
151
|
:param str hostname: The hostname
|
|
207
152
|
:return: The vulnerability or None
|
|
208
|
-
:rtype: Optional[
|
|
153
|
+
:rtype: Optional[IntegrationFinding]
|
|
209
154
|
|
|
210
155
|
"""
|
|
211
156
|
cve = dat.get("CVE", "")
|
|
212
157
|
severity = self.determine_severity(dat.get("Severity", "Info"))
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
158
|
+
finding_data = {
|
|
159
|
+
"hostname": hostname,
|
|
160
|
+
"cve": cve,
|
|
161
|
+
"severity": severity,
|
|
162
|
+
"description": dat.get("uri", ""),
|
|
163
|
+
}
|
|
164
|
+
return self.create_finding(finding_data)
|
|
165
|
+
|
|
166
|
+
def process_json_vulns(self, dat: dict, hostname: str) -> List[IntegrationFinding]:
|
|
218
167
|
"""
|
|
219
168
|
Process the JSON findings from the ECR scan
|
|
220
169
|
|
|
221
170
|
:param dict dat: The data from the ECR scan
|
|
222
171
|
:param str hostname: The hostname
|
|
223
172
|
:return: The list of vulnerabilities
|
|
224
|
-
:rtype: List[
|
|
173
|
+
:rtype: List[IntegrationFinding]
|
|
225
174
|
"""
|
|
226
|
-
vulns: List[
|
|
175
|
+
vulns: List[IntegrationFinding] = []
|
|
227
176
|
if findings := dat.get("imageScanFindings", {}).get("findings"):
|
|
228
177
|
for finding in findings:
|
|
229
178
|
cve = finding.get("name")
|
|
230
179
|
severity = self.determine_severity(finding["severity"])
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
180
|
+
finding_data = {
|
|
181
|
+
"hostname": hostname,
|
|
182
|
+
"cve": cve,
|
|
183
|
+
"severity": severity,
|
|
184
|
+
"description": finding.get("uri", ""),
|
|
185
|
+
}
|
|
186
|
+
vuln = self.create_finding(finding_data)
|
|
187
|
+
vulns.append(vuln)
|
|
235
188
|
return vulns
|
|
@@ -33,7 +33,7 @@ from regscale.core.app.utils.parser_utils import safe_datetime_str
|
|
|
33
33
|
from regscale.integrations.scanner_integration import ScannerIntegration
|
|
34
34
|
from regscale.models import IssueStatus, Metadata, regscale_models
|
|
35
35
|
from regscale.models.app_models.mapping import Mapping
|
|
36
|
-
from regscale.models.regscale_models import Asset, File, Vulnerability
|
|
36
|
+
from regscale.models.regscale_models import Asset, File, IssueSeverity, Vulnerability
|
|
37
37
|
|
|
38
38
|
logger = logging.getLogger(__name__)
|
|
39
39
|
|
|
@@ -41,6 +41,10 @@ DT_FORMAT = "%Y-%m-%d"
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
class FlatFileIntegration(ScannerIntegration):
|
|
44
|
+
"""
|
|
45
|
+
Flat File Integration
|
|
46
|
+
"""
|
|
47
|
+
|
|
44
48
|
title = "Flat File Integration"
|
|
45
49
|
# Required fields from ScannerIntegration
|
|
46
50
|
asset_identifier_field = "name"
|
|
@@ -53,13 +57,6 @@ class FlatFileIntegration(ScannerIntegration):
|
|
|
53
57
|
finding_severity_map: Optional[dict] = None,
|
|
54
58
|
**kwargs: Any,
|
|
55
59
|
):
|
|
56
|
-
"""
|
|
57
|
-
Initialize the FlatFileIntegration
|
|
58
|
-
|
|
59
|
-
:param int plan_id: The plan id
|
|
60
|
-
:param str asset_identifier_field: The asset identifier field to use, defaults to "name"
|
|
61
|
-
:param dict kwargs: Additional keyword arguments
|
|
62
|
-
"""
|
|
63
60
|
self.asset_identifier_field = asset_identifier_field
|
|
64
61
|
if finding_severity_map:
|
|
65
62
|
self.finding_severity_map = finding_severity_map
|
|
@@ -68,6 +65,7 @@ class FlatFileIntegration(ScannerIntegration):
|
|
|
68
65
|
"Critical": regscale_models.IssueSeverity.Critical,
|
|
69
66
|
"High": regscale_models.IssueSeverity.High,
|
|
70
67
|
"Medium": regscale_models.IssueSeverity.Moderate,
|
|
68
|
+
"Moderate": regscale_models.IssueSeverity.Moderate,
|
|
71
69
|
"Low": regscale_models.IssueSeverity.Low,
|
|
72
70
|
}
|
|
73
71
|
super().__init__(plan_id=plan_id, **kwargs)
|
|
@@ -126,20 +124,12 @@ class FlatFileImporter(ABC):
|
|
|
126
124
|
"Critical": regscale_models.IssueSeverity.Critical,
|
|
127
125
|
"High": regscale_models.IssueSeverity.High,
|
|
128
126
|
"Medium": regscale_models.IssueSeverity.Moderate,
|
|
127
|
+
"Moderate": regscale_models.IssueSeverity.Moderate,
|
|
129
128
|
"Low": regscale_models.IssueSeverity.Low,
|
|
130
129
|
}
|
|
131
130
|
|
|
132
|
-
|
|
133
|
-
if "parent_id" not in kwargs and "object_id" in kwargs:
|
|
134
|
-
kwargs["parent_id"] = kwargs["object_id"]
|
|
135
|
-
kwargs["plan_id"] = kwargs["object_id"]
|
|
136
|
-
if kwargs.get("is_component", False):
|
|
137
|
-
kwargs["parent_module"] = regscale_models.Component.get_module_string()
|
|
138
|
-
else:
|
|
139
|
-
kwargs["parent_module"] = regscale_models.SecurityPlan.get_module_string()
|
|
131
|
+
kwargs = self.update_kwargs(kwargs)
|
|
140
132
|
|
|
141
|
-
if "app" not in kwargs:
|
|
142
|
-
kwargs["app"] = Application()
|
|
143
133
|
# empty generator
|
|
144
134
|
self.integration_assets: Generator["IntegrationAsset", None, None] = (x for x in [])
|
|
145
135
|
self.integration_findings: Generator["IntegrationAsset", None, None] = (x for x in [])
|
|
@@ -209,7 +199,7 @@ class FlatFileImporter(ABC):
|
|
|
209
199
|
flat_int.num_assets_to_process = len(self.data["assets"])
|
|
210
200
|
if vuln_count:
|
|
211
201
|
flat_int.num_findings_to_process = vuln_count
|
|
212
|
-
elif isinstance(self.data["vulns"], list):
|
|
202
|
+
elif isinstance(self.data["vulns"], list) and not vuln_count:
|
|
213
203
|
flat_int.num_findings_to_process = len(self.data["vulns"])
|
|
214
204
|
flat_int.sync_assets(
|
|
215
205
|
plan_id=self.attributes.plan_id,
|
|
@@ -229,6 +219,31 @@ class FlatFileImporter(ABC):
|
|
|
229
219
|
)
|
|
230
220
|
self.clean_up()
|
|
231
221
|
|
|
222
|
+
def update_kwargs(self, kwargs: dict) -> dict:
|
|
223
|
+
"""
|
|
224
|
+
Update the kwargs with the default values
|
|
225
|
+
|
|
226
|
+
:param dict kwargs: The kwargs to update
|
|
227
|
+
:return: The updated kwargs
|
|
228
|
+
:rtype: dicta
|
|
229
|
+
"""
|
|
230
|
+
# Set the parent_id, parent_module, and plan_id if they are not provided
|
|
231
|
+
if "parent_id" not in kwargs and "object_id" in kwargs:
|
|
232
|
+
kwargs["parent_id"] = kwargs["object_id"]
|
|
233
|
+
kwargs["plan_id"] = kwargs["object_id"]
|
|
234
|
+
if kwargs.get("is_component", False):
|
|
235
|
+
kwargs["parent_module"] = regscale_models.Component.get_module_string()
|
|
236
|
+
else:
|
|
237
|
+
kwargs["parent_module"] = regscale_models.SecurityPlan.get_module_string()
|
|
238
|
+
|
|
239
|
+
# if plan id is still not set, set it to the object id
|
|
240
|
+
if "plan_id" not in kwargs or not kwargs["plan_id"]:
|
|
241
|
+
kwargs["plan_id"] = kwargs["object_id"]
|
|
242
|
+
|
|
243
|
+
if "app" not in kwargs:
|
|
244
|
+
kwargs["app"] = Application()
|
|
245
|
+
return kwargs
|
|
246
|
+
|
|
232
247
|
def parse_finding(self, vuln: Union[Vulnerability, "IntegrationFinding"]) -> Optional["IntegrationFinding"]:
|
|
233
248
|
"""
|
|
234
249
|
Parses a vulnerability object into an IntegrationFinding object
|
|
@@ -240,6 +255,8 @@ class FlatFileImporter(ABC):
|
|
|
240
255
|
from regscale.integrations.scanner_integration import IntegrationFinding
|
|
241
256
|
|
|
242
257
|
if isinstance(vuln, IntegrationFinding):
|
|
258
|
+
# We will store this in the properties subsystem of issue.
|
|
259
|
+
vuln.extra_data["source_file_path"] = self.attributes.file_path
|
|
243
260
|
return vuln
|
|
244
261
|
|
|
245
262
|
try:
|
|
@@ -543,21 +560,6 @@ class FlatFileImporter(ABC):
|
|
|
543
560
|
if asset not in self.data["assets"]:
|
|
544
561
|
self.data["assets"].append(asset)
|
|
545
562
|
|
|
546
|
-
def _check_vuln(self, vuln_to_check: Union[Vulnerability, "IntegrationFinding"]) -> None:
|
|
547
|
-
"""
|
|
548
|
-
Check if the vuln is in the data
|
|
549
|
-
|
|
550
|
-
:param Union[Vulnerability, IntegrationFinding] vuln_to_check: The vulnerability to check to prevent duplicates
|
|
551
|
-
:rtype: None
|
|
552
|
-
"""
|
|
553
|
-
from regscale.integrations.scanner_integration import IntegrationFinding
|
|
554
|
-
|
|
555
|
-
if isinstance(vuln_to_check, IntegrationFinding):
|
|
556
|
-
if vuln_to_check not in self.data["vulns"]:
|
|
557
|
-
self.data["vulns"].append(vuln_to_check)
|
|
558
|
-
elif (vuln_to_check and vuln_to_check not in self.data["vulns"]) and hasattr(vuln_to_check, "id"):
|
|
559
|
-
self.data["vulns"].append(vuln_to_check)
|
|
560
|
-
|
|
561
563
|
def create_vulns(self, func: Callable) -> None:
|
|
562
564
|
"""
|
|
563
565
|
Create vulns in RegScale from csv file
|
|
@@ -585,11 +587,12 @@ class FlatFileImporter(ABC):
|
|
|
585
587
|
if not vuln:
|
|
586
588
|
vuln_progress.advance(vuln_task, advance=1)
|
|
587
589
|
continue
|
|
588
|
-
|
|
589
|
-
|
|
590
|
+
|
|
591
|
+
if isinstance(vuln, IntegrationFinding):
|
|
592
|
+
self.data["vulns"].append(vuln)
|
|
590
593
|
if isinstance(vuln, list):
|
|
591
594
|
for v in vuln:
|
|
592
|
-
self.
|
|
595
|
+
self.data["vulns"].append(v)
|
|
593
596
|
if isinstance(vuln, Iterator):
|
|
594
597
|
self.integration_findings = vuln
|
|
595
598
|
self.data["vulns"] = vuln
|
|
@@ -936,10 +939,21 @@ class FlatFileImporter(ABC):
|
|
|
936
939
|
:return: The severity
|
|
937
940
|
:rtype: str
|
|
938
941
|
"""
|
|
942
|
+
mapping = {
|
|
943
|
+
"critical": IssueSeverity.Critical,
|
|
944
|
+
"high": IssueSeverity.High,
|
|
945
|
+
"medium": IssueSeverity.Moderate,
|
|
946
|
+
"moderate": IssueSeverity.Moderate,
|
|
947
|
+
"low": IssueSeverity.Low,
|
|
948
|
+
"informational": IssueSeverity.NotAssigned,
|
|
949
|
+
"none": IssueSeverity.NotAssigned,
|
|
950
|
+
"info": IssueSeverity.NotAssigned,
|
|
951
|
+
"unknown": IssueSeverity.NotAssigned,
|
|
952
|
+
}
|
|
939
953
|
severity = "info"
|
|
940
954
|
if s:
|
|
941
955
|
severity = s.lower()
|
|
942
|
-
return severity
|
|
956
|
+
return mapping.get(severity, IssueSeverity.NotAssigned)
|
|
943
957
|
|
|
944
958
|
@staticmethod
|
|
945
959
|
def map_status_to_issue_status(status: str) -> IssueStatus:
|
|
@@ -8,9 +8,10 @@ from urllib.parse import urlparse
|
|
|
8
8
|
from regscale.core.app.application import Application
|
|
9
9
|
from regscale.core.app.logz import create_logger
|
|
10
10
|
from regscale.core.app.utils.app_utils import epoch_to_datetime, get_current_datetime, is_valid_fqdn
|
|
11
|
-
from regscale.
|
|
11
|
+
from regscale.integrations.scanner_integration import IntegrationAsset, IntegrationFinding
|
|
12
|
+
from regscale.models import ImportValidater
|
|
12
13
|
from regscale.models.integration_models.flat_file_importer import FlatFileImporter
|
|
13
|
-
from regscale.models.regscale_models import Asset,
|
|
14
|
+
from regscale.models.regscale_models import Asset, IssueSeverity, IssueStatus
|
|
14
15
|
|
|
15
16
|
ISSUE_TYPE = "Issue Type"
|
|
16
17
|
VULNERABILITY_TITLE = ISSUE_TYPE
|
|
@@ -53,74 +54,55 @@ class AppScan(FlatFileImporter):
|
|
|
53
54
|
**kwargs,
|
|
54
55
|
)
|
|
55
56
|
|
|
56
|
-
def create_asset(self, dat: Optional[dict] = None) ->
|
|
57
|
+
def create_asset(self, dat: Optional[dict] = None) -> IntegrationAsset:
|
|
57
58
|
"""
|
|
58
59
|
Create an asset from a row in the IBM csv file
|
|
59
60
|
|
|
60
61
|
:param Optional[dict] dat: Data row from CSV file, defaults to None
|
|
61
|
-
:return: RegScale
|
|
62
|
-
:rtype:
|
|
62
|
+
:return: RegScale IntegrationAsset object
|
|
63
|
+
:rtype: IntegrationAsset
|
|
63
64
|
"""
|
|
64
65
|
parsed_url = urlparse(self.mapping.get_value(dat, "URL"))
|
|
65
66
|
hostname: str = f"{parsed_url.scheme}://{parsed_url.netloc}"
|
|
66
|
-
return
|
|
67
|
+
return IntegrationAsset(
|
|
67
68
|
**{
|
|
68
|
-
"id": 0,
|
|
69
69
|
"name": hostname,
|
|
70
|
-
"
|
|
70
|
+
"identifier": hostname,
|
|
71
71
|
"status": "Active (On Network)",
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"scanningTool": self.name,
|
|
76
|
-
"assetOwnerId": self.config["userId"],
|
|
77
|
-
"assetType": "Other",
|
|
72
|
+
"asset_category": "Software",
|
|
73
|
+
"scanning_tool": self.name,
|
|
74
|
+
"asset_type": "Other",
|
|
78
75
|
"fqdn": hostname if is_valid_fqdn(hostname) else None,
|
|
79
|
-
"systemAdministratorId": self.config["userId"],
|
|
80
|
-
"parentId": self.attributes.parent_id,
|
|
81
|
-
"parentModule": self.attributes.parent_module,
|
|
82
76
|
}
|
|
83
77
|
)
|
|
84
78
|
|
|
85
|
-
def create_vuln(self, dat: Optional[dict] = None, **kwargs) -> Optional[
|
|
79
|
+
def create_vuln(self, dat: Optional[dict] = None, **kwargs) -> Optional[IntegrationFinding]:
|
|
86
80
|
"""
|
|
87
|
-
Create
|
|
88
|
-
|
|
81
|
+
Create an IntegrationFinding from a row in the IBM csv file
|
|
89
82
|
:param Optional[dict] dat: Data row from CSV file, defaults to None
|
|
90
|
-
:return: RegScale
|
|
91
|
-
:rtype: Optional[
|
|
83
|
+
:return: RegScale IntegrationFinding object or None
|
|
84
|
+
:rtype: Optional[IntegrationFinding]
|
|
92
85
|
"""
|
|
86
|
+
|
|
93
87
|
regscale_vuln = None
|
|
94
88
|
parsed_url = urlparse(self.mapping.get_value(dat, "URL"))
|
|
95
89
|
hostname: str = f"{parsed_url.scheme}://{parsed_url.netloc}"
|
|
96
90
|
description: str = self.mapping.get_value(dat, ISSUE_TYPE)
|
|
97
91
|
app_scan_severity = self.mapping.get_value(dat, "Severity")
|
|
98
92
|
severity = self.severity_map.get(app_scan_severity, "Informational")
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if dat and asset_match:
|
|
103
|
-
regscale_vuln = Vulnerability(
|
|
104
|
-
id=0,
|
|
105
|
-
scanId=0, # set later
|
|
106
|
-
parentId=asset.id,
|
|
107
|
-
parentModule="assets",
|
|
108
|
-
ipAddress="0.0.0.0", # No ip address available
|
|
109
|
-
lastSeen=get_current_datetime(),
|
|
110
|
-
firstSeen=epoch_to_datetime(self.create_epoch),
|
|
111
|
-
daysOpen=None,
|
|
112
|
-
dns=hostname,
|
|
113
|
-
mitigated=None,
|
|
114
|
-
severity=severity,
|
|
115
|
-
plugInName=description,
|
|
116
|
-
cve="",
|
|
117
|
-
vprScore=None,
|
|
118
|
-
tenantsId=0,
|
|
119
|
-
title=description[:255] if description else "No Title",
|
|
93
|
+
if dat:
|
|
94
|
+
return IntegrationFinding(
|
|
95
|
+
title=self.mapping.get_value(dat, self.vuln_title),
|
|
120
96
|
description=description,
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
97
|
+
cve="",
|
|
98
|
+
severity=self.determine_severity(severity),
|
|
99
|
+
asset_identifier=hostname,
|
|
100
|
+
plugin_name=description,
|
|
101
|
+
plugin_text=description[:255],
|
|
102
|
+
control_labels=[],
|
|
103
|
+
category="Hardware",
|
|
104
|
+
status=IssueStatus.Open,
|
|
105
|
+
last_seen=get_current_datetime(),
|
|
106
|
+
first_seen=epoch_to_datetime(self.create_epoch),
|
|
125
107
|
)
|
|
126
108
|
return regscale_vuln
|