18 from packageurl import PackageURL |
18 from packageurl import PackageURL |
19 |
19 |
20 from cyclonedx.model import LicenseChoice |
20 from cyclonedx.model import LicenseChoice |
21 from cyclonedx.model.bom import Bom |
21 from cyclonedx.model.bom import Bom |
22 from cyclonedx.model.component import Component |
22 from cyclonedx.model.component import Component |
|
23 from cyclonedx.model.vulnerability import Vulnerability, VulnerabilitySource |
23 from cyclonedx.output import ( |
24 from cyclonedx.output import ( |
24 OutputFormat, SchemaVersion, get_instance as get_output_instance |
25 OutputFormat, SchemaVersion, get_instance as get_output_instance |
25 ) |
26 ) |
26 from cyclonedx.parser import BaseParser |
27 from cyclonedx.parser import BaseParser |
27 |
28 |
28 from cyclonedx_py.parser.pipenv import PipEnvFileParser |
29 from cyclonedx_py.parser.pipenv import PipEnvFileParser |
29 from cyclonedx_py.parser.poetry import PoetryFileParser |
30 from cyclonedx_py.parser.poetry import PoetryFileParser |
30 from cyclonedx_py.parser.requirements import RequirementsFileParser |
31 from cyclonedx_py.parser.requirements import RequirementsFileParser |
|
32 |
|
33 from PipInterface.PipVulnerabilityChecker import ( |
|
34 Package, VulnerabilityCheckError |
|
35 ) |
31 |
36 |
32 |
37 |
33 class CycloneDXEnvironmentParser(BaseParser): |
38 class CycloneDXEnvironmentParser(BaseParser): |
34 """ |
39 """ |
35 Class implementing a parser to get package data for a named environment. |
40 Class implementing a parser to get package data for a named environment. |
74 @exception RuntimeError raised to indicate illegal creation parameters |
79 @exception RuntimeError raised to indicate illegal creation parameters |
75 """ |
80 """ |
76 from .CycloneDXConfigDialog import CycloneDXConfigDialog |
81 from .CycloneDXConfigDialog import CycloneDXConfigDialog |
77 dlg = CycloneDXConfigDialog(venvName) |
82 dlg = CycloneDXConfigDialog(venvName) |
78 if dlg.exec() == QDialog.DialogCode.Accepted: |
83 if dlg.exec() == QDialog.DialogCode.Accepted: |
79 inputSource, inputFile, fileFormat, schemaVersion, sbomFile = ( |
84 (inputSource, inputFile, fileFormat, schemaVersion, sbomFile, |
80 dlg.getData() |
85 withVulnerabilities) = dlg.getData() |
81 ) |
86 |
82 |
87 # check error conditions first |
83 if inputSource not in ("environment", "pipenv", "poetry", |
88 if inputSource not in ("environment", "pipenv", "poetry", |
84 "requirements"): |
89 "requirements"): |
85 raise RuntimeError("Unsupported input source given.") |
90 raise RuntimeError("Unsupported input source given.") |
86 if fileFormat not in ("XML", "JSON"): |
91 if fileFormat not in ("XML", "JSON"): |
87 raise RuntimeError("Unsupported SBOM file format given.") |
92 raise RuntimeError("Unsupported SBOM file format given.") |
108 elif inputSource == "poetry": |
113 elif inputSource == "poetry": |
109 parser = PoetryFileParser(poetry_lock_filename=inputFile) |
114 parser = PoetryFileParser(poetry_lock_filename=inputFile) |
110 elif inputSource == "requirements": |
115 elif inputSource == "requirements": |
111 parser = RequirementsFileParser(requirements_file=inputFile) |
116 parser = RequirementsFileParser(requirements_file=inputFile) |
112 |
117 |
|
118 if withVulnerabilities: |
|
119 addCycloneDXVulnerabilities(parser) |
|
120 |
113 if fileFormat == "XML": |
121 if fileFormat == "XML": |
114 outputFormat = OutputFormat.XML |
122 outputFormat = OutputFormat.XML |
115 elif fileFormat == "JSON": |
123 elif fileFormat == "JSON": |
116 outputFormat = OutputFormat.JSON |
124 outputFormat = OutputFormat.JSON |
117 |
125 |
148 QCoreApplication.translate( |
156 QCoreApplication.translate( |
149 "CycloneDX", |
157 "CycloneDX", |
150 "<p>The SBOM data was written to file <b>{0}</b>.</p>" |
158 "<p>The SBOM data was written to file <b>{0}</b>.</p>" |
151 ).format(sbomFile) |
159 ).format(sbomFile) |
152 ) |
160 ) |
|
161 |
|
162 |
|
163 def addCycloneDXVulnerabilities(parser): |
|
164 """ |
|
165 Function to add vulnerability data to the list of created components. |
|
166 |
|
167 @param parser reference to the parser object containing the list of |
|
168 components |
|
169 @type BaseParser |
|
170 """ |
|
171 components = parser.get_components() |
|
172 |
|
173 packages = [ |
|
174 Package(name=component.name, version=component.version) |
|
175 for component in components |
|
176 ] |
|
177 |
|
178 pip = ericApp().getObject("Pip") |
|
179 error, vulnerabilities = pip.getVulnerabilityChecker().check(packages) |
|
180 |
|
181 if error == VulnerabilityCheckError.OK: |
|
182 for package in vulnerabilities: |
|
183 component = findCyccloneDXComponent(components, package) |
|
184 if component: |
|
185 for vuln in vulnerabilities[package]: |
|
186 component.add_vulnerability(Vulnerability( |
|
187 id=vuln.cve, |
|
188 description=vuln.advisory, |
|
189 recommendation="upgrade required", |
|
190 source=VulnerabilitySource(name="pyup.io") |
|
191 )) |
|
192 |
|
193 |
|
194 def findCyccloneDXComponent(components, name): |
|
195 """ |
|
196 Function to find a component in a given list of components. |
|
197 |
|
198 @param components list of components to scan |
|
199 @type list of Component |
|
200 @param name name of the component to search for |
|
201 @type str |
|
202 @return reference to the found component or None |
|
203 @rtype Component or None |
|
204 """ |
|
205 for component in components: |
|
206 if component.name == name: |
|
207 return component |
|
208 |
|
209 return None |