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.

Files changed (37) 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/nessus/scanner.py +2 -0
  5. regscale/integrations/commercial/sonarcloud.py +35 -36
  6. regscale/integrations/commercial/synqly/ticketing.py +51 -0
  7. regscale/integrations/integration_override.py +15 -6
  8. regscale/integrations/scanner_integration.py +163 -35
  9. regscale/models/integration_models/amazon_models/inspector_scan.py +32 -57
  10. regscale/models/integration_models/aqua.py +92 -78
  11. regscale/models/integration_models/cisa_kev_data.json +47 -4
  12. regscale/models/integration_models/defenderimport.py +64 -59
  13. regscale/models/integration_models/ecr_models/ecr.py +100 -147
  14. regscale/models/integration_models/flat_file_importer/__init__.py +52 -38
  15. regscale/models/integration_models/ibm.py +29 -47
  16. regscale/models/integration_models/nexpose.py +156 -68
  17. regscale/models/integration_models/prisma.py +46 -66
  18. regscale/models/integration_models/qualys.py +99 -93
  19. regscale/models/integration_models/snyk.py +229 -158
  20. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  21. regscale/models/integration_models/veracode.py +15 -20
  22. regscale/models/integration_models/xray.py +276 -82
  23. regscale/models/regscale_models/control_implementation.py +14 -12
  24. regscale/models/regscale_models/milestone.py +1 -1
  25. regscale/models/regscale_models/rbac.py +22 -0
  26. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.0.0.dist-info}/METADATA +1 -1
  27. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.0.0.dist-info}/RECORD +37 -36
  28. tests/fixtures/test_fixture.py +58 -2
  29. tests/regscale/core/test_app.py +5 -3
  30. tests/regscale/integrations/test_integration_mapping.py +522 -40
  31. tests/regscale/integrations/test_issue_due_date.py +1 -1
  32. tests/regscale/integrations/test_update_finding_dates.py +336 -0
  33. tests/regscale/models/test_asset.py +406 -50
  34. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.0.0.dist-info}/LICENSE +0 -0
  35. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.0.0.dist-info}/WHEEL +0 -0
  36. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.0.0.dist-info}/entry_points.txt +0 -0
  37. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.0.0.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,52 @@
