--- a/eric7/CycloneDXInterface/CycloneDXUtilities.py Sat Jun 04 11:56:48 2022 +0200 +++ b/eric7/CycloneDXInterface/CycloneDXUtilities.py Sat Jun 04 15:53:41 2022 +0200 @@ -20,6 +20,7 @@ from cyclonedx.model import LicenseChoice from cyclonedx.model.bom import Bom from cyclonedx.model.component import Component +from cyclonedx.model.vulnerability import Vulnerability, VulnerabilitySource from cyclonedx.output import ( OutputFormat, SchemaVersion, get_instance as get_output_instance ) @@ -29,6 +30,10 @@ from cyclonedx_py.parser.poetry import PoetryFileParser from cyclonedx_py.parser.requirements import RequirementsFileParser +from PipInterface.PipVulnerabilityChecker import ( + Package, VulnerabilityCheckError +) + class CycloneDXEnvironmentParser(BaseParser): """ @@ -76,10 +81,10 @@ from .CycloneDXConfigDialog import CycloneDXConfigDialog dlg = CycloneDXConfigDialog(venvName) if dlg.exec() == QDialog.DialogCode.Accepted: - inputSource, inputFile, fileFormat, schemaVersion, sbomFile = ( - dlg.getData() - ) + (inputSource, inputFile, fileFormat, schemaVersion, sbomFile, + withVulnerabilities) = dlg.getData() + # check error conditions first if inputSource not in ("environment", "pipenv", "poetry", "requirements"): raise RuntimeError("Unsupported input source given.") @@ -110,6 +115,9 @@ elif inputSource == "requirements": parser = RequirementsFileParser(requirements_file=inputFile) + if withVulnerabilities: + addCycloneDXVulnerabilities(parser) + if fileFormat == "XML": outputFormat = OutputFormat.XML elif fileFormat == "JSON": @@ -150,3 +158,52 @@ "<p>The SBOM data was written to file <b>{0}</b>.</p>" ).format(sbomFile) ) + + +def addCycloneDXVulnerabilities(parser): + """ + Function to add vulnerability data to the list of created components. + + @param parser reference to the parser object containing the list of + components + @type BaseParser + """ + components = parser.get_components() + + packages = [ + Package(name=component.name, version=component.version) + for component in components + ] + + pip = ericApp().getObject("Pip") + error, vulnerabilities = pip.getVulnerabilityChecker().check(packages) + + if error == VulnerabilityCheckError.OK: + for package in vulnerabilities: + component = findCyccloneDXComponent(components, package) + if component: + for vuln in vulnerabilities[package]: + component.add_vulnerability(Vulnerability( + id=vuln.cve, + description=vuln.advisory, + recommendation="upgrade required", + source=VulnerabilitySource(name="pyup.io") + )) + + +def findCyccloneDXComponent(components, name): + """ + Function to find a component in a given list of components. + + @param components list of components to scan + @type list of Component + @param name name of the component to search for + @type str + @return reference to the found component or None + @rtype Component or None + """ + for component in components: + if component.name == name: + return component + + return None