regscale-cli 6.20.9.1__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.

Files changed (56) hide show
  1. regscale/_version.py +1 -1
  2. regscale/core/app/application.py +12 -5
  3. regscale/core/app/internal/set_permissions.py +58 -27
  4. regscale/integrations/commercial/defender.py +9 -0
  5. regscale/integrations/commercial/nessus/scanner.py +2 -0
  6. regscale/integrations/commercial/sonarcloud.py +35 -36
  7. regscale/integrations/commercial/synqly/ticketing.py +51 -0
  8. regscale/integrations/commercial/wizv2/async_client.py +325 -0
  9. regscale/integrations/commercial/wizv2/constants.py +756 -0
  10. regscale/integrations/commercial/wizv2/scanner.py +1301 -89
  11. regscale/integrations/commercial/wizv2/utils.py +280 -36
  12. regscale/integrations/commercial/wizv2/variables.py +2 -10
  13. regscale/integrations/integration_override.py +15 -6
  14. regscale/integrations/scanner_integration.py +221 -37
  15. regscale/integrations/variables.py +1 -0
  16. regscale/models/integration_models/amazon_models/inspector_scan.py +32 -57
  17. regscale/models/integration_models/aqua.py +92 -78
  18. regscale/models/integration_models/cisa_kev_data.json +47 -4
  19. regscale/models/integration_models/defenderimport.py +64 -59
  20. regscale/models/integration_models/ecr_models/ecr.py +100 -147
  21. regscale/models/integration_models/flat_file_importer/__init__.py +52 -38
  22. regscale/models/integration_models/ibm.py +29 -47
  23. regscale/models/integration_models/nexpose.py +156 -68
  24. regscale/models/integration_models/prisma.py +46 -66
  25. regscale/models/integration_models/qualys.py +99 -93
  26. regscale/models/integration_models/snyk.py +229 -158
  27. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  28. regscale/models/integration_models/veracode.py +15 -20
  29. regscale/models/integration_models/xray.py +276 -82
  30. regscale/models/regscale_models/__init__.py +13 -0
  31. regscale/models/regscale_models/classification.py +23 -0
  32. regscale/models/regscale_models/control_implementation.py +14 -12
  33. regscale/models/regscale_models/cryptography.py +56 -0
  34. regscale/models/regscale_models/deviation.py +4 -4
  35. regscale/models/regscale_models/group.py +3 -2
  36. regscale/models/regscale_models/interconnection.py +1 -1
  37. regscale/models/regscale_models/issue.py +140 -41
  38. regscale/models/regscale_models/milestone.py +40 -0
  39. regscale/models/regscale_models/property.py +0 -1
  40. regscale/models/regscale_models/rbac.py +22 -0
  41. regscale/models/regscale_models/regscale_model.py +29 -18
  42. regscale/models/regscale_models/team.py +55 -0
  43. {regscale_cli-6.20.9.1.dist-info → regscale_cli-6.21.0.0.dist-info}/METADATA +1 -1
  44. {regscale_cli-6.20.9.1.dist-info → regscale_cli-6.21.0.0.dist-info}/RECORD +56 -49
  45. tests/fixtures/test_fixture.py +58 -2
  46. tests/regscale/core/test_app.py +5 -3
  47. tests/regscale/integrations/test_integration_mapping.py +522 -40
  48. tests/regscale/integrations/test_issue_due_date.py +1 -1
  49. tests/regscale/integrations/test_property_and_milestone_creation.py +684 -0
  50. tests/regscale/integrations/test_update_finding_dates.py +336 -0
  51. tests/regscale/models/test_asset.py +406 -50
  52. tests/regscale/models/test_report.py +105 -29
  53. {regscale_cli-6.20.9.1.dist-info → regscale_cli-6.21.0.0.dist-info}/LICENSE +0 -0
  54. {regscale_cli-6.20.9.1.dist-info → regscale_cli-6.21.0.0.dist-info}/WHEEL +0 -0
  55. {regscale_cli-6.20.9.1.dist-info → regscale_cli-6.21.0.0.dist-info}/entry_points.txt +0 -0
  56. {regscale_cli-6.20.9.1.dist-info → regscale_cli-6.21.0.0.dist-info}/top_level.txt +0 -0
@@ -2,39 +2,45 @@
2
2
  ECR Scan information
3
3
  """
4
4
 
5
- import json
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.core.app.utils.app_utils import get_current_datetime, is_valid_fqdn
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
- self.name = kwargs.get("name")
24
- self.vuln_title = "name"
25
- self.fmt = "%m/%d/%y"
26
- self.dt_format = "%Y-%m-%d %H:%M:%S"
27
- self.image_name = "Name"
28
- self.raw_dict = {}
29
- self.required_headers = [
30
- self.image_name,
31
- ]
32
- self.mapping_file = kwargs.get("mappings_path")
33
- self.disable_mapping = kwargs.get("disable_mapping")
34
- self.file_type = kwargs.get("file_type")
35
- keys = ["imageScanFindings", "findings"] if self.file_type == ".json" else None
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, kwargs.get("file_path"), self.mapping_file, self.disable_mapping, keys=keys
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 file_to_list_of_dicts(
52
- self,
53
- ) -> tuple[Optional[Sequence[str]], Union[dict, list[Any]]]:
57
+ def create_asset(self, dat: Optional[dict] = None) -> IntegrationAsset:
54
58
  """