1
1
  {
2
2
  "title": "CISA Catalog of Known Exploited Vulnerabilities",
3
- "catalogVersion": "2025.07.29",
4
- "dateReleased": "2025-07-29T12:46:00.2038Z",
5
- "count": 1391,
3
+ "catalogVersion": "2025.08.05",
4
+ "dateReleased": "2025-08-05T18:03:16.7522Z",
5
+ "count": 1394,
6
6
  "vulnerabilities": [
7
+ {
8
+ "cveID": "CVE-2020-25078",
9
+ "vendorProject": "D-Link",
10
+ "product": "DCS-2530L and DCS-2670L Devices",
11
+ "vulnerabilityName": "D-Link DCS-2530L and DCS-2670L Devices Unspecified Vulnerability",
12
+ "dateAdded": "2025-08-05",
13
+ "shortDescription": "D-Link DCS-2530L and DCS-2670L devices contains an unspecified vulnerability that could allow for remote administrator password disclosure. The impacted products could be end-of-life (EoL) and\/or end-of-service (EoS). Users should discontinue product utilization.",
14
+ "requiredAction": "Apply mitigations per vendor instructions, follow applicable BOD 22-01 guidance for cloud services, or discontinue use of the product if mitigations are unavailable.",
15
+ "dueDate": "2025-08-26",
16
+ "knownRansomwareCampaignUse": "Unknown",
17
+ "notes": "https:\/\/support.dlink.com\/productinfo.aspx?m=DCS-2530L ; https:\/\/supportannouncement.us.dlink.com\/announcement\/publication.aspx?name=SAP10180 ; https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2020-25078",
18
+ "cwes": []
19
+ },
20
+ {
21
+ "cveID": "CVE-2020-25079",
22
+ "vendorProject": "D-Link",
23
+ "product": "DCS-2530L and DCS-2670L Devices",
24
+ "vulnerabilityName": "D-Link DCS-2530L and DCS-2670L Command Injection Vulnerability",
25
+ "dateAdded": "2025-08-05",
26
+ "shortDescription": "D-Link DCS-2530L and DCS-2670L devices contains a command injection vulnerability in the cgi-bin\/ddns_enc.cgi. The impacted products could be end-of-life (EoL) and\/or end-of-service (EoS). Users should discontinue product utilization.",
27
+ "requiredAction": "Apply mitigations per vendor instructions, follow applicable BOD 22-01 guidance for cloud services, or discontinue use of the product if mitigations are unavailable.",
28
+ "dueDate": "2025-08-26",
29
+ "knownRansomwareCampaignUse": "Unknown",
30
+ "notes": "https:\/\/support.dlink.com\/productinfo.aspx?m=DCS-2530L ; https:\/\/supportannouncement.us.dlink.com\/announcement\/publication.aspx?name=SAP10180 ; https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2020-25079",
31
+ "cwes": [
32
+ "CWE-77"
33
+ ]
34
+ },
35
+ {
36
+ "cveID": "CVE-2022-40799",
37
+ "vendorProject": "D-Link",
38
+ "product": "DNR-322L",
39
+ "vulnerabilityName": "D-Link DNR-322L Download of Code Without Integrity Check Vulnerability",
40
+ "dateAdded": "2025-08-05",
41
+ "shortDescription": "D-Link DNR-322L contains a download of code without integrity check vulnerability that could allow an authenticated attacker to execute OS level commands on the device. The impacted products could be end-of-life (EoL) and\/or end-of-service (EoS). Users should discontinue product utilization.",
42
+ "requiredAction": "Apply mitigations per vendor instructions, follow applicable BOD 22-01 guidance for cloud services, or discontinue use of the product if mitigations are unavailable.",
43
+ "dueDate": "2025-08-26",
44
+ "knownRansomwareCampaignUse": "Unknown",
45
+ "notes": "https:\/\/www.dlink.com\/uk\/en\/products\/dnr-322l-cloud-network-video-recorder ; https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2022-40799",
46
+ "cwes": [
47
+ "CWE-494"
48
+ ]
49
+ },
7
50
  {
8
51
  "cveID": "CVE-2023-2533",
9
52
  "vendorProject": "PaperCut",
@@ -148,7 +191,7 @@
148
191
  "shortDescription": "Microsoft SharePoint Server on-premises contains a deserialization of untrusted data vulnerability that could allow an unauthorized attacker to execute code over a network. This vulnerability could be chained with CVE-2025-53771. CVE-2025-53770 is a patch bypass for CVE-2025-49704, and the updates for CVE-2025-53770 include more robust protection than those for CVE-2025-49704.",
149
192
  "requiredAction": "Disconnect public-facing versions of SharePoint Server that have reached their end-of-life (EOL) or end-of-service (EOS) to include SharePoint Server 2013 and earlier versions. For supported versions, please follow the mitigations according to CISA (URL listed below in Notes) and vendor instructions (URL listed below in Notes). Adhere to the applicable BOD 22-01 guidance for cloud services or discontinue use of the product if mitigations are not available.",
150
193
  "dueDate": "2025-07-21",
151
- "knownRansomwareCampaignUse": "Unknown",
194
+ "knownRansomwareCampaignUse": "Known",
152
195
  "notes": "CISA Mitigation Instructions: https:\/\/www.cisa.gov\/news-events\/alerts\/2025\/07\/20\/microsoft-releases-guidance-exploitation-sharepoint-vulnerability-cve-2025-53770; https:\/\/www.microsoft.com\/en-us\/security\/blog\/2025\/07\/22\/disrupting-active-exploitation-of-on-premises-sharepoint-vulnerabilities\/ ; https:\/\/msrc.microsoft.com\/update-guide\/vulnerability\/CVE-2025-53770 ; https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2025-53770",
153
196
  "cwes": [
154
197
  "CWE-502"
@@ -3,13 +3,14 @@ Integration model to import data from Defender .csv export
3
3
  """
4
4
 
5
5
  import json
6
- from typing import Optional
6
+ from typing import Iterator, List, Optional
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 get_current_datetime, is_valid_fqdn
10
+ from regscale.core.app.utils.app_utils import is_valid_fqdn
11
11
  from regscale.core.utils.date import datetime_obj, datetime_str
12
- from regscale.models import Asset, ImportValidater, Vulnerability
12
+ from regscale.integrations.scanner_integration import IntegrationAsset, IntegrationFinding
13
+ from regscale.models import Asset, ImportValidater, IssueSeverity, IssueStatus
13
14
  from regscale.models.integration_models.flat_file_importer import FlatFileImporter
14
15
 
15
16
 
@@ -60,7 +61,7 @@ class DefenderImport(FlatFileImporter):
60
61
 
61
62
  return datetime_str(dt_object, self.dt_format)
62
63
 
63
- def create_asset(self, dat: Optional[dict] = None) -> Asset:
64
+ def create_asset(self, dat: Optional[dict] = None) -> IntegrationAsset:
64
65
  """
65
66
  Create an asset from a row in the Snyk file
66
67
 
@@ -72,72 +73,76 @@ class DefenderImport(FlatFileImporter):
72
73
  os = Asset.find_os(additional_data.get("imageDetails", {}).get("osDetails", ""))
73
74
  name = additional_data.get("repositoryName", "")
74
75
  valid_name = is_valid_fqdn(name)
75
- return Asset(
76
- **{
77
- "id": 0,
78
- "name": name,
79
- "ipAddress": "0.0.0.0",
80
- "isPublic": True,
81
- "status": "Active (On Network)",
82
- "assetCategory": "Software",
83
- "bLatestScan": True,
84
- "bAuthenticatedScan": True,
85
- "scanningTool": self.name,
86
- "assetOwnerId": self.config["userId"],
87
- "assetType": "Other",
88
- "fqdn": name if valid_name else None,
89
- "systemAdministratorId": self.config["userId"],
90
- "parentId": self.attributes.parent_id,
91
- "parentModule": self.attributes.parent_module,
92
- "operatingSystem": os,
93
- }
76
+ return IntegrationAsset(
77
+ identifier=name,
78
+ name=name,
79
+ ip_address="0.0.0.0",
80
+ status="Active (On Network)",
81
+ asset_category="Software",
82
+ scanning_tool=self.name,
83
+ asset_type="Other",
84
+ fqdn=name if valid_name else None,
85
+ operating_system=os,
94
86
  )
95
87
 
96
- def create_vuln(self, dat: Optional[dict] = None, **kwargs: dict) -> Optional[Vulnerability]:
88
+ def create_vuln(self, dat: Optional[dict] = None, **kwargs: dict) -> Iterator[IntegrationFinding]:
97
89
  """
98
90
  Create a vulnerability from a row in the Snyk csv file
99
91
 
100
92
  :param Optional[dict] dat: Data row from CSV file, defaults to None
101
93
  :param dict **kwargs: Additional keyword arguments
102
- :return: RegScale Vulnerability object or None
103
- :rtype: Optional[Vulnerability]
94
+ :return: RegScale Iterator of findings
95
+ :rtype: Iterator[IntegrationFinding]
104
96
  """
105
- regscale_vuln = None
106
- severity = self.mapping.get_value(dat, "SEVERITY", "").lower()
97
+ regscale_findings: List[IntegrationFinding] = []
98
+ severity = self.determine_severity(self.mapping.get_value(dat, "SEVERITY", "").lower())
107
99
  additional_data = json.loads(self.mapping.get_value(dat, "ADDITIONALDATA", {}))
108
100
  hostname = additional_data.get("repositoryName", "")
109
101
  description = self.mapping.get_value(dat, self.vuln_title)
110
102
  solution = self.mapping.get_value(dat, self.vuln_id)
111
- config = self.attributes.app.config
112
- asset_match = [asset for asset in self.data["assets"] if asset.name == hostname]
113
- asset = asset_match[0] if asset_match else None
114
103
  cves = [cve.get("title", "") for cve in additional_data.get("cve", [])]
115
104
  cvss_v3_score = float(additional_data.get("cvssV30Score", 0))
116
- if dat and asset_match:
117
- regscale_vuln = Vulnerability(
118
- id=0,
119
- scanId=0, # set later
120
- parentId=asset.id,
121
- parentModule="assets",
122
- ipAddress="0.0.0.0", # No ip address available
123
- lastSeen=get_current_datetime(),
124
- firstSeen=self.determine_first_seen(dat),
125
- daysOpen=None,
126
- dns=hostname,
127
- mitigated=None,
128
- operatingSystem=None,
129
- severity=severity,
130
- plugInName=description,
131
- cve=", ".join(cves) if cves else self.mapping.get_value(dat, self.vuln_title),
132
- vprScore=None,
133
- cvsSv3BaseScore=cvss_v3_score,
134
- tenantsId=0,
135
- title=f"{description} on asset {asset.name}",
136
- description=description,
137
- plugInText=self.mapping.get_value(dat, self.vuln_title),
138
- createdById=config["userId"],
139
- lastUpdatedById=config["userId"],
140
- dateCreated=get_current_datetime(),
141
- extra_data={"solution": solution},
142
- )
143
- return regscale_vuln
105
+ if dat:
106
+ if cves and isinstance(cves, list):
107
+ for cve in cves:
108
+ regscale_finding = IntegrationFinding(
109
+ title=f"{description} on asset {hostname}",
110
+ asset_identifier=hostname,
111
+ description=description,
112
+ severity=severity,
113
+ status=IssueStatus.Open.value,
114
+ cvss_v3_score=cvss_v3_score,
115
+ cvss_v3_base_score=cvss_v3_score,
116
+ plugin_name=description,
117
+ plugin_text=self.mapping.get_value(dat, self.vuln_title),
118
+ cve=cve,
119
+ recommendation_for_mitigation=solution,
120
+ first_seen=self.determine_first_seen(dat),
121
+ last_seen=self.scan_date,
122
+ scan_date=self.scan_date,
123
+ category="Software",
124
+ control_labels=[],
125
+ )
126
+ regscale_findings.append(regscale_finding)
127
+ else:
128
+ regscale_finding = IntegrationFinding(
129
+ title=f"{description} on asset {hostname}",
130
+ description=description,
131
+ severity=severity,
132
+ status=IssueStatus.Open.value,
133
+ cvss_v3_score=cvss_v3_score,
134
+ cvss_v3_base_score=cvss_v3_score,
135
+ plugin_name=description,
136
+ plugin_text=self.mapping.get_value(dat, self.vuln_title),
137
+ asset_identifier=hostname,
138
+ cve=self.mapping.get_value(dat, self.vuln_title),
139
+ recommendation_for_mitigation=solution,
140
+ first_seen=self.determine_first_seen(dat),
141
+ last_seen=self.scan_date,
142
+ scan_date=self.scan_date,
143
+ category="Software",
144
+ control_labels=[],
145
+ )
146
+ regscale_findings.append(regscale_finding)
147
+
148
+ yield from regscale_findings
@@ -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