ol 0.9.6
dotnet tool install --global ol --version 0.9.6
dotnet new tool-manifest
dotnet tool install --local ol --version 0.9.6
#tool dotnet:?package=ol&version=0.9.6
nuke :add-package ol --version 0.9.6
ol
English | 日本語
Open-source license checker for resolved dependencies and SBOMs.
ol lists the licenses of the direct and transitive dependencies an application actually uses. It improves accuracy by combining evidence from SBOMs, package registries, and source repositories. This lets you understand the OSS licenses in use and automatically detect license-policy violations when a pull request changes dependencies.
What ol does
ol does not provide legal advice or claim legal certainty. It does not guess unobservable facts; uncertainty remains visible in the result.
- Reviews licenses across the current project, including transitive dependencies.
- Makes missing, ambiguous, conflicting, and invalid SPDX evidence visible.
- Compares license-relevant changes between two saved reports.
- Represents licenses consistently with SPDX License Identifiers.
- Saves JSON reports with evidence provenance and evaluates them later.
What ol does not do
ol does not resolve dependencies. Ecosystem-native resolution is the most reliable source of the versions a build selected, so ol focuses on resolved inputs. Instead of manifests such as package.json, *.csproj, or Cargo.toml, give ol one of the following:
- a CycloneDX or SPDX JSON SBOM; or
- a lockfile or resolved package-manager output from npm, Cargo, NuGet, or another supported ecosystem, such as
package-lock.json,cargo-metadata.json, orproject.assets.json.
Quick start
Download the asset for your OS from GitHub Releases, then place ol (or ol.exe on Windows) where you want.
# Homebrew (macOS/Linux)
brew tap guitarrapc/ol https://github.com/guitarrapc/ol
brew install guitarrapc/ol/ol
# .NET global tool
dotnet tool install -g ol
# Windows (Scoop)
scoop bucket add guitarrapc https://github.com/guitarrapc/scoop-bucket
scoop install ol
An SBOM such as bom.cdx.json is the most convenient input when you need to cover resolved dependencies across languages. ol can also consume supported lockfiles and package-manager outputs directly.
Tools such as @cyclonedx/cyclonedx-npm can generate a CycloneDX JSON SBOM.
# On macOS/Linux, add execute permission if needed
chmod +x ./ol
# Scan a CycloneDX or SPDX JSON SBOM
npx @cyclonedx/cyclonedx-npm --output-format JSON --output-file bom.cdx.json
ol scan --input bom.cdx.json
# Scan supported ecosystem-resolved dependency inputs under the current directory
ol scan --input .
# Exclude documentation or Pages projects that are outside the audited product
ol scan --input . --exclude-input-path src/documents --exclude-input-path Pages
# Scan a supported lockfile or package-manager output directly
ol scan --input package-lock.json
ol scan --input src/MyProject/obj/project.assets.json
# Scan an SBOM together with the resolved tree it describes (recommended when both exist)
ol scan --input bom.cdx.json --input .
# Write a reviewable Markdown report
ol scan --input . --format markdown > ol-report.md
# Write a reusable JSON report
ol scan --input . --format json > ol-report.json
# Show only direct dependencies and group them by license
ol scan --input . --dependency direct --group-by license
# Check the saved report against an SPDX license allow-list
ol check --report ol-report.json --allow-licenses MIT,Apache-2.0,BSD-2-Clause,BSD-3-Clause
# Compare license-relevant changes between two saved reports
ol diff --previous before.json --current after.json
# Use only license evidence already present in the input
ol scan --input bom.cdx.json --no-external-evidence
GitHub Actions
guitarrapc/setup-ol provides a simple way to install ol.
on:
push:
branches: [main]
jobs:
license-check:
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- uses: guitarrapc/setup-ol@v1.0.0
- name: Scan licenses
run: ol scan --input . --format json > ol-report.json
env:
OL_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Detect license violations
run: ol check --report ol-report.json --allow-licenses MIT,Apache-2.0,BSD-3-Clause --format markdown > "$GITHUB_STEP_SUMMARY"
Commit a previous report to detect added packages and license changes when a pull request updates dependencies. OSS libraries sometimes change licenses between versions; diff makes those changes visible.
on:
pull_request:
branches: [main]
jobs:
license-check:
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- uses: guitarrapc/setup-ol@v1.0.0
- name: Scan licenses
run: ol scan --input . --format json > after.json
env:
OL_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Compare license changes
run: ol diff --previous before.json --current after.json
- name: Detect license violations
run: ol check --report after.json --allow-licenses MIT,Apache-2.0,BSD-3-Clause --format markdown > "$GITHUB_STEP_SUMMARY"
Usage
$ ol --help
Usage: [command] [-h|--help] [--version]
Commands:
cache clear Clears cached evidence for the specified category.
cache info Shows the contents of a cache directory or archive.
cache list Lists managed cache locations and their sizes.
cache pack Packs managed cache entries into one deterministic gzip-compressed archive.
cache prune Removes managed cache entries older than the specified age.
cache unpack Unpacks one Ol cache archive into the managed cache directories.
check Check a canonical JSON scan report against allowed SPDX licenses.
diff Compare two persisted JSON scan reports and report license-relevant changes.
scan Scan a resolved dependency input.
skill export-plugin Export a portable Agent Plugin package.
skill install Install the skill into the current workspace.
spdx clear Clear user-managed SPDX data.
spdx list List installed SPDX data versions.
spdx update Download SPDX data into the user data directory.
spdx use Switch active SPDX data version.
spdx version Show the active SPDX data source.
| Command | Purpose |
|---|---|
ol scan |
Collect license evidence from resolved dependencies and produce a report. |
ol check |
Evaluate a canonical JSON report against an allow-list; --format markdown includes the scan context and findings in CI-friendly tables. |
ol diff |
Compare two canonical JSON reports. |
ol skill install |
Install the bundled license-scan Agent Skill for Codex or Claude. |
ol skill export-plugin |
Export the skill as a portable Agent Plugin package. |
ol cache clear |
Clear evidence caches managed by ol. |
ol cache list |
List resolved cache locations, entry counts, and sizes. |
ol cache info |
Show the contents of a cache directory or .olcache archive; lists entries that failed validation, --verbose lists every entry, --format markdown renders tables. |
ol cache pack |
Pack evidence caches into a deterministic .olcache archive. |
ol cache prune |
Remove managed cache entries older than a specified age; --dry-run previews the entries and bytes without deleting. |
ol cache unpack |
Restore a .olcache archive into an isolated cache directory. |
ol spdx version |
Show the active SPDX data source. |
ol spdx list |
List installed SPDX data versions. |
ol spdx update |
Download SPDX data. |
ol spdx use |
Select the SPDX data version to use. |
ol spdx clear |
Remove user-managed SPDX data. |
Use scan to collect licenses from an SBOM, lockfile, or other resolved dependency input. JSON reports can be reused by check and diff.
You can commit an Ol cache archive to share previously collected evidence across CI repositories. Create or update the archive from a populated cache directory:
ol cache pack cysharp.olcache --cache-dir .ol-cache --max-age 30d
In a consuming GitHub Actions job, unpack the archive under RUNNER_TEMP and use that directory for the scan:
ol cache unpack cysharp.olcache --cache-dir "$RUNNER_TEMP/ol-cache"
ol scan --input . --cache-dir "$RUNNER_TEMP/ol-cache"
The scan can add missing evidence to this temporary cache without changing the committed archive, and GitHub Actions removes the temporary directory with the job. When maintaining a persistent cache directory, remove old entries explicitly with ol cache prune --cache-dir .ol-cache --max-age 30d.
Keep cache archives at or below 1 MiB when possible so they remain practical to store and update in source control. cache pack reports the compressed size and, when an archive exceeds 1 MiB, prints a warning with category counts to help identify what is using space. Archives larger than 8 MiB are rejected. For safety, Ol also limits each cache entry to 2 MiB, expanded archive content to 64 MiB, and each archive to 10,000 entries. Use --max-age to omit evidence that no longer needs to be distributed.
Use ol cache info --cache-dir .ol-cache to inspect a persistent cache directory or ol cache info shared-cache.olcache to inspect a packed cache. Preview retention cleanup with ol cache prune --cache-dir .ol-cache --max-age 30d --dry-run before removing old entries.
The archive contains package and repository identities from the source cache. Do not publish a seed built from private-repository evidence.
$ ol scan --help
Usage: scan [options...] [-h|--help] [--version]
Scan a resolved dependency input.
Options:
--input <string[]> Repeatable resolved dependency input files or directories. [Required]
--exclude-input-path <string[]?> Repeatable file or directory paths excluded from directory input discovery. [Default: null]
--input-format <string> Input format: auto (default), cyclonedx, spdx, nuget-assets, npm-package-lock, pnpm-lock, yarn-classic-lock, yarn-berry-lock, cargo-metadata, go-module-graph, pip-inspect, composer-lock, bundler-lock, maven-dependency-tree, swift-package-resolved, or cocoapods-lock. [Default: @"auto"]
--format <ReportFormat> Output format: text, json, or markdown. [Default: Text]
--verbose Include verbose columns and input detection diagnostics.
--dependency <string?> Dependency output filter: root,direct,transitive,unknown. [Default: null]
--group-by <string?> Group output by fields: name,version,license,ecosystem,dependency,status. [Default: null]
--sort <string> Sort keys: ecosystem,name,version,license,dependency,status,purl. [Default: @"ecosystem,name,version"]
--sort-order <SortOrder> Sort order: asc or desc. [Default: Asc]
--spdx-data <string?> Directory containing licenses.json and exceptions.json. [Default: null]
--quiet Suppress stderr summary.
--refresh Ignore cached package metadata, source repository, and GitHub file entries and fetch them again.
--cache-dir <string?> Root directory for isolated package-metadata, source-repository, and GitHub file caches. [Default: null]
--no-external-evidence Use only license evidence declared in the input; package registries, source repositories, and their caches are never read.
--skip-evidence-packages <string?> Comma-separated package URL prefixes whose external evidence is never collected. A prefix may stop at the ecosystem, as in pkg:github/. [Default: null]
--concurrency <int> Maximum concurrent package metadata and source repository lookups. [Default: 0]
--retry <int> Retry count for package registry and GitHub License API requests. [Default: 1]
$ ol check --help
Usage: check [options...] [-h|--help] [--version]
Check a canonical JSON scan report against allowed SPDX licenses.
Options:
--report <string> Persisted canonical JSON scan report to evaluate. [Required]
--allow-licenses <string> Comma-separated SPDX License Identifiers. [Required]
--allow-dev-licenses <string?> Comma-separated SPDX License Identifiers additionally allowed for development-only components. [Default: null]
--exclude-packages <string?> Comma-separated package URL prefixes whose components are not evaluated. A prefix may stop at the ecosystem, as in pkg:github/. [Default: null]
--spdx-data <string?> Directory containing licenses.json and exceptions.json. [Default: null]
--verbose Include persisted report diagnostics.
--baseline <string[]?> Repeatable baseline files acknowledging already reviewed unresolved components. A component is acknowledged when any of them states it. [Default: null]
--update-baseline Rewrite the last baseline file, holding what the earlier ones do not already acknowledge.
--sarif <string?> Write violations as SARIF to this file for CI code scanning. [Default: null]
Use check to evaluate a JSON report produced by scan and find packages whose licenses violate the configured allow-list.
$ ol diff --help
Usage: diff [options...] [-h|--help] [--version]
Compare two persisted JSON scan reports and report license-relevant changes.
Options:
--previous <string> Previously persisted JSON scan report. [Required]
--current <string> Current JSON scan report. [Required]
--format <DiffFormat> Output format. [Default: Text]
SPDX data is bundled with ol. You can download newer SPDX data and select the active version locally.
$ ol spdx --help
Usage: spdx [command] [-h|--help] [--version]
Manage SPDX data.
Commands:
clear Clear user-managed SPDX data.
list List installed SPDX data versions.
update Download SPDX data into the user data directory.
use Switch active SPDX data version.
version Show the active SPDX data source.
ol caches collected license evidence to avoid repeating the same requests. Use cache list, cache info to inspect the cache, and cache prune or cache clear to remove it.
ol cache list
ol cache info
ol cache info --verbose
ol cache info --format markdown
ol cache info shared-cache.olcache --format markdown
ol cache prune --max-age 30d --dry-run
ol cache clear
$ ol cache --help
Usage: cache [command] [-h|--help] [--version]
Manage locally cached scan evidence.
Commands:
info Shows the contents of a cache directory or archive.
list Lists managed cache locations and sizes.
pack Packs managed cache entries into one deterministic archive.
unpack Unpacks an Ol cache archive into the managed cache directories.
clear Clears cached evidence for the specified category.
prune Removes managed cache entries older than the specified age.
ol bundles an Agent Skill that teaches coding agents how to select resolved inputs, combine an SBOM with package-manager evidence, and interpret scan results. Install it into the current workspace or export a portable Agent Plugin:
ol skill install --target codex
ol skill install --target claude
ol skill export-plugin --output ./ol-plugin
ol skill export-plugin --output ./ol-plugin --with-claude
Codex installs to .agents/skills/license-scan; Claude installs to .claude/skills/license-scan. --output overrides the destination. Existing directories are preserved unless --force is supplied. --with-claude adds a Claude Code manifest adapter while sharing the same skills/license-scan content.
Exit codes
Each command uses the following exit codes. CI can use the check result to distinguish policy violations from command failures.
| Exit code | Meaning |
|---|---|
0 |
The command completed successfully. Help and version output also use 0. |
1 |
Argument parsing, configuration, input, I/O, or another execution failure prevented completion. |
2 |
check completed policy evaluation and found one or more violations. |
3 |
check completed but proved nothing: every finding is a collection failure, or the report states its input declared no resolved dependencies. |
Reading license results
Every component has one status:
| Status | Meaning |
|---|---|
matched |
Evidence resolved to one valid SPDX expression. |
conflict |
Valid evidence sources disagree. |
unknown |
Collection completed but yielded no usable license information. |
ambiguous |
License text exists but cannot be normalized without guessing. |
invalid |
A claimed SPDX expression or identifier is invalid. |
error |
Evidence collection or processing failed and no other evidence resolved the license. |
matched means resolved, not allowed. check applies the organization's allow-list. unknown, conflict, ambiguous, invalid, and error fail closed.
One ambiguous case does not, because nothing is left to decide. A registry such as deps.dev lists the licenses it found without stating how they relate. ol resolves each member against SPDX and records the result as a license listing — candidate kind: license-set, written MIT; Apache-2.0 — so only the operator between them stays unknown. A listing whose every member the allow-list admits is admitted as a conjunction and as a disjunction alike, and check reports it as Allowed on every reading of ambiguous evidence. One member outside the allow-list, one ol could not resolve (deps.dev answers non-standard for a license it could not identify), or an ambiguous value that is not a listing at all — a license name, a URL, a classifier, or a semicolon a publisher wrote in free text — remains a violation. The component stays ambiguous in the scan report either way; this decides the policy question, not the license.
A registry that answers 404 has answered, so the component becomes unknown with the warning package_metadata_not_found rather than error. This is what a package published only to a private feed looks like, and a baseline can acknowledge it. error is reserved for questions that were never answered — timeouts, 429, and 5xx — which is also what makes exit code 3 meaningful.
Improving license confidence
In addition to evidence in the input, scan collects license information from supported package registries and the GitHub License API, then caches it locally. To avoid GitHub API rate limits in GitHub Actions, map GITHUB_TOKEN explicitly to OL_GITHUB_TOKEN; ol never reads GITHUB_TOKEN implicitly.
env:
OL_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
To use only evidence already present in the input, disable external sources and their caches:
ol scan --input bom.cdx.json --no-external-evidence
Without external evidence, more components may remain unresolved and therefore fail check.
A publisher that states no SPDX expression often states a license URL instead, such as NuGet's legacy licenseUrl or CycloneDX's license.url. ol does not fetch those pages. It does recognize a URL the SPDX license list itself publishes as one license's seeAlso — https://www.apache.org/licenses/LICENSE-2.0 is Apache-2.0 in the same record that defines the identifier — matching apart from scheme, case, a leading www., and a trailing slash. A URL SPDX does not publish, or one it publishes for several licenses, stays an unresolved declaration, and a stated license is never overridden by one.
The same rule applies to a license document ol does read, such as the LICENSE file inside a package. A document is identified by the SPDX license text it reproduces, and also by a license URL SPDX publishes that the document itself contains, so a LICENSE that only says Licensed under the Apache License, Version 2.0 and links the canonical Apache page resolves to Apache-2.0. One document that names two licenses resolves neither: xunit 2.4.1 ships a license.txt that puts the project under Apache-2.0 in notice form and quotes MIT in full for imported code in one subdirectory, so ol reports it unresolved rather than picking the one it can read as a template. The report records which reading produced the answer, spdx-template or spdx-license-url.
ol does not crawl arbitrary repository contents or guess a license from repository layout or license files.
Resolving package dependencies
For release and audit artifacts, prefer one CycloneDX or SPDX JSON SBOM covering the complete subject. For quick local feedback, use a supported lockfile or package-manager output directly. When both are available, pass them together — see Combine an SBOM with the resolved tree.
| Ecosystem | Resolved input for ol | How to prepare it |
|---|---|---|
| Any | CycloneDX / SPDX JSON SBOM | Resolve dependencies with an ecosystem-native tool and generate an SBOM. |
| .NET / NuGet | project.assets.json v3/v4 |
Run dotnet restore. |
| npm | package-lock.json v2/v3 |
Run npm install. |
| pnpm | pnpm-lock.yaml v9 |
Run pnpm install. |
| Yarn | Classic v1 or Berry metadata v8 yarn.lock |
Run yarn install. |
| Rust / Cargo | Cargo metadata JSON | Run cargo metadata --format-version 1 --locked. |
| Go modules | module list and graph | Save go list -m -json all and go mod graph in one directory. |
| Python | pip JSON v1 | Run python -m pip inspect --local. |
| PHP / Composer | composer.json and composer.lock |
Keep both files in one directory. |
| Ruby / Bundler | Gemfile.lock |
Run bundle install. |
| Java / Maven | Maven Dependency Plugin 3.7+ tree JSON | Run mvn dependency:tree -DoutputType=json -DoutputFile=maven-dependency-tree.json. |
| Java / Gradle | SBOM | Generate an SBOM; Gradle has no official portable JSON format for its resolved graph. |
| SwiftPM | Package.resolved v2/v3 |
Run swift package resolve. |
| CocoaPods | Podfile.lock |
Run pod install. |
ol detects formats from content, so --input-format is normally unnecessary. Repeat --input A --input B to combine inputs.
Combine an SBOM with the resolved tree
An SBOM records which packages a build resolved, but not where those packages are on disk. A package-manager input records both. Pass one SBOM and the resolved tree together, and each component is judged on the union of what the two inputs make available:
ol scan --input bom.cdx.json --input .
This is the recommended input for a project that publishes an SBOM. Two things come back that an SBOM alone cannot supply:
- License files from the packages the build actually consumed. ol reads the
LICENSEfile inside a restored package only when an input tells it where that package is. This resolves publishers whose registry metadata states no license, and it is the difference betweenmatchedandunresolvedfor real packages —Microsoft.DotNet.PlatformAbstractionsstates no license in the NuGet registry andNOASSERTIONon GitHub, but shipsLICENSE.TXTin the package. The scan summary reports0undertargetsin thePackage artifactsrow of its evidence table when no input pointed at a resolved tree. - A dependency graph the resolver produced. Whether an SBOM carries a usable graph is up to its generator. A generator that emits an incomplete one leaves components ol will not classify, and
dependency: unknowndisables--dependency directand any--allow-dev-licensesallowance, which only applies where the resolver proves a component is development-only.
The SBOM still contributes what only it knows: license claims its producer asserted, and components outside the package managers ol reads directly.
One SBOM may be combined with any number of package-manager inputs. A second SBOM is an input failure, because two repository-wide documents describing one subject is a contradiction in the input rather than something ol can resolve:
Unable to scan input: A collection accepts at most one SBOM document.
Exclude repository subtrees from the audit subject
# Exclude only product-a/docs; product-b/docs remains included
ol scan --input product-a --input product-b \
--exclude-input-path product-a/docs
# Repeat the option to exclude multiple exact paths
ol scan --input product-a --input product-b \
--exclude-input-path product-a/docs \
--exclude-input-path product-b/docs
Paths are exact, existing files or directories relative to the current working directory; globs are not supported. An explicitly named directory that is itself inside an exclusion is skipped, while an explicitly named file inside one is rejected. This affects only ol's directory discovery, so apply the same exclusion when generating a repository-wide SBOM.
Common operations
Filter the view
--dependency filters only the rendered view; analysis still uses the complete inventory.
ol scan --input . --dependency direct
ol scan --input . --group-by license
ol scan --input . --sort status,name
Apply a separate allow-list to development-only dependencies
An additional allow-list applies only when resolver data proves a component is development-only. It never relaxes components whose usage is unknown.
ol check --report ol-report.json \
--allow-licenses MIT,Apache-2.0,BSD-3-Clause \
--allow-dev-licenses CC-BY-4.0
This does not prove that the package is absent from a production artifact. Check the release artifact separately with the primary allow-list.
Scanning an SBOM alongside the lockfile does not withdraw the allowance. An SBOM records no development scope, so it says nothing about reachability and its view of a component abstains rather than overruling the resolver. Only a resolver that classifies the component as runtime downgrades it. A component no input classified stays unknown, and this option never relaxes it.
$ ol scan --input package-lock.json --input bom.cdx.json --format json > report.json
$ ol check --report report.json --allow-licenses MIT --allow-dev-licenses CC-BY-4.0
Allowed by development policy: 1 component.
License check passed: 1 component satisfies the allow-list.
One thing to know: an SBOM covering more than the lockfile does can carry a runtime copy of the same package, and ol matches on package URL alone, so it would fold onto the development row. The Supplied by summary line shows whether the SBOM reached beyond the resolved inputs.
Adopt a baseline for an existing project
An existing project can contain components ol cannot resolve: a package on a private feed, a registry with no license field, a source outside GitHub. They fail closed, and you cannot fix them by editing your own code.
ol check --report ol-report.json --allow-licenses MIT,Apache-2.0
License check failed: 1 violation.
Package Version Ecosystem Purl License/Status Reason Mechanism Reference Path
----------------------- ------- --------- --------------------------------------- -------------- --------------------- -------------------------- --------- ----
@mycompany/internal-sdk 1.0.0 npm pkg:npm/%40mycompany/internal-sdk@1.0.0 unknown license is unresolved package_metadata_not_found - -
Unresolved mechanisms
package_metadata_not_found: 1
Reason says why policy rejected the component. Mechanism says why its evidence never settled, and that is the one that names an action: this package is not on a public registry, so no amount of collection will answer for it. The tally at the end groups the rows, because a hundred unresolved components are usually a handful of populations and each population is fixed once.
Record what you reviewed and accepted with --update-baseline:
ol check --report ol-report.json --allow-licenses MIT,Apache-2.0 \
--baseline ol-baseline.json --update-baseline
Acknowledged by baseline: 1 component.
License check passed: 2 components satisfy the allow-list.
ol-baseline.json now records that component with the evidence that produced it, plus a fingerprint of that evidence. Commit the file; its raw claims and any publisher-declared license references are in it, so a reviewer can judge a future change from the pull request diff alone.
{
"schemaVersion": 1,
"acknowledged": [
{
"ecosystem": "npm",
"name": "@mycompany/internal-sdk",
"version": "1.0.0",
"purl": "pkg:npm/%40mycompany/internal-sdk@1.0.0",
"status": "unknown",
"evidence": [
{ "source": "package-registry", "kind": "fetch", "raw": "" },
{ "source": "source-repository", "kind": "unavailable", "raw": "" }
],
"fingerprint": "eb7d5af4cdf1b2d6cff18128705d9a713c8d82d16426ba3a7d2463e4c512c41e"
}
]
}
Later runs name the file and drop --update-baseline:
ol check --report ol-report.json --allow-licenses MIT,Apache-2.0 --baseline ol-baseline.json
A newly unresolved component still fails. This is the point of a baseline: the accepted set cannot grow without review.
Acknowledged by baseline: 1 component.
License check failed: 1 violation.
Package Version Ecosystem Purl License/Status Reason Mechanism Reference Path
-------------------- ------- --------- ------------------------------------ -------------- --------------------- -------------------------- --------- ----
@mycompany/reporting 2.1.0 npm pkg:npm/%40mycompany/reporting@2.1.0 unknown license is unresolved package_metadata_not_found - -
Unresolved mechanisms
package_metadata_not_found: 1
A forbidden license is never absorbed, even when you regenerate the file. Only unknown, ambiguous, conflict, and invalid can be acknowledged, and only when no recognizable candidate is rejected by the allow-list. A resolved license belongs in --allow-licenses, and an error is a collection failure to repair. An ambiguous listing the allow-list already admits on every reading is not acknowledged either, because it is not a violation to review.
ol check --report ol-report.json --allow-licenses MIT,Apache-2.0 \
--baseline ol-baseline.json --update-baseline
Acknowledged by baseline: 1 component.
License check failed: 1 violation.
Package Version Ecosystem Purl License/Status Reason Mechanism Reference Path
------------ ------- --------- -------------------------- -------------- ---------------------- --------- --------- ---------------------------------------------------------
copyleft-lib 3.0.0 npm pkg:npm/copyleft-lib@3.0.0 GPL-3.0-only license is not allowed - - pkg:npm/report-builder@1.4.0 > pkg:npm/copyleft-lib@3.0.0
An acknowledged component keeps its unresolved status and evidence in the report; only its violation is removed. When the version changes, or a registry corrects its metadata, the fingerprint stops matching and the component fails again until it is reviewed anew.
Skip collection or exclude evaluation for selected components
These options solve different problems:
| Option | Stage | Behavior |
|---|---|---|
scan --skip-evidence-packages <purl-prefix> |
Evidence collection | Makes no external request for matching components. Components remain in the report and policy evaluation. |
check --exclude-packages <purl-prefix> |
Policy evaluation | Removes matching components from allow-list evaluation, baselines, violations, and SARIF. The scan report is unchanged. |
Both use case-sensitive Package URL prefixes. ol does not infer package ownership or whether a package is private.
Write a namespace the way its ecosystem spells it: --skip-evidence-packages pkg:npm/@acme/ matches pkg:npm/%40acme/util@1.0.0. A version separator is unaffected, so pkg:npm/left-pad@1.3.0 still selects that one component.
A prefix can stop at the ecosystem and select all of it. This is what you want when a generator catalogues something your project never depended on — syft reads GitHub Actions out of workflow files, so an SBOM of any repository with CI carries pkg:github/... components that no package registry can answer for:
ol check --report report.json --allow-licenses MIT --exclude-packages "pkg:github/" --verbose
Exclusion prefix pkg:github/ matched 6 components.
Excluded from evaluation: 6 components.
Because one entry can now take a lot, ol always reports how many components were selected, and --verbose attributes the count to each prefix. Check that number: it is what tells you the prefix took what you meant it to take. A prefix naming no ecosystem at all, such as pkg:, is still rejected.
Neither is required to keep a private package reviewable: a registry 404 already yields unknown, which a baseline can acknowledge. Use --skip-evidence-packages when you want to stop spending a request that cannot succeed, and --exclude-packages when the component is outside the check.
Compare two reports
ol diff --previous before.json --current after.json
ol diff --previous before.json --current after.json --format json
diff reports additions, removals, and version, status, license, or evidence changes. It exits 0 when comparison succeeds even when changes exist; policy enforcement belongs to check.
Before the changes it states the boundary each report was produced under — excluded input paths, --dependency filter, and input coverage — whenever the two reports do not obviously describe the same population. A report that read fewer inputs holds fewer components, and every one of them shows up as a removal, so without the boundary "an input was not read" and "a dependency was removed" are the same diff.
Write SARIF
ol check --report ol-report.json \
--allow-licenses MIT,Apache-2.0 \
--sarif ol.sarif
The stdout verdict remains unchanged. SARIF 2.1.0 contains the same violations and, when graph data is available, the shortest dependency path to a transitive violation.
Frequently asked questions
Can I pass package.json, *.csproj, or Cargo.toml directly?
No. These manifests describe requested dependencies, not the exact versions and transitive graph selected by the build. Generate an SBOM or use a supported resolved input. For .NET, run dotnet restore and scan obj/project.assets.json. For Rust, run cargo metadata --format-version 1 --locked > cargo-metadata.json and scan cargo-metadata.json; neither Cargo.toml nor Cargo.lock is accepted directly, and a library that does not commit a lockfile omits --locked.
Should I use an SBOM or package-manager input?
Prefer one SBOM for releases, audits, and repositories containing several ecosystems. Direct package-manager inputs are convenient for local feedback or when a resolved graph is already generated or committed.
Better still, pass both when both exist. ol matches them on package URL and combines their evidence, and the SUPPLIED column shows whether a component came from the SBOM, the package-manager input, or both. This is worth doing when the two inputs enumerate different sets — a lockfile often holds entries an SBOM omits — when you want disagreements between them reported rather than hidden by scanning separately, and because only a package-manager input lets ol read the license files inside the packages the build consumed. See Combine an SBOM with the resolved tree.
Does ol require network access?
By default, yes, for external evidence collection. --no-external-evidence disables all registry, repository, and evidence-cache access. Bundled SPDX data keeps ordinary validation available offline.
How should I handle an existing unresolved dependency?
Review its raw evidence and status first. A baseline can acknowledge known unresolved evidence that cannot be fixed, but never a forbidden license or error.
Can I apply another policy without scanning again?
Yes. Run check with a different --allow-licenses value against the saved canonical JSON report. check performs no external access.
Ecosystem usage
For ecosystem-specific SBOM generation, resolved-input commands, and important input constraints, see the ecosystem usage guide.
It covers .NET/NuGet, JavaScript, Rust, Go, Python, PHP/Composer, Ruby/Bundler, Java/Maven and Gradle, SwiftPM, and CocoaPods.
Detailed documentation
- Ecosystem usage
- Design principles
- Architecture
- CLI and report specification
- SPDX specification
- Package-manager evidence specification
- Source-repository evidence specification
- Cache-format specification
Development
The ecosystem CI and self-scan contract is documented in verification.md.
Repository sandbox
# Regenerate ol's committed SBOM and text, Markdown, and JSON reports.
./sandbox/Update-SelfScan.ps1
# Keep the committed SBOM as a fixed golden input and regenerate only its derived reports.
./sandbox/Update-SelfScan.ps1 -ReportsOnly
Scan
dotnet run --project src/Ol -- scan --input src/Ol/obj/project.assets.json --format markdown
Check
dotnet run --project src/Ol -- scan --input src/Ol/obj/project.assets.json --format json > ol-report.json
dotnet run --project src/Ol -- check --report ol-report.json --allow-licenses MIT,Apache-2.0,BSD-2-Clause,BSD-3-Clause
Generated data
# Generate the SPDX License List
dotnet run --project src/Ol.Update -- generate
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.