src/eric7/CycloneDXInterface/CycloneDXConfigDialog.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 9141
7085ece52151
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to configure the CycloneDX SBOM generation.
8 """
9
10 import os
11
12 from PyQt6.QtCore import pyqtSlot
13 from PyQt6.QtWidgets import QDialog, QDialogButtonBox
14
15 from EricWidgets.EricApplication import ericApp
16 from EricWidgets.EricPathPicker import EricPathPickerModes
17
18 from .Ui_CycloneDXConfigDialog import Ui_CycloneDXConfigDialog
19
20
21 class CycloneDXConfigDialog(QDialog, Ui_CycloneDXConfigDialog):
22 """
23 Class implementing a dialog to configure the CycloneDX SBOM generation.
24 """
25 SupportedSchemas = {
26 "JSON": [
27 (1, 4),
28 (1, 3),
29 (1, 2),
30 ],
31 "XML": [
32 (1, 4),
33 (1, 3),
34 (1, 2),
35 (1, 1),
36 (1, 0),
37 ],
38 }
39 Sources = {
40 "pipenv": "Pipfile.lock",
41 "poetry": "poetry.lock",
42 "requirements": "requirements.txt",
43 }
44 DefaultFileFormat = "JSON"
45 DefaultFileNames = {
46 "JSON": "cyclonedx.json",
47 "XML": "cyclonedx.xml",
48 }
49
50 def __init__(self, environment, parent=None):
51 """
52 Constructor
53
54 @param environment name of the virtual environment
55 @type str
56 @param parent reference to the parent widget (defaults to None)
57 @type QWidget (optional)
58 """
59 super().__init__(parent)
60 self.setupUi(self)
61
62 if environment == "<project>":
63 self.__project = ericApp().getObject("Project")
64 self.__defaultDirectory = self.__project.getProjectPath()
65 else:
66 self.__project = None
67 venvManager = ericApp().getObject("VirtualEnvManager")
68 self.__defaultDirectory = venvManager.getVirtualenvDirectory(
69 environment)
70
71 self.environmentLabel.setText(environment)
72
73 self.pipenvButton.setEnabled(os.path.isfile(os.path.join(
74 self.__defaultDirectory,
75 CycloneDXConfigDialog.Sources["pipenv"]
76 )))
77 self.poetryButton.setEnabled(os.path.isfile(os.path.join(
78 self.__defaultDirectory,
79 CycloneDXConfigDialog.Sources["poetry"]
80 )))
81 self.requirementsButton.setEnabled(os.path.isfile(os.path.join(
82 self.__defaultDirectory,
83 CycloneDXConfigDialog.Sources["requirements"]
84 )))
85
86 self.vulnerabilityCheckBox.toggled.connect(
87 self.__repopulateSchemaVersionComboBox)
88
89 self.filePicker.setMode(EricPathPickerModes.SAVE_FILE_OVERWRITE_MODE)
90 self.filePicker.setDefaultDirectory(self.__defaultDirectory)
91
92 self.fileFormatComboBox.setCurrentText(
93 CycloneDXConfigDialog.DefaultFileFormat)
94 self.on_fileFormatComboBox_currentTextChanged(
95 CycloneDXConfigDialog.DefaultFileFormat)
96
97 self.__metadata = None
98 self.__metadataButton = self.buttonBox.addButton(
99 self.tr("Edit Metadata..."),
100 QDialogButtonBox.ButtonRole.ActionRole)
101 self.__metadataButton.clicked.connect(self.__editMetaData)
102
103 msh = self.minimumSizeHint()
104 self.resize(max(self.width(), msh.width()), msh.height())
105
106 @pyqtSlot()
107 def __repopulateSchemaVersionComboBox(self):
108 """
109 Private slot to repopulate the schema version selector.
110 """
111 fileFormat = self.fileFormatComboBox.currentText()
112 minSchemaVersion = (
113 (1, 4)
114 if self.vulnerabilityCheckBox.isChecked() else
115 (1, 0)
116 )
117 self.schemaVersionComboBox.clear()
118 self.schemaVersionComboBox.addItems(
119 "{0}.{1}".format(*f)
120 for f in CycloneDXConfigDialog.SupportedSchemas[fileFormat]
121 if f >= minSchemaVersion
122 )
123
124 @pyqtSlot(str)
125 def on_fileFormatComboBox_currentTextChanged(self, fileFormat):
126 """
127 Private slot to handle the selection of a SBOM file format.
128
129 @param fileFormat selected format
130 @type str
131 """
132 # re-populate the file schema combo box
133 self.__repopulateSchemaVersionComboBox()
134
135 # set the file filter
136 if fileFormat == "JSON":
137 self.filePicker.setFilters(
138 self.tr("JSON Files (*.json);;All Files (*)"))
139 elif fileFormat == "XML":
140 self.filePicker.setFilters(
141 self.tr("XML Files (*.xml);;All Files (*)"))
142 else:
143 self.filePicker.setFilters(self.tr("All Files (*)"))
144
145 @pyqtSlot()
146 def __editMetaData(self):
147 """
148 Private slot to open a dialog for editing the SBOM metadata.
149 """
150 from .CycloneDXMetaDataDialog import CycloneDXMetaDataDialog
151
152 # populate a metadata dictionary from project data
153 metadata = (
154 {
155 "Name": self.__project.getProjectName(),
156 "Type": "",
157 "Version": self.__project.getProjectVersion(),
158 "Description": self.__project.getProjectDescription(),
159 "AuthorName": self.__project.getProjectAuthor(),
160 "AuthorEmail": self.__project.getProjectAuthorEmail(),
161 "License": self.__project.getProjectLicense(),
162 "Manufacturer": "",
163 "Supplier": "",
164 }
165 if self.__metadata is None and self.__project is not None else
166 self.__metadata
167 )
168
169 dlg = CycloneDXMetaDataDialog(metadata=metadata, parent=self)
170 if dlg.exec() == QDialog.DialogCode.Accepted:
171 self.__metadata = dlg.getMetaData()
172
173 def getData(self):
174 """
175 Public method to get the SBOM configuration data.
176
177 @return tuple containing the input source, the input file name, the
178 file format, the schema version, the path of the SBOM file to be
179 written, a flag indicating to include vulnerability information,
180 a flag indicating to include dependency information and a
181 dictionary containing the SBOM meta data
182 @rtype tuple of (str, str, str, str, str, bool, bool, dict)
183 """
184 if self.environmentButton.isChecked():
185 inputSource = "environment"
186 inputFile = None
187 elif self.pipenvButton.isChecked():
188 inputSource = "pipenv"
189 inputFile = os.path.join(
190 self.__defaultDirectory,
191 CycloneDXConfigDialog.Sources["pipenv"]
192 )
193 elif self.poetryButton.isChecked():
194 inputSource = "poetry"
195 inputFile = os.path.join(
196 self.__defaultDirectory,
197 CycloneDXConfigDialog.Sources["poetry"]
198 )
199 elif self.requirementsButton.isChecked():
200 inputSource = "requirements"
201 inputFile = os.path.join(
202 self.__defaultDirectory,
203 CycloneDXConfigDialog.Sources["requirements"]
204 )
205 else:
206 # should not happen
207 inputSource = None
208 inputFile = None
209
210 fileFormat = self.fileFormatComboBox.currentText()
211 schemaVersion = self.schemaVersionComboBox.currentText()
212 sbomFile = self.filePicker.text()
213 if not sbomFile:
214 try:
215 sbomFile = os.path.join(
216 self.__defaultDirectory,
217 CycloneDXConfigDialog.DefaultFileNames[fileFormat]
218 )
219 except KeyError:
220 # should not happen
221 sbomFile = None
222
223 return (
224 inputSource, inputFile, fileFormat, schemaVersion, sbomFile,
225 self.vulnerabilityCheckBox.isChecked(),
226 self.dependenciesCheckBox.isChecked(),
227 self.__metadata
228 )

eric ide

mercurial