55
- Override the base method: Converts a json or csv file to a list of dictionaries
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 Asset object
89
- :rtype: Asset
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(dat, "repositoryName", self.raw_dict.get("repositoryName", "")):
93
- if (image_id_data := self.raw_dict.get("imageId", {}).get("imageDigest", "").split(":")) and len(
94
- image_id_data
95
- ) > 1:
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 Asset(
103
- **{
104
- "id": 0,
105
- "name": name,
106
- "description": "Container Image" if name and "/" in name else "",
107
- "operatingSystem": "Linux",
108
- "operatingSystemVersion": "",
109
- "ipAddress": "0.0.0.0",
110
- "isPublic": True,
111
- "status": "Active (On Network)",
112
- "assetCategory": "Software",
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(self, dat: Optional[dict] = None, **kwargs) -> Union[Vulnerability, List[Vulnerability], None]:
90
+ def create_vuln(
91
+ self, dat: Optional[dict] = None, **_kwargs
92
+ ) -> Union[IntegrationFinding, List[IntegrationFinding], None]:
126
93
  """
127
- Create a vulnerability from a row in the ECR csv file
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 Vulnerability object, a list of RegScale Vulnerability objects or None
131
- :rtype: Union[Vulnerability, List[Vulnerability], None]
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[Vulnerability] = []
100
+ vulns: List[IntegrationFinding] = []
134
101
  hostname = dat.get("Name") or dat.get("name")
135
- if repository_name := self.mapping.get_value(dat, "repositoryName", self.raw_dict.get("repositoryName", "")):
136
- image_id_data = self.raw_dict.get("imageId", {}).get("imageDigest", "").split(":")
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 get_asset(self, hostname: str) -> Optional[Asset]:
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 a vulnerability from a row in the ECR file
121
+ Create an IntegrationFinding from finding data
166
122
 
167
- :param Asset asset: The asset
168
- :param str hostname: The hostname
169
- :param str cve: The CVE
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
- config = self.attributes.app.config
176
-
177
- return Vulnerability(
178
- id=0,
179
- scanId=0,
180
- parentId=asset.id,
181
- parentModule="assets",
182
- ipAddress="0.0.0.0",
183
- firstSeen=get_current_datetime(), # No timestamp on ECR
184
- lastSeen=get_current_datetime(), # No timestamp on ECR
185
- daysOpen=None,
186
- dns=hostname,
187
- mitigated=None,
188
- operatingSystem=asset.operatingSystem,
189
- severity=severity,
190
- plugInName=cve,
191
- cve=cve,
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[Vulnerability]:
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[Vulnerability]
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
- if asset := self.get_asset(hostname):
214
- return self.create_vulnerability_object(asset, hostname, cve, severity, dat.get("uri", ""))
215
- return None
216
-
217
- def process_json_vulns(self, dat: dict, hostname: str) -> List[Vulnerability]:
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[Vulnerability]
173
+ :rtype: List[IntegrationFinding]
225
174
  """
226
- vulns: List[Vulnerability] = []
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
- asset = self.get_asset(hostname)
232
- if asset:
233
- vuln = self.create_vulnerability_object(asset, hostname, cve, severity, finding.get("uri", ""))
234
- vulns.append(vuln)
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
- # Set the parent_id, parent_module, and plan_id if they are not provided
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
- if isinstance(vuln, Vulnerability) or isinstance(vuln, IntegrationFinding):
589
- self._check_vuln(vuln)
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._check_vuln(v)
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.models import ImportValidater, Mapping
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, Vulnerability
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) -> Asset:
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 Asset object
62
- :rtype: Asset
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 Asset(
67
+ return IntegrationAsset(
67
68
  **{
68
- "id": 0,
69
69
  "name": hostname,
70
- "isPublic": True,
70
+ "identifier": hostname,
71
71
  "status": "Active (On Network)",
72
- "assetCategory": "Software",
73
- "bLatestScan": True,
74
- "bAuthenticatedScan": True,
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[Vulnerability]:
79
+ def create_vuln(self, dat: Optional[dict] = None, **kwargs) -> Optional[IntegrationFinding]:
86
80
  """
87
- Create a vulnerability from a row in the IBM csv file
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 Vulnerability object or None
91
- :rtype: Optional[Vulnerability]
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
- config = self.attributes.app.config
100
- asset_match = [asset for asset in self.data["assets"] if asset.name == hostname]
101
- asset = asset_match[0] if asset_match else None
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
- plugInText=description,
122
- createdById=config["userId"],
123
- lastUpdatedById=config["userId"],
124
- dateCreated=get_current_datetime(),
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