ol 0.9.6

dotnet tool install --global ol --version 0.9.6
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local ol --version 0.9.6
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=ol&version=0.9.6
                    
nuke :add-package ol --version 0.9.6
                    

build Release

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, or project.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 seeAlsohttps://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 LICENSE file 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 between matched and unresolved for real packages — Microsoft.DotNet.PlatformAbstractions states no license in the NuGet registry and NOASSERTION on GitHub, but ships LICENSE.TXT in the package. The scan summary reports 0 under targets in the Package artifacts row 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: unknown disables --dependency direct and any --allow-dev-licenses allowance, 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

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
0.9.6 60 9/9/2026
0.9.5 77 8/26/2026
0.9.4 72 8/25/2026
0.9.3 156 8/18/2026
0.9.2 80 8/12/2026
0.9.1 81 8/11/2026
0.9.0 78 8/6/2026