python-sbom 0.1.1__tar.gz

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.
Files changed (155) hide show
  1. python_sbom-0.1.1/.git_archival.txt +3 -0
  2. python_sbom-0.1.1/.gitattributes +3 -0
  3. python_sbom-0.1.1/.github/CODEOWNERS +1 -0
  4. python_sbom-0.1.1/.github/CONTRIBUTING.md +18 -0
  5. python_sbom-0.1.1/.github/FUNDING.yaml +2 -0
  6. python_sbom-0.1.1/.github/SECURITY.md +47 -0
  7. python_sbom-0.1.1/.github/THREAT_MODEL.md +78 -0
  8. python_sbom-0.1.1/.github/dependabot.yaml +30 -0
  9. python_sbom-0.1.1/.github/release.yaml +5 -0
  10. python_sbom-0.1.1/.github/workflows/check.yaml +62 -0
  11. python_sbom-0.1.1/.github/workflows/codeql.yaml +22 -0
  12. python_sbom-0.1.1/.github/workflows/news.yaml +30 -0
  13. python_sbom-0.1.1/.github/workflows/prepare-release.yaml +34 -0
  14. python_sbom-0.1.1/.github/workflows/release.yaml +56 -0
  15. python_sbom-0.1.1/.github/workflows/scorecard.yaml +26 -0
  16. python_sbom-0.1.1/.github/workflows/upgrade-release-tools.yaml +17 -0
  17. python_sbom-0.1.1/.gitignore +6 -0
  18. python_sbom-0.1.1/.pre-commit-config.yaml +88 -0
  19. python_sbom-0.1.1/.python-sbom-revision +1 -0
  20. python_sbom-0.1.1/.readthedocs.yaml +8 -0
  21. python_sbom-0.1.1/CONTRIBUTING.md +5 -0
  22. python_sbom-0.1.1/LICENSE +18 -0
  23. python_sbom-0.1.1/PKG-INFO +95 -0
  24. python_sbom-0.1.1/README.md +51 -0
  25. python_sbom-0.1.1/bench/__init__.py +1 -0
  26. python_sbom-0.1.1/bench/__main__.py +337 -0
  27. python_sbom-0.1.1/bench/_cases.py +238 -0
  28. python_sbom-0.1.1/bench/_fixtures.py +319 -0
  29. python_sbom-0.1.1/docs/__init__.py +1 -0
  30. python_sbom-0.1.1/docs/_static/changelog-toc.css +12 -0
  31. python_sbom-0.1.1/docs/changelog.d/.gitkeep +0 -0
  32. python_sbom-0.1.1/docs/changelog.rst +35 -0
  33. python_sbom-0.1.1/docs/cli.rst +5 -0
  34. python_sbom-0.1.1/docs/conf.py +133 -0
  35. python_sbom-0.1.1/docs/contributing.rst +118 -0
  36. python_sbom-0.1.1/docs/explanation.rst +130 -0
  37. python_sbom-0.1.1/docs/extend.rst +113 -0
  38. python_sbom-0.1.1/docs/index.rst +43 -0
  39. python_sbom-0.1.1/docs/py_api.rst +87 -0
  40. python_sbom-0.1.1/docs/usage.rst +177 -0
  41. python_sbom-0.1.1/hatch_build.py +45 -0
  42. python_sbom-0.1.1/pylock.toml +3124 -0
  43. python_sbom-0.1.1/pyproject.toml +277 -0
  44. python_sbom-0.1.1/src/python_sbom/__init__.py +46 -0
  45. python_sbom-0.1.1/src/python_sbom/__main__.py +10 -0
  46. python_sbom-0.1.1/src/python_sbom/_cli/__init__.py +1 -0
  47. python_sbom-0.1.1/src/python_sbom/_cli/_archive.py +38 -0
  48. python_sbom-0.1.1/src/python_sbom/_cli/_embed.py +28 -0
  49. python_sbom-0.1.1/src/python_sbom/_cli/_main.py +28 -0
  50. python_sbom-0.1.1/src/python_sbom/_cli/_parser.py +144 -0
  51. python_sbom-0.1.1/src/python_sbom/_cli/_spdx.py +61 -0
  52. python_sbom-0.1.1/src/python_sbom/_cli/_validate.py +40 -0
  53. python_sbom-0.1.1/src/python_sbom/_collect/__init__.py +1 -0
  54. python_sbom-0.1.1/src/python_sbom/_collect/_archive.py +94 -0
  55. python_sbom-0.1.1/src/python_sbom/_collect/_content.py +109 -0
  56. python_sbom-0.1.1/src/python_sbom/_collect/_facts.py +155 -0
  57. python_sbom-0.1.1/src/python_sbom/_collect/_revision.py +93 -0
  58. python_sbom-0.1.1/src/python_sbom/_collect/_settings.py +82 -0
  59. python_sbom-0.1.1/src/python_sbom/_collect/_source.py +54 -0
  60. python_sbom-0.1.1/src/python_sbom/_collect/_wheel.py +55 -0
  61. python_sbom-0.1.1/src/python_sbom/_errors.py +15 -0
  62. python_sbom-0.1.1/src/python_sbom/_formats/__init__.py +1 -0
  63. python_sbom-0.1.1/src/python_sbom/_formats/_json.py +18 -0
  64. python_sbom-0.1.1/src/python_sbom/_formats/_spdx.py +263 -0
  65. python_sbom-0.1.1/src/python_sbom/_generate/__init__.py +1 -0
  66. python_sbom-0.1.1/src/python_sbom/_generate/_archive.py +84 -0
  67. python_sbom-0.1.1/src/python_sbom/_generate/_embed.py +74 -0
  68. python_sbom-0.1.1/src/python_sbom/_model/__init__.py +1 -0
  69. python_sbom-0.1.1/src/python_sbom/_model/_archive.py +117 -0
  70. python_sbom-0.1.1/src/python_sbom/_model/_bom.py +137 -0
  71. python_sbom-0.1.1/src/python_sbom/_model/_components.py +182 -0
  72. python_sbom-0.1.1/src/python_sbom/_model/_content.py +128 -0
  73. python_sbom-0.1.1/src/python_sbom/_model/_document.py +100 -0
  74. python_sbom-0.1.1/src/python_sbom/_model/_graph.py +64 -0
  75. python_sbom-0.1.1/src/python_sbom/_model/_identity.py +53 -0
  76. python_sbom-0.1.1/src/python_sbom/_model/_inputs.py +141 -0
  77. python_sbom-0.1.1/src/python_sbom/_model/_iri.py +96 -0
  78. python_sbom-0.1.1/src/python_sbom/_model/_licenses.py +58 -0
  79. python_sbom-0.1.1/src/python_sbom/_model/_markers.py +61 -0
  80. python_sbom-0.1.1/src/python_sbom/_model/_metadata.py +100 -0
  81. python_sbom-0.1.1/src/python_sbom/_model/_names.py +90 -0
  82. python_sbom-0.1.1/src/python_sbom/_model/_tools.py +48 -0
  83. python_sbom-0.1.1/src/python_sbom/_model/_types.py +281 -0
  84. python_sbom-0.1.1/src/python_sbom/_model/_wheel.py +89 -0
  85. python_sbom-0.1.1/src/python_sbom/_validate/__init__.py +1 -0
  86. python_sbom-0.1.1/src/python_sbom/_validate/_artifact.py +55 -0
  87. python_sbom-0.1.1/src/python_sbom/_validate/_document.py +221 -0
  88. python_sbom-0.1.1/src/python_sbom/_validate/_licenses.py +104 -0
  89. python_sbom-0.1.1/src/python_sbom/_validate/_limits.py +27 -0
  90. python_sbom-0.1.1/src/python_sbom/_validate/_schemas.py +124 -0
  91. python_sbom-0.1.1/src/python_sbom/_validate/schemas/LICENSE +201 -0
  92. python_sbom-0.1.1/src/python_sbom/_validate/schemas/bom-1.6.schema.json +5699 -0
  93. python_sbom-0.1.1/src/python_sbom/_validate/schemas/jsf-0.82.schema.json +240 -0
  94. python_sbom-0.1.1/src/python_sbom/_validate/schemas/spdx.schema.json +1662 -0
  95. python_sbom-0.1.1/src/python_sbom/_wheel/__init__.py +1 -0
  96. python_sbom-0.1.1/src/python_sbom/_wheel/_bundled.py +142 -0
  97. python_sbom-0.1.1/src/python_sbom/_wheel/_reader.py +59 -0
  98. python_sbom-0.1.1/src/python_sbom/_wheel/_record.py +55 -0
  99. python_sbom-0.1.1/src/python_sbom/_wheel/_write.py +103 -0
  100. python_sbom-0.1.1/src/python_sbom/_wheel/_zip.py +94 -0
  101. python_sbom-0.1.1/src/python_sbom/hatch/__init__.py +9 -0
  102. python_sbom-0.1.1/src/python_sbom/hatch/_hook.py +150 -0
  103. python_sbom-0.1.1/src/python_sbom/hatch/hooks.py +18 -0
  104. python_sbom-0.1.1/src/python_sbom/py.typed +0 -0
  105. python_sbom-0.1.1/src/python_sbom/version.py +24 -0
  106. python_sbom-0.1.1/tests/_cli/conftest.py +34 -0
  107. python_sbom-0.1.1/tests/_cli/test_archive.py +134 -0
  108. python_sbom-0.1.1/tests/_cli/test_embed.py +97 -0
  109. python_sbom-0.1.1/tests/_cli/test_main.py +49 -0
  110. python_sbom-0.1.1/tests/_cli/test_parser.py +27 -0
  111. python_sbom-0.1.1/tests/_cli/test_spdx.py +102 -0
  112. python_sbom-0.1.1/tests/_cli/test_validate.py +63 -0
  113. python_sbom-0.1.1/tests/_collect/test_archive.py +31 -0
  114. python_sbom-0.1.1/tests/_collect/test_content.py +145 -0
  115. python_sbom-0.1.1/tests/_collect/test_facts.py +254 -0
  116. python_sbom-0.1.1/tests/_collect/test_revision.py +197 -0
  117. python_sbom-0.1.1/tests/_collect/test_settings.py +80 -0
  118. python_sbom-0.1.1/tests/_collect/test_source.py +80 -0
  119. python_sbom-0.1.1/tests/_collect/test_wheel.py +112 -0
  120. python_sbom-0.1.1/tests/_formats/test_spdx.py +305 -0
  121. python_sbom-0.1.1/tests/_generate/pinned/embed-members.json +100 -0
  122. python_sbom-0.1.1/tests/_generate/pinned/embed.cdx.json +583 -0
  123. python_sbom-0.1.1/tests/_generate/test_archive.py +299 -0
  124. python_sbom-0.1.1/tests/_generate/test_embed.py +149 -0
  125. python_sbom-0.1.1/tests/_model/test_bom.py +395 -0
  126. python_sbom-0.1.1/tests/_model/test_components.py +436 -0
  127. python_sbom-0.1.1/tests/_model/test_content.py +34 -0
  128. python_sbom-0.1.1/tests/_model/test_document.py +146 -0
  129. python_sbom-0.1.1/tests/_model/test_identity.py +56 -0
  130. python_sbom-0.1.1/tests/_model/test_iri.py +47 -0
  131. python_sbom-0.1.1/tests/_model/test_licenses.py +28 -0
  132. python_sbom-0.1.1/tests/_model/test_markers.py +72 -0
  133. python_sbom-0.1.1/tests/_model/test_metadata.py +115 -0
  134. python_sbom-0.1.1/tests/_model/test_tools.py +66 -0
  135. python_sbom-0.1.1/tests/_seeder/LICENSE +3 -0
  136. python_sbom-0.1.1/tests/_seeder/hatch_build.py +49 -0
  137. python_sbom-0.1.1/tests/_seeder/pyproject.toml +29 -0
  138. python_sbom-0.1.1/tests/_seeder/src/seeder/__init__.py +0 -0
  139. python_sbom-0.1.1/tests/_seeder/src/seeder/embed/__init__.py +10 -0
  140. python_sbom-0.1.1/tests/_validate/test_artifact.py +156 -0
  141. python_sbom-0.1.1/tests/_validate/test_document.py +325 -0
  142. python_sbom-0.1.1/tests/_validate/test_licenses.py +144 -0
  143. python_sbom-0.1.1/tests/_validate/test_schemas.py +220 -0
  144. python_sbom-0.1.1/tests/_wheel/test_bundled.py +281 -0
  145. python_sbom-0.1.1/tests/_wheel/test_reader.py +36 -0
  146. python_sbom-0.1.1/tests/_wheel/test_record.py +45 -0
  147. python_sbom-0.1.1/tests/_wheel/test_write.py +290 -0
  148. python_sbom-0.1.1/tests/_wheel/test_zip.py +82 -0
  149. python_sbom-0.1.1/tests/conftest.py +196 -0
  150. python_sbom-0.1.1/tests/hatch/conftest.py +79 -0
  151. python_sbom-0.1.1/tests/hatch/test_hook.py +246 -0
  152. python_sbom-0.1.1/tests/test_no_hatchling.py +14 -0
  153. python_sbom-0.1.1/tests/test_public_api.py +151 -0
  154. python_sbom-0.1.1/tests/test_version.py +9 -0
  155. python_sbom-0.1.1/tox.toml +163 -0
