4 Commits
Author SHA1 Message Date
Mesut Oezdil 0310cbed6c fix(sbom): detect sha256 hashes in expression-form licenses in needs_fix (#1911)
* fix(sbom): detect sha256 hashes in expression-form licenses in needs_fix

CycloneDX allows licenses as either {"license": {"id": "..."}} or
{"expression": "..."}. needs_fix only checked the license form, so
expression entries with sha256 hashes were silently skipped.

Add expression-form check to needs_fix, mirroring the fix in
extract_licenses (#1898). Add tests covering both forms.

* fix(sbom): align license checks with current test layout

Reuse needs_fix from sbom:check so expression-form hashes are detected. Fold coverage into the existing SBOM test module and task introduced on main.

Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>

---------

Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
2026-08-11 18:45:22 +00:00
Mesut Oezdil 85c52bba85 fix(sbom): handle SPDX expression licenses in extract_licenses (#1898)
CycloneDX allows licenses as either {"license": {"id": "..."}} or
{"expression": "MIT OR Apache-2.0"}. The expression form was silently
dropped, producing an empty license field in the CSV output.
2026-06-22 17:36:24 -05:00
Mesut Oezdil f084eb39cb fix(sbom): release lock before sleeping in _rate_limit (#1896)
* fix(sbom): release lock before sleeping in _rate_limit

time.sleep was called inside the _rate_lock block, blocking all threads
from checking their own domain rate limit while one thread slept.
With _MAX_WORKERS=12 querying crates.io, npm, and pypi concurrently,
this made the thread pool effectively serial.

Move the sleep outside the lock so threads for different domains
can proceed concurrently.

* fix(sbom): reserve next slot in _rate_limit to prevent same-domain races

Use time.monotonic() and store next_req = max(now, last + interval) so
concurrent same-domain callers each get a unique future slot rather than
all sleeping the same amount. Add regression tests for same-domain
spacing and different-domain non-blocking.

* fix(sbom): wire rate-limit tests into test suite via test:sbom task

Rename test_resolve_licenses.py to resolve_licenses_test.py to match
repo *_test.py convention. Add test:sbom task to tasks/test.toml and
include it in the top-level test depends so mise run test picks it up.

* fix(sbom): add SPDX header, fix cross-domain regression test to exercise lock contention

* fix(sbom): replace zip with itertools.pairwise to satisfy ruff lint
2026-06-22 13:28:13 -05:00
John T. Myers 169655a0d8 feat(sbom): add SBOM generation, license resolution, and CSV export tooling (#239)
* feat(sbom): add SBOM generation, license resolution, and CSV export tooling

Add mise-integrated SBOM pipeline for container images using Syft.
Includes license resolution via crates.io/npm/PyPI APIs and CycloneDX
JSON to CSV conversion. Adds agent skill for on-demand SBOM operations.

Closes #237

* fix(sbom): chain task dependencies to run generate → resolve → csv sequentially

* fix(sbom): add concurrent license resolution, progress logging, and exclude dev artifacts

* feat(notices): add mise run notices to generate THIRD-PARTY-NOTICES with full license texts

Use cargo-about for Rust crate licenses and pip-licenses for Python
packages. Produces a single attribution file with per-package copyright
notices and full license text for open-source compliance.
2026-03-11 15:34:00 -07:00