eric7/CycloneDXInterface/CycloneDXUtilities.py

Fri, 03 Jun 2022 19:54:57 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Fri, 03 Jun 2022 19:54:57 +0200
branch
eric7
changeset 9117
c6afba2049cf
child 9119
5bcdef5207f6
permissions
-rw-r--r--

CycloneDX Interface
- added capability to create a Software Bill of Materials (SBOM) file in CycloneDX format
- added this capability to the pip Interface and Project

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

eric ide

mercurial