csspin-python 4.1.0rc1__py3-none-any.whl → 4.1.0rc2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- csspin_python/python_sbom.py +114 -13
- {csspin_python-4.1.0rc1.dist-info → csspin_python-4.1.0rc2.dist-info}/METADATA +1 -1
- {csspin_python-4.1.0rc1.dist-info → csspin_python-4.1.0rc2.dist-info}/RECORD +8 -6
- csspin_python-4.1.0rc2.dist-info/scm_file_list.json +78 -0
- csspin_python-4.1.0rc2.dist-info/scm_version.json +8 -0
- {csspin_python-4.1.0rc1.dist-info → csspin_python-4.1.0rc2.dist-info}/WHEEL +0 -0
- {csspin_python-4.1.0rc1.dist-info → csspin_python-4.1.0rc2.dist-info}/licenses/LICENSE +0 -0
- {csspin_python-4.1.0rc1.dist-info → csspin_python-4.1.0rc2.dist-info}/top_level.txt +0 -0
csspin_python/python_sbom.py
CHANGED
|
@@ -17,8 +17,12 @@
|
|
|
17
17
|
|
|
18
18
|
"""Module implementing the python_sbom plugin for csspin"""
|
|
19
19
|
|
|
20
|
+
import email.utils
|
|
20
21
|
import json
|
|
21
22
|
import sys
|
|
23
|
+
import sysconfig
|
|
24
|
+
from importlib.metadata import metadata as _pkg_metadata
|
|
25
|
+
from importlib.metadata import version as _pkg_version
|
|
22
26
|
from subprocess import DEVNULL, PIPE
|
|
23
27
|
from tempfile import TemporaryDirectory
|
|
24
28
|
|
|
@@ -68,8 +72,9 @@ def sbom(cfg: ConfigTree) -> None:
|
|
|
68
72
|
third_party_deps = _collect_thirdparty_deps(
|
|
69
73
|
metadata.get("requires_dist", set()), python_version=cfg.python.version
|
|
70
74
|
)
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
sbom_json = json.loads(_run_cyclonedx(cfg, third_party_deps, stderr))
|
|
76
|
+
_enrich_sbom(sbom_json, metadata, third_party_deps)
|
|
77
|
+
_write_sbom(cfg, sbom_json, metadata.get("name"))
|
|
73
78
|
|
|
74
79
|
|
|
75
80
|
def cleanup(cfg: ConfigTree) -> None:
|
|
@@ -159,22 +164,118 @@ def _run_cyclonedx(cfg: ConfigTree, third_party_deps: set[str], stderr: int) ->
|
|
|
159
164
|
return backtick(interpreter_cdx, "-m", "cyclonedx_py", "environment", venv, stderr=stderr) # type: ignore[no-any-return] # noqa: E501
|
|
160
165
|
|
|
161
166
|
|
|
162
|
-
def _write_sbom(
|
|
163
|
-
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
# using pyproject.toml
|
|
169
|
-
sbom_json = json.loads(content)
|
|
170
|
-
sbom_json |= {
|
|
171
|
-
"metadata": {"component": {"name": project_name, "version": project_version}}
|
|
172
|
-
}
|
|
167
|
+
def _write_sbom(cfg: ConfigTree, sbom_json: dict, project_name: str) -> None:
|
|
168
|
+
"""Write the CycloneDX JSON document to the output file."""
|
|
169
|
+
platform_tag = sysconfig.get_platform().replace("-", "_")
|
|
170
|
+
output_file = cfg.spin.project_root / (
|
|
171
|
+
f"{project_name}.{platform_tag}.python_sbom.cdx.json"
|
|
172
|
+
)
|
|
173
173
|
with open(output_file, "w", encoding="utf-8") as f:
|
|
174
174
|
json.dump(sbom_json, f, indent=2, sort_keys=True)
|
|
175
175
|
info(f"Generated Python SBOM successfully ({output_file})")
|
|
176
176
|
|
|
177
177
|
|
|
178
|
+
def _parse_authors(author_name: str, author_email: str) -> str:
|
|
179
|
+
"""Parse RFC 2822 Author-email metadata into 'name (email)' format."""
|
|
180
|
+
if not author_email:
|
|
181
|
+
die("Project metadata has no Author-email field.")
|
|
182
|
+
return ""
|
|
183
|
+
|
|
184
|
+
entries = email.utils.getaddresses([author_email])
|
|
185
|
+
|
|
186
|
+
if len(entries) == 1 and not entries[0][0] and author_name:
|
|
187
|
+
entries = [(author_name, entries[0][1])]
|
|
188
|
+
|
|
189
|
+
for name, addr in entries:
|
|
190
|
+
if not name:
|
|
191
|
+
die(f"Author entry '{addr}' has no name; all authors require a name.")
|
|
192
|
+
return ""
|
|
193
|
+
if not addr:
|
|
194
|
+
die(f"Author entry '{name}' has no email; all authors require an email.")
|
|
195
|
+
return ""
|
|
196
|
+
|
|
197
|
+
return ", ".join(f"{name} ({addr})" for name, addr in entries)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _build_primary_component(metadata: dict) -> tuple[dict, str]:
|
|
201
|
+
"""Build the CycloneDX primary component dict and its bom-ref from project metadata."""
|
|
202
|
+
if not (name := metadata.get("name")):
|
|
203
|
+
die("Project metadata is missing 'name'.")
|
|
204
|
+
if not (version := metadata.get("version")):
|
|
205
|
+
die("Project metadata is missing 'version'.")
|
|
206
|
+
if not (license_id := metadata.get("license")):
|
|
207
|
+
die("Project metadata is missing 'license'.")
|
|
208
|
+
|
|
209
|
+
authors = _parse_authors(
|
|
210
|
+
author_name=metadata.get("author", "").strip(),
|
|
211
|
+
author_email=metadata.get("author_email", "").strip(),
|
|
212
|
+
)
|
|
213
|
+
primary_ref = f"{name}=={version}"
|
|
214
|
+
component = {
|
|
215
|
+
"author": authors,
|
|
216
|
+
"bom-ref": primary_ref,
|
|
217
|
+
"licenses": [{"expression": license_id}],
|
|
218
|
+
"name": name,
|
|
219
|
+
"type": "application",
|
|
220
|
+
"version": version,
|
|
221
|
+
}
|
|
222
|
+
return component, primary_ref
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _enrich_sbom(
|
|
226
|
+
sbom_json: dict,
|
|
227
|
+
metadata: dict,
|
|
228
|
+
third_party_deps: set[str],
|
|
229
|
+
) -> None:
|
|
230
|
+
"""Add the primary component and its dependency entry to the CycloneDX document."""
|
|
231
|
+
component, primary_ref = _build_primary_component(metadata)
|
|
232
|
+
existing_metadata = sbom_json.get("metadata", {})
|
|
233
|
+
existing_tools = existing_metadata.get("tools", {})
|
|
234
|
+
existing_tool_components = (
|
|
235
|
+
existing_tools
|
|
236
|
+
if isinstance(existing_tools, list)
|
|
237
|
+
else existing_tools.get("components", [])
|
|
238
|
+
)
|
|
239
|
+
sbom_json["metadata"] = {
|
|
240
|
+
**existing_metadata,
|
|
241
|
+
"component": component,
|
|
242
|
+
"tools": {
|
|
243
|
+
"components": existing_tool_components
|
|
244
|
+
+ [
|
|
245
|
+
{
|
|
246
|
+
"description": "Python SBOM plugin for csspin",
|
|
247
|
+
"licenses": [
|
|
248
|
+
{
|
|
249
|
+
"expression": _pkg_metadata("csspin-python")[
|
|
250
|
+
"License-Expression"
|
|
251
|
+
]
|
|
252
|
+
}
|
|
253
|
+
],
|
|
254
|
+
"name": "csspin-python",
|
|
255
|
+
"supplier": {
|
|
256
|
+
"name": "CONTACT Software GmbH",
|
|
257
|
+
"url": [
|
|
258
|
+
"https://www.contact-software.com/",
|
|
259
|
+
"https://pypi.org/project/csspin-python/",
|
|
260
|
+
],
|
|
261
|
+
},
|
|
262
|
+
"type": "application",
|
|
263
|
+
"version": _pkg_version("csspin-python"),
|
|
264
|
+
}
|
|
265
|
+
]
|
|
266
|
+
},
|
|
267
|
+
}
|
|
268
|
+
direct_dep_names = {Requirement(dep).name.lower() for dep in third_party_deps}
|
|
269
|
+
direct_dep_refs = sorted(
|
|
270
|
+
comp["bom-ref"]
|
|
271
|
+
for comp in sbom_json.get("components", [])
|
|
272
|
+
if comp.get("name", "").lower() in direct_dep_names and "bom-ref" in comp
|
|
273
|
+
)
|
|
274
|
+
dependencies = sbom_json.get("dependencies", [])
|
|
275
|
+
dependencies.append({"dependsOn": direct_dep_refs, "ref": primary_ref})
|
|
276
|
+
sbom_json["dependencies"] = dependencies
|
|
277
|
+
|
|
278
|
+
|
|
178
279
|
def _collect_thirdparty_deps(requires_dist: list, python_version: str) -> set[str]:
|
|
179
280
|
"""Extract 'thirdparty' dependency specifiers from project metadata."""
|
|
180
281
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: csspin-python
|
|
3
|
-
Version: 4.1.
|
|
3
|
+
Version: 4.1.0rc2
|
|
4
4
|
Summary: Plugin-package for csspin providing Python related plugins
|
|
5
5
|
Author-email: CONTACT Software GmbH <info@contact-software.com>
|
|
6
6
|
Maintainer-email: Waleri Enns <waleri.enns@contact-software.com>, Benjamin Thomas Schwertfeger <benjaminthomas.schwertfeger@contact-software.com>, Fabian Hafer <fabian.hafer@contact-software.com>
|
|
@@ -9,15 +9,17 @@ csspin_python/playwright_schema.yaml,sha256=TSeR16YHa7m7bfO59F2eMV-jXcglluTJdEpU
|
|
|
9
9
|
csspin_python/pytest.py,sha256=N9YaU_ouQab0PFPf46HLE7Vg4JeoZW4dzVD7EevqJ1U,4573
|
|
10
10
|
csspin_python/pytest_schema.yaml,sha256=tzXtdF6MvGC9v59EVRJFfLeMMHqPsXcFXy2zJtRECBI,1535
|
|
11
11
|
csspin_python/python.py,sha256=yqH1eG6mqRJomu-F99cB74PFaUzbSoG_dhpUjYwr1xE,37725
|
|
12
|
-
csspin_python/python_sbom.py,sha256=
|
|
12
|
+
csspin_python/python_sbom.py,sha256=8NVZ6uVUz7rU3VhEoXJ_wHLCdv4BIV6Zjz1ZRa9nQhc,10488
|
|
13
13
|
csspin_python/python_sbom_schema.yaml,sha256=VCxY9E2AG_fS00dvIYe6Fbvz1maPjpY0zyZK1Z0wJu4,538
|
|
14
14
|
csspin_python/python_schema.yaml,sha256=pgVVjByUYjxQWek7aFmjQzRwmq2ROLvHYgwGPMrT9sM,6351
|
|
15
15
|
csspin_python/radon.py,sha256=uFqm6FEi5oWj-_XVaAm3s9cam0cUmr1_FwRf40K6xWs,1876
|
|
16
16
|
csspin_python/radon_schema.yaml,sha256=rlRzXw5z4XbjOVznRiUxWGP4E9hx1Jm-gGw1iQiYzE0,548
|
|
17
17
|
csspin_python/uv_provisioner.py,sha256=1e-_Sb39JrqNWyaUNeBX59R5tutXLJ1ZsT7urCN1U0I,6044
|
|
18
18
|
csspin_python/uv_provisioner_schema.yaml,sha256=Y8ZNC2OMnhR8Us3WUXAXK9hMjqGWAKFJB2puX4X5XNQ,727
|
|
19
|
-
csspin_python-4.1.
|
|
20
|
-
csspin_python-4.1.
|
|
21
|
-
csspin_python-4.1.
|
|
22
|
-
csspin_python-4.1.
|
|
23
|
-
csspin_python-4.1.
|
|
19
|
+
csspin_python-4.1.0rc2.dist-info/licenses/LICENSE,sha256=4MAecetnRTQw5DlHtiikDSzKWO1xVLwzM5_DsPMYlnE,10172
|
|
20
|
+
csspin_python-4.1.0rc2.dist-info/METADATA,sha256=v7TeI32ffR2KghQaNg2Yi1nXefTReG2Pr6iI3D8LKM0,5257
|
|
21
|
+
csspin_python-4.1.0rc2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
22
|
+
csspin_python-4.1.0rc2.dist-info/scm_file_list.json,sha256=Q47_SEZYR492yYYWq_DlPPp-64AFlvHaOz7APY0i8j0,2902
|
|
23
|
+
csspin_python-4.1.0rc2.dist-info/scm_version.json,sha256=r-bmiPdloFp64jPWB2hS3VjSJvF_ljCIL5y985ukLKY,163
|
|
24
|
+
csspin_python-4.1.0rc2.dist-info/top_level.txt,sha256=QSeglMEGbFu1z4L6MCQYwo01NgL0KojWvC4rzgMQ8gU,14
|
|
25
|
+
csspin_python-4.1.0rc2.dist-info/RECORD,,
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files": [
|
|
3
|
+
"spinfile.yaml",
|
|
4
|
+
"README.rst",
|
|
5
|
+
".pre-commit-config.yaml",
|
|
6
|
+
"CONTRIBUTING.md",
|
|
7
|
+
"requirements-dev.txt",
|
|
8
|
+
"LICENSE",
|
|
9
|
+
".gitignore",
|
|
10
|
+
".gitlab-ci.yml",
|
|
11
|
+
".prettierrc.yaml",
|
|
12
|
+
"pyproject.toml",
|
|
13
|
+
".readthedocs.yaml",
|
|
14
|
+
"doc/virtual_environment.rst",
|
|
15
|
+
"doc/development.rst",
|
|
16
|
+
"doc/relnotes.rst",
|
|
17
|
+
"doc/index.rst",
|
|
18
|
+
"doc/links.rst",
|
|
19
|
+
"doc/conf.py",
|
|
20
|
+
"doc/installation.rst",
|
|
21
|
+
"doc/plugins/playwright.rst",
|
|
22
|
+
"doc/plugins/uv_provisioner.rst",
|
|
23
|
+
"doc/plugins/debugpy.rst",
|
|
24
|
+
"doc/plugins/devpi.rst",
|
|
25
|
+
"doc/plugins/python.rst",
|
|
26
|
+
"doc/plugins/python_sbom.rst",
|
|
27
|
+
"doc/plugins/pytest.rst",
|
|
28
|
+
"doc/plugins/behave.rst",
|
|
29
|
+
"doc/plugins/radon.rst",
|
|
30
|
+
"tests/conftest.py",
|
|
31
|
+
"tests/unit/test_uv_provisioner.py",
|
|
32
|
+
"tests/unit/test_python_sbom.py",
|
|
33
|
+
"tests/unit/test_python.py",
|
|
34
|
+
"tests/integration/test_provisioning.py",
|
|
35
|
+
"tests/integration/conftest.py",
|
|
36
|
+
"tests/integration/fixtures/activation_script/test_env.ps1",
|
|
37
|
+
"tests/integration/fixtures/activation_script/test_env.sh",
|
|
38
|
+
"tests/integration/fixtures/activation_script/plugins/dummy.py",
|
|
39
|
+
"tests/integration/yamls/pytest.yaml",
|
|
40
|
+
"tests/integration/yamls/uv_provisioner_use.yaml",
|
|
41
|
+
"tests/integration/yamls/pytest_with_playwright.yaml",
|
|
42
|
+
"tests/integration/yamls/python_version.yaml",
|
|
43
|
+
"tests/integration/yamls/playwright.yaml",
|
|
44
|
+
"tests/integration/yamls/devpi.yaml",
|
|
45
|
+
"tests/integration/yamls/python_use.yaml",
|
|
46
|
+
"tests/integration/yamls/behave.yaml",
|
|
47
|
+
"tests/integration/yamls/radon.yaml",
|
|
48
|
+
"tests/integration/yamls/python_activation_scripts.yaml",
|
|
49
|
+
"tests/integration/yamls/uv_provisioner.yaml",
|
|
50
|
+
"tests/acceptance/test_aws_auth.py",
|
|
51
|
+
"tests/acceptance/python_sbom/spinfile.yaml",
|
|
52
|
+
"tests/acceptance/python_sbom/test_sbom.py",
|
|
53
|
+
"tests/acceptance/python_sbom/pyproject.toml",
|
|
54
|
+
"tests/acceptance/python_constraints/test_constraints.py",
|
|
55
|
+
"tests/acceptance/python_constraints/spinfile.yaml",
|
|
56
|
+
"tests/acceptance/python_constraints/constraints.txt",
|
|
57
|
+
"tests/acceptance/yamls/python_aws_auth.yaml",
|
|
58
|
+
"src/csspin_python/debugpy_schema.yaml",
|
|
59
|
+
"src/csspin_python/devpi_schema.yaml",
|
|
60
|
+
"src/csspin_python/uv_provisioner_schema.yaml",
|
|
61
|
+
"src/csspin_python/pytest.py",
|
|
62
|
+
"src/csspin_python/playwright_schema.yaml",
|
|
63
|
+
"src/csspin_python/behave.py",
|
|
64
|
+
"src/csspin_python/pytest_schema.yaml",
|
|
65
|
+
"src/csspin_python/uv_provisioner.py",
|
|
66
|
+
"src/csspin_python/radon.py",
|
|
67
|
+
"src/csspin_python/python_sbom_schema.yaml",
|
|
68
|
+
"src/csspin_python/python_sbom.py",
|
|
69
|
+
"src/csspin_python/devpi.py",
|
|
70
|
+
"src/csspin_python/debugpy.py",
|
|
71
|
+
"src/csspin_python/playwright.py",
|
|
72
|
+
"src/csspin_python/behave_schema.yaml",
|
|
73
|
+
"src/csspin_python/python.py",
|
|
74
|
+
"src/csspin_python/python_schema.yaml",
|
|
75
|
+
"src/csspin_python/radon_schema.yaml",
|
|
76
|
+
".gitlab/merge_request_templates/default.md"
|
|
77
|
+
]
|
|
78
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|