eric7/CycloneDXInterface/CycloneDXUtilities.py

branch
eric7
changeset 9117
c6afba2049cf
child 9119
5bcdef5207f6
equal deleted inserted replaced
9116:698660ef7350 9117:c6afba2049cf
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the interface to CycloneDX.
8 """
9
10 import os
11
12 from PyQt6.QtCore import QCoreApplication
13 from PyQt6.QtWidgets import QDialog
14
15 from EricWidgets.EricApplication import ericApp
16 from EricWidgets import EricMessageBox
17
18 from packageurl import PackageURL
19
20 from cyclonedx.model import LicenseChoice
21 from cyclonedx.model.bom import Bom
22 from cyclonedx.model.component import Component
23 from cyclonedx.output import (
24 OutputFormat, SchemaVersion, get_instance as get_output_instance
25 )
26 from cyclonedx.parser import BaseParser
27
28 from cyclonedx_py.parser.pipenv import PipEnvFileParser
29 from cyclonedx_py.parser.poetry import PoetryFileParser
30 from cyclonedx_py.parser.requirements import RequirementsFileParser
31
32
33 class CycloneDXEnvironmentParser(BaseParser):
34 """
35 Class implementing a parser to get package data for a named environment.
36 """
37 def __init__(self, venvName):
38 """
39 Constructor
40
41 @param venvName name of the virtual environment
42 @type str
43 """
44 super().__init__()
45
46 pip = ericApp().getObject("Pip")
47 packages = pip.getLicenses(venvName)
48 for package in packages:
49 comp = Component(
50 name=package["Name"],
51 version=package["Version"],
52 author=package["Author"],
53 description=package["Description"],
54 purl=PackageURL(
55 type='pypi',
56 name=package["Name"],
57 version=package["Version"]
58 )
59 )
60 for lic in package["License"].split(";"):
61 comp.licenses.add(
62 LicenseChoice(license_expression=lic.strip())
63 )
64
65 self._components.append(comp)
66
67
68 def createCycloneDXFile(venvName):
69 """
70 Function to create a CyccloneDX SBOM file.
71
72 @param venvName name of the virtual environment
73 @type str
74 @exception RuntimeError raised to indicate illegal creation parameters
75 """
76 from .CycloneDXConfigDialog import CycloneDXConfigDialog
77 dlg = CycloneDXConfigDialog(venvName)
78 if dlg.exec() == QDialog.DialogCode.Accepted:
79 inputSource, inputFile, fileFormat, schemaVersion, sbomFile = (
80 dlg.getData()
81 )
82
83 if inputSource not in ("environment", "pipenv", "poetry",
84 "requirements"):
85 raise RuntimeError("Unsupported input source given.")
86 if fileFormat not in ("XML", "JSON"):
87 raise RuntimeError("Unsupported SBOM file format given.")
88
89 if inputSource == "environment":
90 parser = CycloneDXEnvironmentParser(venvName)
91 else:
92 # all other parsers need an input file
93 if not os.path.isfile(inputFile):
94 EricMessageBox.warning(
95 None,
96 QCoreApplication.translate(
97 "CycloneDX", "CycloneDX - SBOM Creation"),
98 QCoreApplication.translate(
99 "CycloneDX",
100 "<p>The configured input file <b>{0}</b> does not"
101 " exist. Aborting...</p>"
102 ).format(inputFile)
103 )
104 return
105
106 if inputSource == "pipenv":
107 parser = PipEnvFileParser(pipenv_lock_filename=inputFile)
108 elif inputSource == "poetry":
109 parser = PoetryFileParser(poetry_lock_filename=inputFile)
110 elif inputSource == "requirements":
111 parser = RequirementsFileParser(requirements_file=inputFile)
112
113 if fileFormat == "XML":
114 outputFormat = OutputFormat.XML
115 elif fileFormat == "JSON":
116 outputFormat = OutputFormat.JSON
117
118 if parser.has_warnings():
119 excludedList = ["<li>{0}</li>".format(warning.get_item())
120 for warning in parser.get_warnings()]
121 EricMessageBox.warning(
122 None,
123 QCoreApplication.translate(
124 "CycloneDX", "CycloneDX - SBOM Creation"),
125 QCoreApplication.translate(
126 "CycloneDX",
127 "<p>Some of the dependencies do not have pinned version"
128 " numbers.<ul>{0}</ul>The above listed packages will NOT"
129 " be included in the generated CycloneDX SBOM file as"
130 " version is a mandatory field.</p>"
131 ).format("".join(excludedList))
132 )
133
134 bom = Bom.from_parser(parser=parser)
135 output = get_output_instance(
136 bom=bom,
137 output_format=outputFormat,
138 schema_version=SchemaVersion['V{0}'.format(
139 schemaVersion.replace('.', '_')
140 )]
141 )
142 output.output_to_file(filename=sbomFile, allow_overwrite=True)
143
144 EricMessageBox.information(
145 None,
146 QCoreApplication.translate(
147 "CycloneDX", "CycloneDX - SBOM Creation"),
148 QCoreApplication.translate(
149 "CycloneDX",
150 "<p>The SBOM data was written to file <b>{0}</b>.</p>"
151 ).format(sbomFile)
152 )

eric ide

mercurial