Ohrisk catches open-source license risk before your PR ships.
It is a local CLI for developers who need a quick answer to questions like:
- Did this dependency bring in AGPL, GPL, BUSL, or unknown license evidence?
- Is the risky package production-relevant or dev-only?
- Which parent package introduced the transitive risk?
- Does the answer change for SaaS versus distributed app usage?
Ohrisk is a risk decision aid, not legal advice. It reports low, review,
high, and unknown findings for the selected usage profile.
Install and run your first scan in under a minute:
npm install -g ohrisk@latest
cd your-project
ohrisk scanThe terminal report shows findings sorted by severity:
Risks: 1 high, 1 review, 1 unknown, 2 low
high— replace or escalate before shippingreview— check before shipping under the selected profileunknown— license evidence is missing or unrecognizedlow— known low-risk license expression
Gate a production SaaS build by narrowing to production dependencies and the SaaS usage profile:
ohrisk scan --profile saas --prodOpen a browser-friendly HTML report:
ohrisk scan --html --output ohrisk-report.html --openUse Korean, Spanish, French, Chinese, Hindi, Japanese, Indonesian, Turkish, Russian, or German HTML report text when you want a local review artifact for those readers:
ohrisk scan --html --language ko --output ohrisk-report.html --open
ohrisk scan --html --language es --output ohrisk-report-es.html --open
ohrisk scan --html --language fr --output ohrisk-report-fr.html --open
ohrisk scan --html --language zh --output ohrisk-report-zh.html --open
ohrisk scan --html --language hi --output ohrisk-report-hi.html --open
ohrisk scan --html --language ja --output ohrisk-report-ja.html --open
ohrisk scan --html --language id --output ohrisk-report-id.html --open
ohrisk scan --html --language tr --output ohrisk-report-tr.html --open
ohrisk scan --html --language ru --output ohrisk-report-ru.html --open
ohrisk scan --html --language de --output ohrisk-report-de.html --openPrefer not to install globally? Use npx ohrisk scan instead.
Ready to gate PRs? Run ohrisk ci --fail-on high locally, or see the GitHub Actions guide to wire it into CI.
Run Ohrisk when you are about to add or upgrade a dependency and want a fast, local read on whether the license evidence introduces risk for your shipping model. It sits between "I just installed a package" and "legal review."
- before opening a PR that adds or changes dependencies
- before cutting a release or tagging a build
- when a transitive dependency surprise appears in a lockfile diff
- when you need a SARIF or SBOM artifact for a compliance pipeline
Ohrisk does not approve or block packages on its own. It gives you the evidence and a profile-aware severity so you can decide.
| Command | What it answers |
|---|---|
ohrisk scan |
What does my dependency tree look like right now? Non-failing local decision aid. |
ohrisk ci |
Should this PR fail the build? Runs a scan and exits non-zero when findings meet --fail-on. |
ohrisk diff <ref> |
What changed since the baseline git ref? Surfaces only new or meaningfully changed findings. |
ohrisk explain <expr> |
How would Ohrisk classify this license expression for a profile, without scanning a project? |
Ohrisk evaluates the same dependency tree differently depending on how you ship software, because redistribution changes license obligations.
saas(default): you run the service and do not redistribute the package binaries to users. GPL-only copyleft such as GPL-2.0 and GPL-3.0 is treated asreviewrather than an immediate block, because SaaS usage does not trigger redistribution obligations. AGPL and source-available restrictions remainhigh.distributed-app: you ship the package to users. GPL becomeshighbecause redistribution obligations apply. Weak copyleft (LGPL, MPL, EPL) is flagged asreview.
Pick the profile that matches how the dependency reaches your users:
ohrisk scan --profile saas
ohrisk scan --profile distributed-appOhrisk is distributed as an npm package, and the packaged CLI runs on Node.js
>=24.0.0. Bun is used for Ohrisk development, tests, and packaging, but users
do not need Bun installed to run the published CLI.
Ohrisk scans dependency-free package.json manifests, Bun, npm package-lock/shrinkwrap, pnpm, Deno npm, Yarn, Rust Cargo,
Go modules and workspaces, Python pyproject/pylock/uv/Pipenv/PDM/Poetry/requirements.txt, Java Gradle lockfiles and version catalogs,
Maven pom.xml, Bazel MODULE.bazel, .NET NuGet lockfiles, Conan locks, Conda environment specs and locks, vcpkg manifests, Haskell Stack locks, Perl Carton snapshots, LuaRocks locks, Dart/Flutter Pub locks,
Terraform provider locks, Helm chart dependency metadata, Nix flake locks,
Unity Package Manager locks, R renv locks, Julia manifests, SwiftPM pins,
Carthage pins, CocoaPods locks, Elixir Mix locks, Erlang Rebar3 locks, Ruby Bundler lockfiles, and PHP Composer lockfiles, plus
CycloneDX JSON/XML, SPDX JSON/RDF, and SPDX tag-value SBOM inputs, regardless of which
package manager you use to install the CLI.
The current implementation is the first local dependency-risk vertical slice:
- dependency-free
package.jsonmanifests, Bunbun.lock, npmpackage-lock.json, npmnpm-shrinkwrap.json, pnpmpnpm-lock.yaml, Denodeno.lock, RustCargo.lock, Gogo.work, Gogo.mod, Pythonpyproject.toml, Pythonpylock.toml, named Pythonpylock.<name>.toml, Pythonuv.lock, Python PipenvPipfile.lock, Python PDMpdm.lock, Pythonpoetry.lock, Pythonrequirements.txt, Java Gradlegradle.lockfile, Java Gradlegradle/dependency-locksdirectories andgradle/dependency-locks/*.lockfile, Java Gradlegradle/libs.versions.toml, Java Mavenpom.xml, BazelMODULE.bazel, .NET NuGetpackages.lock.json, .NET restoredobj/project.assets.json, .NET NuGetpackages.config, .NET*.csproj, Conanconan.lock, Condaenvironment.yml, Condaenvironment.yaml, Condaconda-lock.yml, Condaconda-lock.yaml, vcpkgvcpkg.json, Terraform.terraform.lock.hcl, HelmChart.lock, HelmChart.yaml, Nixflake.lock, Unity Package ManagerPackages/packages-lock.json, Rrenv.lock, JuliaManifest.toml, Haskell Stackstack.yaml.lock, Perl Cartoncpanfile.snapshot, LuaRocksluarocks.lock, Dart/Flutterpubspec.lock, SwiftPMPackage.resolved, CarthageCartfile.resolved, CocoaPodsPodfile.lock, Elixir Mixmix.lock, Erlang Rebar3rebar.lock, Ruby BundlerGemfile.lock, PHP Composercomposer.lock, CycloneDX JSON/XML, SPDX JSON/RDF, SPDX tag-value.spdx, and Yarn classic/Berryyarn.lockproject discovery - Node-compatible packaged CLI entrypoint for npm, pnpm, Yarn, npx, pnpm dlx, and yarn dlx users
- explicit dependency input selection with
--lockfile <path>for projects that contain more than one supported input file - direct and transitive dependency graph extraction when the dependency input records parent/child relationships
- Bun, npm, pnpm, and Yarn classic/Berry workspace projects are scanned from every workspace/importer package root
- pnpm
catalog:andcatalog:<name>dependency specifiers are resolved frompnpm-workspace.yaml - Deno
deno.lockprojects are scanned for npm package dependencies recorded innpm:specifiers; root remote URL imports and JSR packages fail closed instead of being silently skipped - Rust
Cargo.lockprojects are scanned for crates, using adjacentCargo.tomlroot dependencies plus literal and segment*/?Cargo workspace member manifests such ascrates/*,crates/app-*,tools/?li, andcrates/*/plugins/*when available, honoring workspaceexcludeentries,crate.workspace = truedependency keys, workspace dependency package aliases, and table-form dependency sections such as[dependencies.foo] - Go
go.workprojects are scanned across workspace modules and apply workspacereplacedirectives before module-level replacements; Gogo.modprojects are scanned for required modules, Goreplacedirectives, and adjacentgo.summodule versions when available - Python
pylock.tomland namedpylock.<name>.tomlprojects are scanned for versioned PyPI package records and project-root-contained source-tree package records with local source metadata - Python
pyproject.tomlprojects without a companion lockfile are scanned for exact PEP 621name==versiondirect dependency pins - Python
uv.lockprojects are scanned for PyPI package dependencies recorded in the lockfile and project-root-containeddirectoryoreditablepackage source records - Python PDM
pdm.lockandpoetry.lockprojects are scanned for PyPI package dependencies recorded in the lockfile - Python Pipenv
Pipfile.lockprojects are scanned for exact==versionPyPI package entries and project-root-contained localpathor editable source entries in thedefaultanddevelopsections - Python PDM
pdm.lockprojects use adjacentpyproject.tomlroot dependencies when available, infer roots from lockfile dependency references otherwise, and scan project-root-contained localpathor relativefile:source records - Python
requirements.txtfiles are scanned for pinned direct PyPI package dependencies, project-root-contained local source entries, editable local source entries, nested-rrequirement files, and exact-cconstraint pins - Java Gradle
gradle.lockfileand legacygradle/dependency-locksdirectory projects are scanned for Maven coordinates recorded in dependency locking output; explicitgradle/dependency-locks/*.lockfilefiles are also accepted. Java Gradlegradle/libs.versions.tomlprojects are scanned for exact Maven library aliases from compact notation,moduleplus exactversion, ormoduleplusversion.ref - Java Maven
pom.xmlprojects are scanned for direct dependencies with explicit, property-resolved, same-filedependencyManagement, or local.m2/repositoryparent/imported-BOMdependencyManagementversions - Bazel
MODULE.bazelprojects are scanned for directbazel_depentries with literal exactversionstrings; nodeprepo_name = Noneentries,include()expansion, overrides, module extensions, andMODULE.bazel.lockresolution output fail closed instead of being partial-scanned - .NET NuGet
packages.lock.jsonand restoredobj/project.assets.jsonprojects are scanned for direct and transitive package dependencies; .NET NuGetpackages.configand*.csprojfiles are scanned for direct package references, including versions resolved from the nearestDirectory.Packages.propsPackageVersionentries - Conan 2
conan.lockprojects are scanned for recipe references fromrequires,build_requires, andpython_requires; Conan binary package IDs, settings, options, user/channel, and recipe revisions are not modeled in Package URLs yet - Conda
environment.ymlandenvironment.yamlprojects are scanned for exact Condaname=versionpins and exact pipname==versionpins; Condaconda-lock.ymlandconda-lock.yamlprojects are scanned for resolvedcondaandpippackage entries and are preferred when both an environment spec and conda-lock output are present - vcpkg
vcpkg.jsonprojects are scanned from installedvcpkg_installed/vcpkg/statusrecords when available, or from exact top-leveloverrideswhen installed status is absent; baseline andversion>=constraints are not treated as resolved package versions - Terraform
.terraform.lock.hclprojects are scanned for locked provider versions; provider constraints and platform hashes are not modeled in Package URLs yet - Helm
Chart.lockandChart.yamlprojects are scanned for chart dependency entries;Chart.lockis preferred when both files are present - Nix
flake.lockprojects are scanned for reachable flake inputs from the root input graph; Nix derivation package graphs are not reconstructed - Unity Package Manager
Packages/packages-lock.jsonprojects are scanned for non-built-in package entries; Unity built-in modules,Packages/manifest.jsonwithout a lockfile, Asset Store.unitypackagearchives, Addressables catalogs, and remote UPM registry metadata fetch are not scanned yet - R
renv.lockprojects are scanned for package records in the lockfile; adjacent rootDESCRIPTIONDepends,Imports,LinkingTo,Suggests, andEnhancesfields are used for production/development root classification when available, while dependency parent graphs, remote CRAN/GitHub/Bioconductor artifact fetch, and Packrat lockfiles are not scanned yet - Julia
Manifest.tomlprojects are scanned for versioned[[deps.Name]]records; unversioned standard libraries are skipped, adjacentProject.toml[deps]and test target[extras]entries are used for root/dev classification when available, and remote Julia registry or package server artifact fetch is not scanned yet - Haskell Stack
stack.yaml.lockprojects are scanned for completed Hackage package pins; local Stack package database license metadata is used when present, while snapshot package expansion, git/path extra-deps, direct/transitive graph reconstruction, and Hackage metadata fetch are not scanned yet - Perl Carton
cpanfile.snapshotprojects are scanned for Carton snapshot v1 distribution pins and dependency paths inferred fromprovidesandrequirements; local Carton cache archiveMETA.jsonorMETA.ymllicense metadata is used when present, while MetaCPAN artifact fetch is not scanned yet - LuaRocks
luarocks.lockprojects are scanned for literaldependenciestable package pins; local.rockspecfiles in the project root or local rocks tree are used for literal string or string-table license metadata when present, while dependency graph reconstruction and LuaRocks metadata fetch are not scanned yet - Dart and Flutter
pubspec.lockprojects are scanned for concrete Pub package versions recorded in the lockfile - Swift Package Manager
Package.resolvedprojects are scanned for pinned packages with resolved versions, revisions, or branches; Package.resolved does not expose parent dependency graphs, so packages are reported as root-level pins with unknown dependency type - Carthage
Cartfile.resolvedprojects are scanned for resolved GitHub, git, and binary pins; Cartfile.resolved does not expose parent dependency graphs, so packages are reported as root-level pins with unknown dependency type - CocoaPods
Podfile.lockprojects are scanned for resolved pods; subspecs are collapsed to their root pod identity and dependency type is reported as unknown because Podfile.lock does not encode production/development groups - Elixir Mix
mix.lockprojects are scanned for resolved Hex package pins; adjacent rootmix.exsliteralonly:dependency options are used for production/development root classification when available, while mix.lock dependency graph reconstruction and remote Hex.pm artifact fetch are not scanned yet - Erlang Rebar3
rebar.lockprojects are scanned for Hexpkgpins; depth-0 Hex pins are classified as production roots, while git/path deps, plugin locks, profile-specific test deps, and Rebar dependency tree reconstruction are not scanned yet - Ruby Bundler
Gemfile.lockprojects are scanned for direct and transitive gem dependencies - PHP Composer
composer.lockprojects are scanned for production and development package dependencies, using adjacentcomposer.jsonroot dependencies when available - CycloneDX JSON/XML, SPDX JSON/RDF, and SPDX tag-value SBOM files are scanned for Package URL-backed package identities, dependency relationships, and embedded license evidence
- explicit
--lockfileSBOM paths are sniffed by content when their filename does not use a supported SBOM name or suffix - npm alias dependency resolution, including pnpm alias package keys, with alias context preserved in dependency paths
- production, development, optional, and peer dependency classification
- local
file:package artifact evidence - installed
node_modulespackage evidence, including npm alias install names, before network fallback - Yarn Berry
.yarn/cachepackage zip evidence before registry fallback for PnP installs withoutnode_modules - local Cargo registry source and
vendor/<crate>package evidence before unavailable fallback - local Go module cache,
vendor/<module>, and project-root-contained localreplacepath evidence before unavailable fallback forgo.workandgo.modscans - Python
.venvandvenv*.dist-info/METADATApackage evidence, plus project-root-contained local source metadata and license files foruv.lock,pylock.toml,requirements.txt,Pipfile.lock, andpdm.locklocal source entries, before unavailable fallback - local Maven
.m2/repositoryPOMs for Maven parent/BOM version management and package license evidence before unavailable fallback for Gradle lockfile and Mavenpom.xmlcoordinates - Bazel module license evidence uses local Bazel registry
local_pathsources from file-based registries when present; remote Bazel registry metadata fetching is not scanned yet - local NuGet package cache
.nuspecevidence before unavailable fallback forpackages.lock.json,obj/project.assets.json,packages.config, and*.csprojpackages - local Conan cache
conanfile.pymetadata and package source license evidence before unavailable fallback forconan.lockrecipes - local Conda package cache
info/index.jsonmetadata and license files before unavailable fallback forenvironment.yml,environment.yaml,conda-lock.yml, andconda-lock.yamlConda packages - local vcpkg
vcpkg_installed/<triplet>/share/<port>/copyrightevidence before unavailable fallback forvcpkg.jsonpackages - local Terraform
.terraform/providerslicense file evidence before unavailable fallback for.terraform.lock.hclproviders - local Helm
charts/Chart.yamlmetadata and license file evidence before unavailable fallback forChart.lockandChart.yamldependencies - local Nix path input license file evidence before unavailable fallback for
flake.lockinputs - local Unity
Packages/andLibrary/PackageCachepackage source evidence before unavailable fallback forPackages/packages-lock.jsonpackages - local R
renv/libraryDESCRIPTION metadata and license file evidence before unavailable fallback forrenv.lockpackages - local Julia depot
Project.tomlmetadata and license file evidence before unavailable fallback forManifest.tomlpackages - local Stack
.stack-work/installpackage database metadata before unavailable fallback for Hackage packages - local Carton cache archive
META.jsonorMETA.ymlmetadata before unavailable fallback for CPAN distributions - local Dart Pub cache package source evidence before unavailable fallback for
pubspec.lockpackages - local SwiftPM
.build/checkoutsand XcodeSourcePackages/checkoutspackage source evidence before unavailable fallback forPackage.resolvedpackages - local Carthage
Carthage/Checkoutspackage source evidence before unavailable fallback forCartfile.resolvedpackages - local CocoaPods
Pods/<pod>source andPods/Local Podspecs/<pod>.podspec.jsonevidence before unavailable fallback forPodfile.lockpackages - local Elixir/Erlang
deps/<package>source andmix.exsorrebar.configlicense metadata before unavailable fallback for Hex packages - local Bundler/RubyGems install path gemspec evidence before unavailable fallback for
Gemfile.lockgems - local Composer
vendor/<vendor>/<package>/composer.jsonevidence before unavailable fallback forcomposer.lockpackages - remote HTTPS package tarball evidence when the lockfile points to a tarball with supported integrity metadata, with plaintext HTTP, credential-bearing URLs, obvious local, private, special-purpose, and DNS-resolved internal hosts blocked before fetch, connected socket addresses rechecked by the default fetcher, redirects followed only after each target is validated, and transient network failures recorded as unavailable package evidence so other packages can still be scanned
- lockfile integrity verification for local and remote package tarballs; remote tarballs without integrity are reported as unavailable instead of being trusted as license evidence
- npm registry metadata lookup when the lockfile does not include a direct tarball URL
- gzipped package tarball evidence
package.jsonlicense fields- Cargo
Cargo.tomlpackagelicensefields - Python
METADATALicense-Expression,License, and recognized license classifier fields - Maven POM
<licenses>names - NuGet
.nuspec<license>expressions - Ruby gemspec
licenseandlicensesfields - Composer package
composer.jsonlicensefields - CycloneDX JSON/XML and SPDX JSON/RDF/tag-value package license declarations from SBOM metadata
- common root-level
LICENSE,LICENCE,UNLICENSE,COPYING, andNOTICEfile variants - medium-confidence standard license detection from recognizable
LICENSEandCOPYINGfile text, including SPDX identifiers, GPL-family v2/v3 text, Zlib text, public-domain-style text, and malformed metadata pointers - SPDX-like license expression parsing
- common human-readable license metadata alias normalization, including slash and comma dual-license shorthands
- low-risk classification for common permissive, Zlib, and public-domain-style SPDX licenses
- NOTICE evidence is surfaced as attribution-preservation action text without raising severity
- high-risk classification for common source-available restriction licenses
- explicit commercial restriction text detection in license evidence and package metadata
- profile-aware risk evaluation for
saasanddistributed-app - terminal, JSON, and HTML reports
- SARIF 2.1.0 reports for code scanning upload
- waived findings in SARIF output as externally suppressed results
- Markdown reports for PR comments and release notes
- browser-friendly HTML reports for local review
- CycloneDX 1.5 JSON SBOM reports with dependency relationships and Ohrisk risk decision properties
- stable finding IDs for PR comments and local waiver workflows
- local
.ohrisk-waivers.jsonwaivers by finding ID or fingerprint - stable diff matching that uses finding fingerprints so severity, recommendation, reason, or evidence changes surface without being triggered by action prose churn
- exact finding fingerprints for SARIF partial fingerprints and audit trails
- finding fingerprints in terminal and Markdown reports for waiver and audit workflows
- structured dependency type and direct/transitive scope in findings
- report file output with project-relative
--output <file>paths - optional browser opening for written HTML reports with
--openthrough a temporary127.0.0.1URL - command-specific help with
ohrisk help <command>andohrisk <command> --help - standalone license expression explanation
- git ref diff reports that show only new or meaningfully changed findings
- JSON threshold outcomes for
ci --fail-onanddiff --fail-on - terminal and Markdown threshold outcomes for
ci --fail-onanddiff --fail-on - strict CI waiver drift checks for expired or unmatched local waivers
- raw scan and CI mode with
--no-waiverswhen waiver files should be ignored - explicit waiver mode in JSON, terminal, Markdown, HTML, and SARIF reports
- explicit waiver mode in CycloneDX SBOM metadata
Central approval workflows, GitHub App checks, Go go.work use paths outside the project root, Go local replace paths outside the project root, full Go module parent graph
reconstruction, unpinned or direct-reference pyproject.toml dependencies, uv, Pipenv, and PDM remote VCS entries, uv, Pipenv, and PDM local source paths outside the project root, remote VCS requirements.txt entries, unpinned requirements ranges without exact constraint pins,
remote Maven parent/BOM fetching, Maven transitive graph
resolution, external Maven repository resolution beyond local .m2/repository, Gradle graph reconstruction, Gradle version catalog rich versions, bundle aliases, plugin aliases, and usage-site configuration reconstruction, Bazel MODULE.bazel include() expansion, Bazel overrides, module extensions, MODULE.bazel.lock graph reconstruction, remote Bazel registry metadata fetching, Conan 1 graph lock support, Conan binary package ID and remote ConanCenter
artifact fetching, unpinned or ranged Conda environment.yml specs, Conda environment transitive dependency reconstruction, explicit per-platform conda-<platform>.lock exports, remote Conda channel artifact fetching,
Conda build/channel/subdir Package URL qualifiers, Terraform module scanning, remote Terraform Registry metadata fetching, Helm transitive chart graph
reconstruction, remote Helm repository chart fetching, Nix derivation package graph reconstruction,
Nixpkgs package license extraction, vcpkg baseline-only resolution without installed status,
vcpkg feature/platform selection reconstruction, remote vcpkg registry metadata fetching, SwiftPM parent graph reconstruction,
Carthage parent graph reconstruction, remote Swift package checkout fetching,
Carthage remote checkout or binary framework license fetching, CocoaPods remote podspec or source
fetching, Mix and Rebar3 dependency graph reconstruction, Rebar3 git/path deps, Rebar3 plugin locks, remote Hex.pm artifact fetching,
Composer plugin/platform repository resolution, remote
crates.io, Go proxy, PyPI, Maven, NuGet, pub.dev, RubyGems, or Packagist artifact
fetching are not part of this slice yet.
Install with another package manager if you do not want npm:
pnpm add -g ohrisk
yarn global add ohrisk
bun add -g ohriskRun once with a package-manager exec command:
pnpm dlx ohrisk scan
yarn dlx ohrisk scan
bunx ohrisk scanRun a local scan from a supported project:
ohrisk scanBeginner HTML report flow on Windows PowerShell:
npm install -g ohrisk@latest
ohrisk version
cd C:\path\to\your\project
ohrisk scan --html --output reports\ohrisk-report.html --open
ohrisk scan --html --language ko --output reports\ohrisk-report-ko.html --open
ohrisk scan --html --language es --output reports\ohrisk-report-es.html --open
ohrisk scan --html --language fr --output reports\ohrisk-report-fr.html --open
ohrisk scan --html --language zh --output reports\ohrisk-report-zh.html --open
ohrisk scan --html --language hi --output reports\ohrisk-report-hi.html --open
ohrisk scan --html --language ja --output reports\ohrisk-report-ja.html --open
ohrisk scan --html --language id --output reports\ohrisk-report-id.html --open
ohrisk scan --html --language tr --output reports\ohrisk-report-tr.html --open
ohrisk scan --html --language ru --output reports\ohrisk-report-ru.html --open
ohrisk scan --html --language de --output reports\ohrisk-report-de.html --openThe scan prints live terminal progress while it reads the project, collects
license evidence, evaluates risk, and writes the report. In CI or redirected
stderr, Ohrisk keeps the plain append-only progress lines so logs stay readable.
When the scan succeeds, the terminal prints Wrote report to ... so you can see
the exact saved file path. With --open, Ohrisk opens the report through a
temporary 127.0.0.1 browser URL after the HTML file is written. If the browser
does not open, the scan can still succeed; open the printed HTML file path
manually.
If a lockfile contains local file: package dependencies that point to sibling
packages outside the current project repository, keep the default fail-closed
boundary and declare the monorepo/workspace root explicitly:
ohrisk scan --workspace-root .. --html --output reports/ohrisk-report.html
ohrisk ci --workspace-root C:\path\to\workspace --fail-on high
ohrisk diff main --workspace-root .. --prod--workspace-root must point to an existing directory. Local package evidence
is then trusted only when the resolved artifact stays inside the project,
repository root, or that explicit workspace root. Local packages marked
"private": true in package.json are treated as internal package evidence
when they omit public license metadata, so they do not appear as unknown
external OSS findings solely because they have no license field.
Print command help or the installed package version:
ohrisk help
ohrisk help scan
ohrisk versionSupported dependency input files:
- dependency-free
package.jsonmanifests, reported as an empty dependency graph bun.lockpackage-lock.jsonwith either a modernpackagessection or an npm v1 dependency treenpm-shrinkwrap.jsonwith the same package-lock parser supportpnpm-lock.yamlwithimporters,packages, andsnapshotssections, including default and named catalogs frompnpm-workspace.yamldeno.locknpm package entries from Deno v3/v4-style lockfilesCargo.lockcrate entries from Rust Cargo projects, using adjacentCargo.tomlroot dependencies plus literal and segment*/?Cargo workspace member manifests such ascrates/*,crates/app-*,tools/?li, andcrates/*/plugins/*when available, honoring workspaceexcludeentries,crate.workspace = truedependency keys, workspace dependency package aliases, and table-form dependency sections such as[dependencies.foo], plus local Cargo registry source for evidencego.workworkspace modules, workspacereplacedirectives, modulego.modrequirements, modulereplacedirectives, and adjacentgo.summodule versions when available, using local Go module cache,vendor/<module>, and project-root-contained local replacement path evidencego.modmodule requirements, Goreplacedirectives, and adjacentgo.summodule versions when available, using local Go module cache,vendor/<module>, and project-root-contained local replacement path evidencepylock.tomlandpylock.<name>.tomlversioned package entries and project-root-contained source-tree records from the PyPA lockfile specification, using dependency references for audit paths and installed.venv/venvdist-info metadata or local source metadata and license files for local evidencepyproject.tomlexact PEP 621 direct dependency pins such asname==version, using installed.venv/venvdist-info metadata for local evidenceuv.lockpackage entries from Python uv projects plus project-root-containeddirectoryandeditablepackage source records, using installed.venv/venvdist-info metadata or local source metadata and license files for local evidencePipfile.lockexact==versionentries and project-root-contained localpathor editable source entries from Python Pipenv projects, using installed.venv/venvdist-info metadata or local source metadata and license files for local evidencepdm.lockpackage entries and project-root-contained localpathor relativefile:source records from Python PDM projects, using adjacentpyproject.tomlroot dependencies when available and installed.venv/venvdist-info metadata or local source metadata and license files for local evidencepoetry.lockpackage entries from Python Poetry projects, using adjacentpyproject.tomlroot dependencies when available and installed.venv/venvdist-info metadata for local evidence- pinned
requirements.txtentries such asname==version, local source entries such as-e ./local-package,./local-package,file:./local-package, andname @ file:./local-package, nested-rrequirement files, and exact-cconstraint pins for ranged entries, using installed.venv/venvdist-info metadata or project-root-contained local source metadata and license files for local evidence gradle.lockfile, legacygradle/dependency-locksdirectories, and explicitgradle/dependency-locks/*.lockfileMaven coordinates from Java Gradle dependency locking, using local.m2/repositoryPOM metadata for evidencegradle/libs.versions.tomlMaven library aliases with exact versions orversion.refvalues from the same catalog, using local.m2/repositoryPOM metadata for evidence- Maven
pom.xmldirect dependencies with explicit versions or versions resolved from local<properties>, same-filedependencyManagement, or local.m2/repositoryparent/imported-BOMdependencyManagement, using local.m2/repositoryPOM metadata for evidence - Bazel
MODULE.bazeldirectbazel_depentries with literal exact versions, failing closed on graph-expanding constructs and using local Bazel registrylocal_pathsource evidence when present - .NET NuGet
packages.lock.jsonpackage entries, restoredobj/project.assets.jsonpackage graph entries,packages.configpackage entries, and direct*.csprojPackageReferenceentries, resolving central versions from the nearestDirectory.Packages.propswhen present and using local NuGet cache.nuspecmetadata for evidence - Conan 2
conan.lockrecipe references fromrequires,build_requires, andpython_requires, using local Conan cacheconanfile.pymetadata and license files for evidence - Conda
environment.ymlandenvironment.yamlexact package pins plus Condaconda-lock.ymlandconda-lock.yamlpackage entries, using local Conda package cacheinfo/index.jsonmetadata and license files for Conda package evidence - vcpkg
vcpkg.jsonmanifest dependencies resolved from installedvcpkg_installed/vcpkg/statusrecords or exact top-level overrides, using installedvcpkg_installed/<triplet>/share/<port>/copyrightfiles for evidence - Terraform
.terraform.lock.hclprovider entries, using local.terraform/providerslicense files for evidence - Helm
Chart.lockandChart.yamlchart dependency entries, using localcharts/Chart.yamlmetadata and license files for evidence - Nix
flake.lockreachable flake input entries, using local path input license files for evidence - Unity Package Manager
Packages/packages-lock.jsonpackage entries, using localPackages/andLibrary/PackageCachesource for evidence - R
renv.lockpackage records, using adjacent rootDESCRIPTIONdependency fields for production/development classification and localrenv/librarypackage source and DESCRIPTION metadata for evidence - Julia
Manifest.tomlversioned package records, using local Julia depot package source andProject.tomlmetadata for evidence - Haskell Stack
stack.yaml.lockcompleted Hackage package pins, using local Stack package database metadata before unavailable evidence fallback - Perl Carton
cpanfile.snapshotdistribution pins, using local Carton cache archive metadata before unavailable evidence fallback - LuaRocks
luarocks.lockdependency pins, using local.rockspeclicense metadata before unavailable evidence fallback - Dart/Flutter
pubspec.lockpackage entries, using local.dart_tool/package_config.jsonand Pub cache package source for evidence - SwiftPM
Package.resolvedpackage pins, using local.build/checkoutsorSourcePackages/checkoutspackage source for evidence - Carthage
Cartfile.resolvedpackage pins, using localCarthage/Checkoutspackage source for evidence - CocoaPods
Podfile.lockpod entries, using localPods/source andPods/Local Podspecsmetadata for evidence - Elixir Mix
mix.lockHex package pins, using adjacent rootmix.exsliteralonly:options for production/development classification and localdeps/package source andmix.exsmetadata for evidence; Erlang Rebar3rebar.lockHex package pins, using depth-0 production root classification and localdeps/package source andrebar.configmetadata for evidence - Ruby Bundler
Gemfile.lockgem entries, using literal companionGemfilegroup blocks and inlinegroup:options for development classification and local Bundler/RubyGems gemspec metadata for evidence - PHP Composer
composer.lockpackage entries, using adjacentcomposer.jsonroot dependencies when available and localvendor/package metadata for evidence - CycloneDX JSON/XML SBOM package entries with Package URL identities, dependency relationships, and embedded license evidence
- SPDX JSON/RDF and tag-value SBOM package entries with Package URL external refs, dependency relationships, and embedded license evidence
- Yarn classic/Berry
yarn.lockwith root and workspace dependency sets frompackage.jsonmanifests, plus local.yarn/cachezip evidence for Berry/PnP installs
Select a specific dependency input when a project contains more than one supported input file:
ohrisk scan --lockfile package-lock.json
ohrisk scan --lockfile npm-shrinkwrap.json
ohrisk ci --lockfile pnpm-lock.yaml --fail-on high
ohrisk scan --lockfile deno.lock
ohrisk scan --lockfile Cargo.lock
ohrisk scan --lockfile go.work
ohrisk scan --lockfile go.mod
ohrisk scan --lockfile pylock.toml
ohrisk scan --lockfile pyproject.toml
ohrisk scan --lockfile uv.lock
ohrisk scan --lockfile Pipfile.lock
ohrisk scan --lockfile pdm.lock
ohrisk scan --lockfile poetry.lock
ohrisk scan --lockfile requirements.txt
ohrisk scan --lockfile gradle.lockfile
ohrisk scan --lockfile gradle/dependency-locks
ohrisk scan --lockfile gradle/dependency-locks/runtimeClasspath.lockfile
ohrisk scan --lockfile gradle/libs.versions.toml
ohrisk scan --lockfile pom.xml
ohrisk scan --lockfile MODULE.bazel
ohrisk scan --lockfile packages.lock.json
ohrisk scan --lockfile obj/project.assets.json
ohrisk scan --lockfile packages.config
ohrisk scan --lockfile MyApp.csproj
ohrisk scan --lockfile conan.lock
ohrisk scan --lockfile environment.yml
ohrisk scan --lockfile conda-lock.yml
ohrisk scan --lockfile vcpkg.json
ohrisk scan --lockfile .terraform.lock.hcl
ohrisk scan --lockfile Chart.lock
ohrisk scan --lockfile Chart.yaml
ohrisk scan --lockfile flake.lock
ohrisk scan --lockfile Packages/packages-lock.json
ohrisk scan --lockfile renv.lock
ohrisk scan --lockfile Manifest.toml
ohrisk scan --lockfile stack.yaml.lock
ohrisk scan --lockfile cpanfile.snapshot
ohrisk scan --lockfile luarocks.lock
ohrisk scan --lockfile pubspec.lock
ohrisk scan --lockfile Package.resolved
ohrisk scan --lockfile Cartfile.resolved
ohrisk scan --lockfile Podfile.lock
ohrisk scan --lockfile mix.lock
ohrisk scan --lockfile rebar.lock
ohrisk scan --lockfile Gemfile.lock
ohrisk scan --lockfile composer.lock
ohrisk scan --lockfile cyclonedx.json
ohrisk scan --lockfile licenses.cdx.json
ohrisk scan --lockfile cyclonedx.xml
ohrisk scan --lockfile sbom.cdx.xml
ohrisk scan --lockfile spdx.json
ohrisk scan --lockfile licenses.spdx.json
ohrisk scan --lockfile spdx.rdf
ohrisk scan --lockfile sbom.spdx.rdf.xml
ohrisk scan --lockfile sbom.spdx
ohrisk diff main --lockfile bun.lockPick the usage profile:
ohrisk scan --profile saas
ohrisk scan --profile distributed-appLimit the scan to production-relevant dependencies by excluding development-only packages:
ohrisk scan --prodPrint machine-readable output:
ohrisk scan --jsonPrint SARIF output for code scanning upload:
ohrisk scan --sarifPrint a Markdown report:
ohrisk scan --markdown --prodPrint a CycloneDX SBOM:
ohrisk scan --cyclonedx --prodWrite browser, SARIF, SBOM, or PR reports to files:
ohrisk scan --html --output reports/ohrisk.html --open
ohrisk scan --html --language ko --output reports/ohrisk-ko.html --open
ohrisk scan --html --language es --output reports/ohrisk-es.html --open
ohrisk scan --html --language fr --output reports/ohrisk-fr.html --open
ohrisk scan --html --language zh --output reports/ohrisk-zh.html --open
ohrisk scan --html --language hi --output reports/ohrisk-hi.html --open
ohrisk scan --html --language ja --output reports/ohrisk-ja.html --open
ohrisk scan --html --language id --output reports/ohrisk-id.html --open
ohrisk scan --html --language tr --output reports/ohrisk-tr.html --open
ohrisk scan --html --language ru --output reports/ohrisk-ru.html --open
ohrisk scan --html --language de --output reports/ohrisk-de.html --open
ohrisk scan --sarif --output reports/ohrisk.sarif
ohrisk scan --cyclonedx --output reports/ohrisk.cdx.json
ohrisk diff main --prod --markdown --output reports/ohrisk-pr.md--output accepts project-relative file paths only. Absolute paths,
drive-relative paths, UNC paths, and . or .. path segments are rejected.
Fail a local CI step when findings meet a threshold:
ohrisk ci --fail-on highFail a local CI step when waiver files contain expired or unmatched waivers:
ohrisk ci --strict-waiversRun a raw audit scan or CI step without reading local waiver files:
ohrisk scan --no-waivers
ohrisk ci --no-waivers --fail-on highWaive a finding locally by ID or fingerprint in .ohrisk-waivers.json:
{
"waivers": [
{
"id": "agpl-child@0.1.0::production::transitive::fixture-bun-project>permissive-parent@1.0.0>agpl-child@0.1.0",
"reason": "Accepted for this release after internal review.",
"expiresOn": "2026-09-30"
}
]
}Waived findings are excluded from ci --fail-on threshold failures, but scan
and CI JSON, terminal, Markdown, HTML, and SARIF reports still show them. Terminal,
Markdown, and HTML reports include finding fingerprints so waiver files can target
either id or fingerprint. Expired waivers and unmatched active waivers are
reported separately in JSON, terminal, Markdown, and HTML reports and are not applied.
ci --strict-waivers exits non-zero when either expired or unmatched waivers
are present, even if active findings stay below the --fail-on threshold. JSON,
terminal, Markdown, HTML, and SARIF outputs include the strict waiver drift result
when that option is enabled. scan --no-waivers and ci --no-waivers do not
read or apply local waiver files; ci --no-waivers cannot be combined with
--strict-waivers. Reports include a waiver mode field or summary line so raw
audits can distinguish ignored waiver files from projects with no waivers.
If package names, versions, paths, reasons, or evidence text contain finding
delimiters such as ::, >, or |, Ohrisk percent-escapes those characters in
the generated IDs and fingerprints to keep waiver matching unambiguous.
Explain a license expression without scanning a project:
ohrisk explain AGPL-3.0-only --profile saasCompare the current findings against a baseline git ref:
ohrisk diff main --prod
ohrisk diff main --prod --fail-on unknown
ohrisk diff main --prod --markdownBaseline refs must be branch, tag, or commit-like names such as main,
origin/main, release/v1.2.3, or a commit hash. Git rev syntax such as
HEAD@{1}, HEAD~1, and main:path is rejected.
Print the package version:
ohrisk --versionThe terminal report is designed to show the highest-risk findings first:
Ohrisk scan
Profile: saas
Production only: yes
Risks: 1 high, 1 review, 1 unknown, 2 low
Waiver mode: local (.ohrisk-waivers.json)
Waived: 0 applied, 0 expired, 0 unmatched
Findings:
- [high] agpl-child@0.1.0
id: agpl-child@0.1.0::production::transitive::fixture-bun-project>permissive-parent@1.0.0>agpl-child@0.1.0
fingerprint: agpl-child@0.1.0::production::transitive::fixture-bun-project>permissive-parent@1.0.0>agpl-child@0.1.0::high::replace::License expression is high risk for saas.::license: AGPL-3.0-only|dependency: production|transitive dependency|source: local|package.json license: AGPL-3.0-only|file: COPYING (copying)
License expression is high risk for saas.
recommendation: replace
action: Replace this package or escalate before shipping.
dependency: production transitive
path: fixture-bun-project -> permissive-parent@1.0.0 -> agpl-child@0.1.0
evidence: license: AGPL-3.0-only; dependency: production; transitive dependency; source: local; package.json license: AGPL-3.0-only; file: COPYING (copying)
JSON output reuses the same finding model:
{
"status": "profile_risk_evaluated",
"profile": "saas",
"prodOnly": true,
"waiverMode": "local",
"nextAction": "Replace or escalate high-risk dependencies before shipping.",
"failOn": "high",
"failed": true,
"failingFindingCount": 1,
"findings": [
{
"id": "agpl-child@0.1.0::production::transitive::fixture-bun-project>permissive-parent@1.0.0>agpl-child@0.1.0",
"fingerprint": "agpl-child@0.1.0::production::transitive::fixture-bun-project>permissive-parent@1.0.0>agpl-child@0.1.0::high::replace::License expression is high risk for saas.::license: AGPL-3.0-only|dependency: production|transitive dependency",
"packageId": "agpl-child@0.1.0",
"severity": "high",
"reason": "License expression is high risk for saas.",
"recommendation": "replace",
"action": "Replace this package or escalate before shipping.",
"dependencyType": "production",
"dependencyScope": "transitive",
"paths": [
[
"fixture-bun-project",
"permissive-parent@1.0.0",
"agpl-child@0.1.0"
]
]
}
]
}Markdown output keeps the scan summary and PR-facing decision fields together:
- Licenses: `4 high-confidence`, `0 medium-confidence`, `1 low-confidence`
- License issues: `1 missing`, `0 malformed`
- Waiver mode: `local (.ohrisk-waivers.json)`
- Threshold: failed on high (1 finding at or above threshold)
| ID | Fingerprint | Severity | Package | Dependency | Reason | Recommendation | Action | Path |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `agpl-child@0.1.0::production::transitive::fixture-bun-project>permissive-parent@1.0.0>agpl-child@0.1.0` | `agpl-child@0.1.0::production::transitive::fixture-bun-project>permissive-parent@1.0.0>agpl-child@0.1.0::high::replace::License expression is high risk for saas.::license: AGPL-3.0-only\|dependency: production\|transitive dependency\|source: local\|package.json license: AGPL-3.0-only\|file: COPYING (copying)` | high | `agpl-child@0.1.0` | production transitive | License expression is high risk for saas. | replace | Replace this package or escalate before shipping. | fixture-bun-project -> permissive-parent@1.0.0 -> agpl-child@0.1.0 |Ohrisk intentionally avoids legal safe or unsafe verdicts.
low: known low-risk license expression for the selected profilereview: review before shipping under the selected profilehigh: replace or escalate before shipping under the selected profile, including explicit commercial-use restrictions and packages markedUNLICENSEDunknown: missing, malformed, or unrecognized license evidence
For example, GPL is treated differently for saas and distributed-app
because redistribution changes the risk profile.
Run the test suite:
bun testRun the release-ready local gate:
bun run verify:releaseRun the fixture scan manually:
cd test/fixtures/bun-project
bun run ../../../src/cli/main.ts scan --profile saas- Documentation Index — All guides in one place
- GitHub Actions Guide — PR gates, comments, SARIF upload, and waiver drift checks
- Risky Demo — Run the bundled fixture to see high, review, unknown, and low findings
- CI Usage Guide — GitHub Actions examples for PR gates and artifacts
- Waiver Guide — Managing license risk waivers safely
- Profile Guide — Choosing between saas and distributed-app
- Report Formats Guide — What each output format includes
- Remote Fetching Boundary — Remote evidence scope and safety rules
- 한국어 사용 가이드 — Korean usage guide for developers