Skip to content

feat(java): resolve JAR license URLs to SPDX IDs (Bundle-License, pom <url>)#10948

Open
DmitriyLewen wants to merge 8 commits into
aquasecurity:mainfrom
DmitriyLewen:jar-license-urls
Open

feat(java): resolve JAR license URLs to SPDX IDs (Bundle-License, pom <url>)#10948
DmitriyLewen wants to merge 8 commits into
aquasecurity:mainfrom
DmitriyLewen:jar-license-urls

Conversation

@DmitriyLewen

@DmitriyLewen DmitriyLewen commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Description

Java packages often declare their license only as a URL — in the OSGi Bundle-License manifest header or in the pom.xml <license><url> — instead of a name or an SPDX ID.
Until now such URLs were not usable, so these packages carried no license.

This PR builds a reverse "license URL → SPDX ID" index from the seeAlso field of the SPDX license list and uses it to resolve those URLs.

SPDX index (magefiles/spdx.go, pkg/licensing)

  • The generator now keeps seeAlso and embeds a normalized URL → SPDX-ID map; licenses.json maps each license ID to its normalized URLs and still doubles as the SPDX license ID list used for validation.
  • licensing.NormalizeLicenseURL canonicalizes a URL (scheme, www., trailing slash, document extension, opensource.org /licenses/ vs /license/, case) and unwraps web.archive.org snapshots down to the embedded URL.
  • A URL shared by different licenses is dropped as ambiguous; a URL shared only by +/-only/-or-later variants of one family is kept for the lexicographically smallest of those license IDs (which is the bare base when it is present, e.g. GPL-2.0), so the choice is deterministic and always a license that actually lists the URL.
  • expression.SPDXLicenseIDByURL performs the lookup and expects an already-normalized URL, mirroring SPDXLicenseID.

Java parsers

  • pom.xml: when a <license> has no <name>, fall back to its <url> resolved to an SPDX ID — for both the standalone pom parser and the pom.xml embedded in a JAR.
  • OSGi Bundle-License manifest header: unfold wrapped continuation lines (per the JAR File Specification / java.util.jar.Manifest), then for each comma-separated entry resolve its name as an SPDX ID and as a URL, and its ;link= attribute as a URL; entries that resolve to neither (free text, <<EXTERNAL>>) are skipped.
  • Unresolved URLs are skipped so a packed LICENSE file is still classified.
  • License priority for a JAR's own artifact: pom <name> → pom <url>Bundle-License → packed LICENSE file.

The expression.SPDXLicenseIDByURL mapping is data-driven from SPDX upstream, so it needs no hand-maintained dictionary.

Before / after

jackson-core declares its license only as a URL in the OSGi Bundle-License manifest header:

$ unzip -p jackson-core-2.9.10.jar META-INF/MANIFEST.MF | grep Bundle-License
Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt

Before — the URL is ignored, so no license is reported:

$ trivy rootfs --format json jackson-core-2.9.10.jar \
    | jq -c '.Results[].Packages[] | {Name, Licenses}'
{"Name":"com.fasterxml.jackson.core:jackson-core","Licenses":null}

After — the URL is resolved to its SPDX ID:

$ trivy rootfs --format json jackson-core-2.9.10.jar \
    | jq -c '.Results[].Packages[] | {Name, Licenses}'
{"Name":"com.fasterxml.jackson.core:jackson-core","Licenses":["Apache-2.0"]}

Binary size

The embedded SPDX seeAlso data and the new code add ~81 KiB (+0.04%) to the release binary.

Build (-ldflags="-s -w") Size
main 204,718,706 B (195.23 MiB)
this branch 204,801,634 B (195.31 MiB)
delta +82,928 B (+81 KiB, +0.04%)

Related issues

Checklist

  • I've read the guidelines for contributing to this repository.
  • I've followed the conventions in the PR title.
  • I've added tests that prove my fix is effective or that my feature works.
  • I've updated the documentation with the relevant information (if needed).
  • I've added usage information (if the PR introduces new options).
  • I've included a "before" and "after" example to the description.

@DmitriyLewen DmitriyLewen self-assigned this Jul 9, 2026
Build a reverse "license URL -> SPDX ID" index from the seeAlso field of the SPDX license list.
The generator normalizes URLs, unwraps web.archive.org snapshots, and collapses only/or-later/+ families to their base.
licenses.json now maps each license ID to its normalized URLs, and NormalizeLicenseURL / SPDXLicenseIDByURL are exposed for the parsers.
Fall back to <license><url> when <name> is empty, mapping the URL to its SPDX ID via the seeAlso index.
Unresolved URLs are skipped.
Resolve the OSGi Bundle-License manifest header (unfolding wrapped continuation lines and parsing the ;link= form) and the pom.xml <license><url> fallback for JAR-embedded poms to SPDX IDs.
Values that do not resolve are skipped so the packed LICENSE file is still classified; the manifest read is bounded with io.LimitReader.
Priority: pom <name> -> pom <url> -> Bundle-License -> LICENSE file.
…m <url>

List the OSGi Bundle-License manifest header and the pom.xml <license><url> fallback among the JAR license sources, and note that license URLs are resolved to SPDX IDs via the SPDX seeAlso references.
Two comments were left over from earlier iterations: one referenced a generator guard that was later removed, another used a "collapse to base" example that no longer matches the "smallest referencing ID" logic. A few verbose comments are also trimmed.
Replace the sync.OnceValue over the parsed licenses.json with a plain parseLicenses helper, so the intermediate map is not kept resident for the whole process. The validation set and URL index are still built lazily and independently.
Trim entry values with a single strings.Trim (dropping the trimBundleLicenseValue helper), and fall back to Bundle-License only when no Jenkins Plugin-License-Name is present instead of concatenating both manifest sources.
Range with strings.SplitSeq, use strings.CutSuffix, and put the blank "embed" import in its own gci section.
@DmitriyLewen DmitriyLewen marked this pull request as ready for review July 10, 2026 12:27
@DmitriyLewen DmitriyLewen requested a review from knqyf263 as a code owner July 10, 2026 12:27
@DmitriyLewen DmitriyLewen requested a review from nikpivkin July 13, 2026 09:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant