Versions and estimates#
licenseal is a license resolver, not a dependency resolver: it does not resolve version conflicts between transitive requirements, and it does not model what a given environment would install. A lockfile gives the installed versions. Without one, the rules below decide which version is reported.
Which version is reported#
- Exact pins (
==2.28.1,18.2.0) are reported at that exact version. - An empty version constraint resolves to the registry's latest published release. A NuGet reference with no version, and a
pom.xmldependency with no version that the parent chain does not manage either, are reported with no version and resolve toUNKNOWN. - A range resolves to the highest published release inside it, read in the grammar of its ecosystem (PEP 440, npm, Cargo, RubyGems, Hex, Composer). A prerelease is selected only when the range admits prereleases; Python, like pip, also takes one when no release is inside the range. NuGet resolves as a restore does: a bare version or a range with an inclusive lower bound resolves to that bound, any other range to the lowest published release inside it, and a floating version (
1.*) to the highest release it matches. - Not resolved, and reported as
UNKNOWNwith no version: a constraint that is not written in its ecosystem's grammar; a range that no published version satisfies; a Composer branch constraint (dev-main); a Maven or Gradle version range, dynamic version, orRELEASE/LATESTmarker ([1.0,2.0),1.+,latest.release); a NuGet version holding an unresolved MSBuild property. None of these falls back to the latest release. - Go and R have no range to resolve.
go.modnames exact versions. ADESCRIPTIONconstraint other than(== x)is not evaluated: the CRAN index lists one version of each package, and that version is the one reported.
Estimates#
- Estimated versions. A version that licenseal picked from a registry is marked:
~1.2.3in the table and Markdown formats, with a legend line under the table, and"estimated": ["version"]in JSON. That is every transitive row that does not come from a lockfile and every direct dependency declared with a range or with no constraint. A row a lockfile supplies, an exact pin, a Maven version the parent chain manages, and a Go version are not marked. - Estimated licenses. A license read from metadata that describes another release than the reported one is marked
~MIT, and"license"in the JSONestimatedarray. The mark is not a finding by itself, and the license is judged like any other: an estimatedGPL-3.0-onlyin an MIT project is a violation. A license is an estimate where a registry does not serve it per release:- an R package pinned to a version other than the one the CRAN index lists;
- a Hex package reported at a release other than the one published last;
- a Python release that declares no license in its own PyPI metadata or wheel metadata, which takes the license of the project's latest release;
- a Python release that PyPI does not have.
Failed lookups and rate limits#
- A registry lookup that cannot produce a license resolves that one dependency to
UNKNOWNand the scan continues. Apart from the cases above, a failed lookup of a pinned release is never answered with the metadata of another release. - Rate limits. hex.pm allows 100 unauthenticated API requests per minute, so a project with more than 100 Hex packages pauses until the limit resets (about a minute per further 100 packages); each pause is announced on stderr. The waiting is bounded: packages still rate limited when it ends resolve to
UNKNOWN, and aWarning:that is not an analysis gap gives their number. Requests to crates.io are spaced one second apart, which slows a Rust scan that has noCargo.lock.