* 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>
* 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.