@@ -0,0 +1,3 @@
1
+ node: $Format:%H$
2
+ node-date: $Format:%cI$
3
+ describe-name: $Format:%(describe:tags=true,match=*[0-9]*)$
@@ -0,0 +1,3 @@
1
+ .git_archival.txt export-subst
2
+ tests/_generate/pinned/** -text
3
+ src/python_sbom/_validate/schemas/** -text
@@ -0,0 +1 @@
1
+ * @gaborbernat
@@ -0,0 +1,18 @@
1
+ # Contributing
2
+
3
+ Add a `docs/changelog.d/+short-name.<type>.rst` fragment for a change that affects package users. Choose a unique short
4
+ name; the release workflow links the fragment to its merged squash pull request and credits the human author. A pull
5
+ request number is not needed in the filename.
6
+
7
+ Use `feature` for Added, `change` for Changes, `bugfix` for Fixed, and `doc` for Documentation. Use `breaking`,
8
+ `security`, `performance`, `deprecation`, `removal`, or `packaging` only when the change affects users in that way. A
9
+ supported Python version added or removed is a `feature` and results in a minor release.
10
+
11
+ Use `skip news` for internal maintenance without a user-facing change. The label may be added after opening the pull
12
+ request. Dependabot and maintenance bot pull requests without a fragment receive it automatically; a bot pull request
13
+ with a fragment still needs the news check to pass. For a change written by more than one person, put all human authors
14
+ in a `by` credit. Do not list bots or reviewers.
15
+
16
+ The documentation preview shows fragments under Unreleased. The versioned `docs/changelog.rst` changes only during
17
+ release preparation. The release workflow chooses a version from the fragment types, runs `prek` on the assembled
18
+ changelog, and publishes GitHub notes from the tagged changelog after PyPI succeeds.
@@ -0,0 +1,2 @@
1
+ github: gaborbernat
2
+ thanks_dev: u/gh/gaborbernat
@@ -0,0 +1,47 @@
1
+ # Security policy
2
+
3
+ python-sbom writes a CycloneDX SBOM into wheels, from a hatchling build hook or from `python-sbom embed`, which rewrites
4
+ a wheel another build backend built. `python-sbom archive` describes another zip archive a project publishes, such as a
5
+ zipapp, and `python-sbom spdx` and `validate` render or check an SBOM. This policy says how to report a security problem
6
+ and how we decide whether a report is one.
7
+
8
+ ## Reporting a vulnerability
9
+
10
+ **Do not open a public issue for a security problem.** Use
11
+ [GitHub's private vulnerability reporting](https://github.com/tox-dev/python-sbom/security/advisories/new). If neither
12
+ works for you, email [gaborjbernat@gmail.com](mailto:gaborjbernat@gmail.com).
13
+
14
+ Include what the attacker controls and what they gain, the steps or a proof of concept, and the versions and platform
15
+ you tested. Write the report yourself; unreviewed tool or model output takes longer to triage.
16
+
17
+ ## What to expect
18
+
19
+ Volunteers maintain python-sbom, with no on-call rotation. We aim to acknowledge a report within **5 working days** and
20
+ assess it within **15 working days**. Fixes are developed in a private fork attached to the draft advisory, ship in a
21
+ new release, and the advisory, with a CVE requested through GitHub, goes out with that release. The default embargo is
22
+ **90 days** from the report; we agree any change with the reporter. We credit reporters in the advisory under the name
23
+ they choose, or not at all. There is no bug bounty.
24
+
25
+ ## Supported versions
26
+
27
+ Only the latest release receives fixes. Confirm the problem reproduces on it before reporting.
28
+
29
+ ## Vulnerability or bug
30
+
31
+ A report is a vulnerability when an attacker who controls only an input that the [threat model](THREAT_MODEL.md) lists
32
+ as untrusted breaks a guarantee python-sbom makes. A report that first needs control of something the threat model
33
+ trusts, such as the project source, its `pyproject.toml` or the build environment, is a bug: we fix it in the open and
34
+ publish no advisory. Classify by who supplies the dangerous input, not by how bad the outcome sounds.
35
+
36
+ **In scope**
37
+
38
+ - A wheel bundled in a wheel or in an archive given to `python-sbom archive`, or an SBOM given to `python-sbom spdx` or
39
+ `validate`, that makes python-sbom write outside its output file or the wheel or archive it rewrites, or run code.
40
+ - An SBOM that misstates what a wheel holds in a way its inputs cannot explain, such as a wrong hash.
41
+
42
+ **Out of scope**
43
+
44
+ - Anything hatchling's own build trusts.
45
+ - Vulnerabilities in the packages an SBOM lists; report those to their projects.
46
+
47
+ If you cannot tell which side a finding falls on, report it privately and we will decide.
@@ -0,0 +1,78 @@
1
+ # Threat model
2
+
3
+ This model says what python-sbom trusts, where untrusted input enters, and which guarantees hold at each boundary, so a
4
+ report can be classified as a vulnerability or a bug. [SECURITY.md](SECURITY.md) covers reporting; if the two disagree,
5
+ SECURITY.md wins and we fix this file.
6
+
7
+ ## How python-sbom works
8
+
9
+ The build hook runs inside a hatchling wheel build. It reads the wheel's core metadata, the project's license files, the
10
+ wheels the project bundles and the installed distributions of the build environment, and writes a CycloneDX document
11
+ that it writes into the built wheel. `python-sbom embed` does the same for a wheel another backend built,
12
+ `python-sbom archive` describes another zip archive such as a zipapp, and `python-sbom spdx` and `validate` read
13
+ CycloneDX documents and write or check them.
14
+
15
+ ```mermaid
16
+ flowchart LR
17
+ project[("Project source, pyproject.toml<br/>(trusted)")] --> hook["python-sbom"]
18
+ env[("Build environment<br/>(trusted)")] --> hook
19
+ bundled[("Bundled wheels<br/>(third-party content)")] -->|"metadata, RECORD, vendor list"| hook
20
+ sbom[("SBOM files given to spdx, validate")] -->|"untrusted JSON"| hook
21
+ hook --> wheel[("Wheel")]
22
+ classDef trusted fill:#cfe4ff,stroke:#1f6feb,color:#0b1f3a;
23
+ classDef boundary fill:#ffe3a3,stroke:#d29200,color:#3a2c00;
24
+ class project,env,hook,wheel trusted;
25
+ class bundled,sbom boundary;
26
+ ```
27
+
28
+ ## Trusted
29
+
30
+ An attacker who controls any of these already has what python-sbom could give them:
31
+
32
+ - The project source, its `pyproject.toml`, the build hook script, and the module the `content-provider` option or
33
+ `--content-provider` names: python-sbom imports that module from the project directory, so `embed` and `archive` run
34
+ code from `--root`.
35
+ - The git checkout that tracks the project, whose `HEAD` python-sbom reads. A repository that merely contains the
36
+ project directory is not trusted: git is used only when it tracks the project, and runs with `core.fsmonitor` off.
37
+ - The build environment and the build backend.
38
+ - The wheel handed to `python-sbom embed` and the archive handed to `python-sbom archive`, which the project's own build
39
+ produced.
40
+
41
+ ## Untrusted inputs
42
+
43
+ | Input | Comes from | What python-sbom guarantees |
44
+ | --------------------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
45
+ | Metadata and `RECORD` of bundled wheels | Upstream projects | Read as text with capped sizes, copied as JSON strings; every member is checked against its `RECORD` row, duplicate members are refused, nothing is executed. |
46
+ | `_vendor/vendor.txt` of bundled wheels | Upstream projects | Each line must pin one exact version, or the build fails. |
47
+ | SBOM files given to `spdx`, `validate` | Whoever produced the file | Parsed as JSON only, with sizes capped by the component cap; `validate` refuses nesting past 128 levels and reports problems instead of raising them; `spdx` reports input too deep to parse as an error and writes the one file named on the command line. |
48
+
49
+ ## Threats and controls
50
+
51
+ | STRIDE | Threat | Control |
52
+ | ---------------------- | ---------------------------------------------------------- | ------------------------------------------------------------- |
53
+ | Tampering | A bundled wheel's metadata injects JSON into the document. | The document is serialized with `json.dumps`. |
54
+ | Tampering | A bundled wheel changes and the SBOM still lists it. | Each bundled wheel is hashed from its bytes at build time. |
55
+ | Elevation of privilege | A bundled wheel runs code during the build. | Wheels are read as zip archives; nothing in them is imported. |
56
+
57
+ ## Release chain
58
+
59
+ Releases run on the shared [tox-dev/github-workflows](https://github.com/tox-dev/github-workflows) pipeline. A release
60
+ App, not a person, pushes the release commit and tag; the build installs its tools from a hash-checked `pylock.toml`
61
+ resolved with a seven-day upload cooldown by default; PyPI receives the files through
62
+ [trusted publishing](https://docs.pypi.org/trusted-publishers/) with attestations, and the GitHub Release is immutable.
63
+ Every workflow pins third-party actions to a commit SHA.
64
+
65
+ ## Severity
66
+
67
+ - **High:** a bundled wheel or SBOM file that makes python-sbom run code or write outside its output.
68
+ - **Medium:** an SBOM that misstates a hash, a license or the contents of a wheel.
69
+ - **Low:** a crash on a malformed bundled wheel or SBOM file instead of a clean error.
70
+
71
+ ## Accepted risks
72
+
73
+ - The SBOM reports what the build environment holds; it does not check that environment against a lock.
74
+
75
+ ## Keeping this current
76
+
77
+ Revisit this model when python-sbom gains a new input, a new way to run code, or a new place it writes to, and whenever
78
+ an advisory is published.
@@ -0,0 +1,30 @@
1
+ version: 2
2
+ multi-ecosystem-groups:
3
+ dependencies:
4
+ schedule:
5
+ interval: weekly
6
+ day: tuesday
7
+ labels:
8
+ - dependencies
9
+ - skip news
10
+ updates:
11
+ - package-ecosystem: github-actions
12
+ directory: /
13
+ patterns:
14
+ - '*'
15
+ multi-ecosystem-group: dependencies
16
+ labels:
17
+ - dependencies
18
+ - skip news
19
+ cooldown:
20
+ default-days: 7
21
+ - package-ecosystem: pre-commit
22
+ directory: /
23
+ patterns:
24
+ - '*'
25
+ multi-ecosystem-group: dependencies
26
+ labels:
27
+ - dependencies
28
+ - skip news
29
+ cooldown:
30
+ default-days: 7
@@ -0,0 +1,5 @@
1
+ changelog:
2
+ exclude:
3
+ authors:
4
+ - dependabot[bot]
5
+ - pre-commit-ci[bot]
@@ -0,0 +1,62 @@
1
+ name: 🧪 check
2
+ on:
3
+ workflow_dispatch:
4
+ push:
5
+ branches: ["main"]
6
+ pull_request:
7
+ types: [opened, synchronize, reopened]
8
+ schedule:
9
+ - cron: "27 8 * * *"
10
+ concurrency:
11
+ group: ${{ github.workflow }}-${{ github.ref }}
12
+ cancel-in-progress: true
13
+ permissions:
14
+ contents: read
15
+ jobs:
16
+ test:
17
+ name: 🧪 test
18
+ if: github.event_name != 'schedule' || github.repository == 'tox-dev/python-sbom'
19
+ uses: tox-dev/github-workflows/.github/workflows/check.yaml@0c4cffd63033d38196319524d101caae1eef925b # 6.7.2
20
+ permissions:
21
+ contents: read
22
+ with:
23
+ include: |
24
+ [
25
+ {"env": "3.15", "python": "3.15"},
26
+ {"env": "3.14", "python": "3.14"},
27
+ {"env": "3.13", "python": "3.13"},
28
+ {"env": "3.12", "python": "3.12"},
29
+ {"env": "3.11", "python": "3.11"},
30
+ {"env": "3.15", "python": "3.15", "os": "windows-2025"},
31
+ {"env": "3.11", "python": "3.11", "os": "windows-2025"},
32
+ {"env": "3.15", "python": "3.15", "os": "macos-26"},
33
+ {"env": "dev"},
34
+ {"env": "docs"},
35
+ {"env": "fix"},
36
+ {"env": "type"},
37
+ {"env": "pkg_meta"}
38
+ ]
39
+ release:
40
+ name: 🏷️ Release dry run
41
+ uses: $/.github/workflows/prepare-release.yaml
42
+ permissions:
43
+ contents: read
44
+ pull-requests: read
45
+ with:
46
+ dry-run: true
47
+ hooks:
48
+ name: 🧹 Hooks under the cooldown
49
+ uses: tox-dev/github-workflows/.github/workflows/cooled-hooks.yaml@0c4cffd63033d38196319524d101caae1eef925b # 6.7.2
50
+ permissions:
51
+ contents: read
52
+ gate:
53
+ name: 🚦 ${{ github.event_name == 'pull_request' && 'Required checks pass' || 'Non-PR checks pass' }}
54
+ if: ${{ !cancelled() }} # codespell:ignore cancelled
55
+ needs: [test, release, hooks]
56
+ runs-on: ubuntu-26.04
57
+ permissions: {}
58
+ steps:
59
+ - name: 🚦 Require all needed jobs to pass
60
+ uses: tox-dev/github-workflows/required-checks@0c4cffd63033d38196319524d101caae1eef925b # 6.7.2
61
+ with:
62
+ needs: ${{ toJSON(needs) }}
@@ -0,0 +1,22 @@
1
+ name: 🛡️ CodeQL
2
+ on:
3
+ workflow_dispatch:
4
+ push:
5
+ branches: ["main"]
6
+ pull_request:
7
+ schedule:
8
+ - cron: "35 8 * * 2"
9
+ concurrency:
10
+ group: ${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: true
12
+ permissions: {}
13
+ jobs:
14
+ analyze:
15
+ name: 🛡️ Analyze
16
+ uses: tox-dev/github-workflows/.github/workflows/codeql.yaml@0c4cffd63033d38196319524d101caae1eef925b # 6.7.2
17
+ permissions:
18
+ contents: read
19
+ security-events: write
20
+ with:
21
+ languages: '["actions","python"]'
22
+ paths-ignore: '["tests"]'
@@ -0,0 +1,30 @@
1
+ name: Changelog
2
+ on:
3
+ pull_request:
4
+ types: [opened, reopened, synchronize, labeled, unlabeled]
5
+ permissions:
6
+ contents: read
7
+ pull-requests: read
8
+ jobs:
9
+ changelog:
10
+ name: 📝 News fragment
11
+ runs-on: ubuntu-26.04
12
+ steps:
13
+ - name: 📥 Checkout code
14
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
15
+ with:
16
+ fetch-depth: 0
17
+ persist-credentials: false
18
+ - name: 🐍 Install uv
19
+ uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
20
+ with:
21
+ version: latest-known
22
+ python-version: "3.15"
23
+ enable-cache: false
24
+ - name: 📦 Install locked release tools
25
+ uses: tox-dev/github-workflows/install-locked-tools@0c4cffd63033d38196319524d101caae1eef925b # 6.7.2
26
+ with:
27
+ groups: release-news
28
+ venv: ${{ runner.temp }}/release-tools
29
+ - name: 🔍 Validate changelog fragments
30
+ uses: tox-dev/github-workflows/validate-news@0c4cffd63033d38196319524d101caae1eef925b # 6.7.2
@@ -0,0 +1,34 @@
1
+ name: Prepare release
2
+ on:
3
+ workflow_dispatch:
4
+ inputs:
5
+ bump:
6
+ description: Version bump strategy
7
+ required: true
8
+ type: choice
9
+ options:
10
+ - auto
11
+ - major
12
+ - minor
13
+ - patch
14
+ default: auto
15
+ workflow_call:
16
+ inputs:
17
+ dry-run:
18
+ description: Assemble the release without pushing, for the pull request gate
19
+ type: boolean
20
+ required: true
21
+ permissions: {}
22
+ jobs:
23
+ prepare:
24
+ name: 🏷️ Prepare Towncrier release
25
+ uses: tox-dev/github-workflows/.github/workflows/prepare-towncrier-release.yaml@0c4cffd63033d38196319524d101caae1eef925b # 6.7.2
26
+ permissions:
27
+ contents: read
28
+ pull-requests: read
29
+ with:
30
+ bump: ${{ inputs.bump || 'auto' }}
31
+ dry-run: ${{ inputs.dry-run == true }}
32
+ release-auth-deployment: false
33
+ secrets:
34
+ RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
@@ -0,0 +1,56 @@
1
+ name: Release to PyPI
2
+ on:
3
+ push:
4
+ tags: ["*"]
5
+ permissions:
6
+ contents: read
7
+ concurrency:
8
+ group: release-${{ github.ref }}
9
+ cancel-in-progress: false
10
+ jobs:
11
+ build:
12
+ name: 🏗️ Build
13
+ uses: tox-dev/github-workflows/.github/workflows/validate-python-release.yaml@0c4cffd63033d38196319524d101caae1eef925b # 6.7.2
14
+ permissions:
15
+ contents: read
16
+ notes:
17
+ name: 📝 Render GitHub Release notes
18
+ uses: tox-dev/github-workflows/.github/workflows/release-notes.yaml@0c4cffd63033d38196319524d101caae1eef925b # 6.7.2
19
+ permissions:
20
+ contents: read
21
+ with:
22
+ tag: ${{ github.ref_name }}
23
+ changelog: docs/changelog.rst
24
+ artifact-url: https://pypi.org/project/python-sbom/${{ github.ref_name }}/
25
+ publish:
26
+ name: 🚀 Publish to PyPI
27
+ needs: [build, notes]
28
+ runs-on: ubuntu-26.04
29
+ environment:
30
+ name: release
31
+ url: https://pypi.org/project/python-sbom/${{ github.ref_name }}
32
+ permissions:
33
+ id-token: write
34
+ steps:
35
+ - name: 📩 Download distributions
36
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
37
+ with:
38
+ artifact-ids: ${{ needs.build.outputs.distributions-artifact-id }}
39
+ path: dist/
40
+ - name: 🚀 Publish to PyPI
41
+ uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
42
+ with:
43
+ attestations: true
44
+ packages-dir: dist
45
+ release:
46
+ name: 🚀 Publish GitHub Release
47
+ needs: [build, publish, notes]
48
+ uses: tox-dev/github-workflows/.github/workflows/github-release.yaml@0c4cffd63033d38196319524d101caae1eef925b # 6.7.2
49
+ permissions:
50
+ contents: write
51
+ with:
52
+ project: python-sbom
53
+ version: ${{ github.ref_name }}
54
+ tag: ${{ github.ref_name }}
55
+ artifact-pattern: python-package-distributions
56
+ notes-artifact-id: ${{ needs.notes.outputs.artifact-id }}
@@ -0,0 +1,26 @@
1
+ name: 🛡️ scorecard
2
+ on:
3
+ branch_protection_rule:
4
+ schedule:
5
+ - cron: "43 8 * * 2"
6
+ push:
7
+ branches: ["main"]
8
+ workflow_dispatch:
9
+ concurrency:
10
+ group: ${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: true
12
+ permissions: {}
13
+ jobs:
14
+ analysis:
15
+ name: 🛡️ Scorecard analysis
16
+ if: github.repository == 'tox-dev/python-sbom'
17
+ permissions:
18
+ actions: read # read workflow runs for the Packaging check
19
+ checks: read # read check runs for the CI-Tests and SAST checks
20
+ contents: read # check out and scan the repository
21
+ id-token: write # authenticate the results published to the scorecard API
22
+ issues: read # read issue activity for the Maintained check
23
+ pull-requests: read # read reviews for the Code-Review check
24
+ security-events: write # upload the SARIF results to code scanning
25
+ statuses: read # read commit statuses for the CI-Tests check
26
+ uses: tox-dev/github-workflows/.github/workflows/scorecard.yaml@0c4cffd63033d38196319524d101caae1eef925b # 6.7.2
@@ -0,0 +1,17 @@
1
+ name: Upgrade release tools
2
+ on:
3
+ workflow_dispatch:
4
+ schedule:
5
+ - cron: "51 8 * * 2"
6
+ permissions: {}
7
+ concurrency:
8
+ group: upgrade-release-tools
9
+ cancel-in-progress: false
10
+ jobs:
11
+ upgrade:
12
+ name: 🔀 Upgrade release tools
13
+ uses: tox-dev/github-workflows/.github/workflows/upgrade-release-tools.yaml@0c4cffd63033d38196319524d101caae1eef925b # 6.7.2
14
+ permissions:
15
+ contents: read
16
+ secrets:
17
+ RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
@@ -0,0 +1,6 @@
1
+ *.pyc
2
+ *.egg-info
3
+ dist/
4
+ .tox/
5
+ /src/python_sbom/version.py
6
+ /uv.lock
@@ -0,0 +1,88 @@
1
+ # prek runs lower priorities first and equal ones concurrently, so hooks sharing a priority never write the same file;
2
+ # pre-commit ignores the key and runs hooks in file order.
3
+ ci:
4
+ autoupdate_schedule: quarterly
5
+ default_language_version:
6
+ python: python3.15
7
+ # the CycloneDX schema files ship byte for byte as upstream released them
8
+ exclude: ^src/python_sbom/_validate/schemas/
9
+ repos:
10
+ - repo: https://github.com/pre-commit/pre-commit-hooks
11
+ rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
12
+ hooks:
13
+ - id: end-of-file-fixer
14
+ priority: 0
15
+ - id: trailing-whitespace
16
+ priority: 1
17
+ - repo: https://github.com/python-jsonschema/check-jsonschema
18
+ rev: a76b92851fc9eede9f9bc4073eb55202f34eb0df # frozen: 0.39.0
19
+ hooks:
20
+ - id: check-github-workflows
21
+ priority: 6
22
+ args: ["--verbose"]
23
+ - repo: https://github.com/codespell-project/codespell
24
+ rev: 57b21406f092110c18776e39b0bda50d37c945c8 # frozen: v2.4.3
25
+ hooks:
26
+ - id: codespell
27
+ priority: 6
28
+ - repo: https://github.com/tox-dev/tox-toml-fmt
29
+ rev: "5125f601c3e134347288c3d42f3b34298b7cf1be" # frozen: v1.11.1
30
+ hooks:
31
+ - id: tox-toml-fmt
32
+ priority: 2
33
+ - repo: https://github.com/tox-dev/pyproject-fmt
34
+ rev: "50e0f3aee9dc5747a5f173f79a7417403c18dda6" # frozen: v2.30.3
35
+ hooks:
36
+ - id: pyproject-fmt
37
+ priority: 2
38
+ - repo: https://github.com/astral-sh/ruff-pre-commit
39
+ rev: "25bcf95026b96e2dd3c0aec3090c3d6ef6ca2e6c" # frozen: v0.17.3
40
+ hooks:
41
+ - id: ruff-check
42
+ priority: 3
43
+ args: ["--fix", "--unsafe-fixes", "--exit-non-zero-on-fix"]
44
+ - repo: https://github.com/gaborbernat/simplint-pre-commit
45
+ rev: "c8719dca99c13381570bc69f8955c9448fc271b7" # frozen: v0.7.1
46
+ hooks:
47
+ - id: simplint
48
+ priority: 4
49
+ - repo: https://github.com/astral-sh/ruff-pre-commit
50
+ rev: "25bcf95026b96e2dd3c0aec3090c3d6ef6ca2e6c" # frozen: v0.17.3
51
+ hooks:
52
+ - id: ruff-format
53
+ priority: 5
54
+ - repo: https://github.com/google/yamlfmt
55
+ rev: "21ca5323a9c87ee37a434e0ca908efc0a89daa07" # frozen: v0.21.0
56
+ hooks:
57
+ - id: yamlfmt
58
+ priority: 2
59
+ - repo: https://github.com/executablebooks/mdformat
60
+ rev: 82912cdaea4fb830f751504486a7879c70526547 # frozen: 1.0.0
61
+ hooks:
62
+ - id: mdformat
63
+ priority: 2
64
+ args: ["--wrap", "120"]
65
+ additional_dependencies:
66
+ - mdformat-gfm==1.0.0
67
+ - mdformat-ruff==0.1.3
68
+ - repo: https://github.com/LilSpazJoekp/docstrfmt
69
+ rev: c1813841673cdcd6cf602dde1edb78a5d5e45235 # frozen: v2.2.1
70
+ hooks:
71
+ - id: docstrfmt
72
+ priority: 2
73
+ files: ^docs/(changelog|contributing)[.]rst$
74
+ args: ["-l", "120", "--preserve-adornments"]
75
+ additional_dependencies:
76
+ - "sphinx==9.2.0"
77
+ - repo: https://github.com/zizmorcore/zizmor-pre-commit
78
+ rev: c92afdf2c6ca336aaa8f39e2bd8d48f62dbd8e1f # frozen: v1.31.1
79
+ hooks:
80
+ - id: zizmor
81
+ priority: 6
82
+ args: ["--min-severity", "high"]
83
+ - repo: meta
84
+ hooks:
85
+ - id: check-hooks-apply
86
+ priority: 6
87
+ - id: check-useless-excludes
88
+ priority: 6
@@ -0,0 +1 @@
1
+ node: 0880f77d5e60335f23d569ad6a85e4b626990d84
@@ -0,0 +1,8 @@
1
+ version: 2
2
+ build:
3
+ os: ubuntu-lts-latest
4
+ tools: {}
5
+ commands:
6
+ - curl -LsSf https://astral.sh/uv/install.sh | sh
7
+ - ~/.local/bin/uv tool install tox --with tox-uv -p 3.15 --managed-python
8
+ - ~/.local/bin/tox run -e docs
@@ -0,0 +1,5 @@
1
+ # Contributing
2
+
3
+ ## Tests
4
+
5
+ Add a test for every new feature and bug fix. A change without a test needs a reason in the pull request description.
@@ -0,0 +1,18 @@
1
+ Permission is hereby granted, free of charge, to any person obtaining a
2
+ copy of this software and associated documentation files (the
3
+ "Software"), to deal in the Software without restriction, including
4
+ without limitation the rights to use, copy, modify, merge, publish,
5
+ distribute, sublicense, and/or sell copies of the Software, and to
6
+ permit persons to whom the Software is furnished to do so, subject to
7
+ the following conditions:
8
+
9
+ The above copyright notice and this permission notice shall be included
10
+ in all copies or substantial portions of the Software.
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
13
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
15
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
16
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
17
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
18
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.