Skip to content

Report declared licences, and emit CycloneDX licenses #12

Description

@richardmhope

There is no licence handling anywhere in src/ today. Two things make it worth adding.

The SBOM is incomplete without it. _component in src/icebergsca/report/cyclonedx.py:106 emits type, bom-ref, name, purl, version and custom properties — no licenses array. A lot of downstream SBOM tooling treats a licence-free BOM as near-worthless, and it is a field consumers specifically expect.

"Software composition analysis" sets the expectation. SCA conventionally covers vulnerabilities and licence compliance. Having renamed the category (#8), the absence is more conspicuous than it was.

The trap to avoid

Declared licence metadata is unreliable — PyPI classifiers disagreeing with the license field, dual licensing expressed a dozen ways, UNKNOWN in a meaningful fraction of packages, and metadata that does not match the LICENSE file actually shipped in the artefact.

So this must not become a compliance verdict. "All dependencies MIT ✅" derived from declared metadata is the licence-shaped version of the false clean everything else here is designed to prevent. The honest framing is what each package declares, and which packages declare nothing — with "undeclared" reported as prominently as any other unchecked state, not folded into a total. No pass/fail, no policy engine, at least not in the first cut.

None vs [] matters here as much as anywhere: "we could not fetch the metadata" and "the package declares no licence" are different facts and must not collapse.

Architectural cost, worth knowing before starting

registry/ is currently used only for range resolution (RegistryClient.versions, src/icebergsca/registry/client.py:103), so a lockfile-first scan can make zero registry calls today. Licences mean per-package metadata fetches on every scan, which brings:

  • a new cache namespace with its own freshness rules
  • a materially different network profile, including under --offline and --no-resolve
  • a new failure mode needing somewhere honest to go, rather than being silently dropped
  • the endpoints diverging per ecosystem (PyPI JSON, npm packument, crates.io, RubyGems, NuGet catalog, Maven POM) — the POM case overlaps with work MavenResolver already does

This is the most expensive item on the list, which is a fair reason to schedule it after #10 and #11.

Scope

  • Licence fields on the dependency model, distinguishing undeclared from unfetched
  • Per-ecosystem metadata fetch and cache namespace
  • licenses on CycloneDX components, using SPDX expression where the declaration parses and name where it does not
  • JSON report fields, documented in references/json-report.md
  • Some surfacing in table/csv, most likely a count of undeclared rather than a column
  • Explicit docs note that these are declared licences, unverified against artefact contents

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions