
Product
Socket for Asana Is Now Available
Create and manage Asana tasks directly from Socket alerts, with manual task creation, automated ticketing rules, and two-way sync.
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:
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@1.14.1
cd your-project
ohrisk scan
The 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 expressionGate a production SaaS build by narrowing to production dependencies and the SaaS usage profile:
ohrisk scan --profile saas --prod
Open a browser-friendly HTML report:
ohrisk scan --html --output ohrisk-report.html --open
Scan a public GitHub repository without keeping a checkout. With --html and
no --output, the report is written to the directory where you ran Ohrisk:
ohrisk scan --html https://github.com/0disoft/laqu.git
# writes ./laqu-ohrisk.html
Git submodules are not fetched. Their paths are skipped by default and the
report is marked as incomplete; use --submodules reject when any submodule
must fail the scan instead.
Symbolic links are never followed. Remote scans remove their checkout entries, continue with ordinary dependency inputs, and record bounded skipped paths as incomplete coverage. Regular files whose names cannot be represented portably on supported systems are also excluded before checkout and reported as incomplete coverage; path traversal and repository-control paths still fail the scan.
Remote scans automatically merge supported dependency inputs found at the same
project root. Maven aggregator POMs are followed through bounded, project-contained
<module> paths, so a root packaging=pom file does not produce an empty success
while its child modules contain the actual dependencies.
Maven Central remains the default remote evidence source. A project-declared
HTTPS Maven repository is contacted only when its exact hostname is allowed by
policy or --allow-host; allowing a host does not bypass DNS, socket, redirect,
response-size, checksum, or package-identity validation:
ohrisk scan --allow-host repo.papermc.io https://github.com/PlayPro/CoreProtect
Use 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 --open
Prefer 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."
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? Separates new, meaningfully changed, and resolved findings. |
ohrisk explain <expr> | How would Ohrisk classify this license expression for a profile, optionally with license-level organization policy? |
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
as review rather than an immediate block, because SaaS usage does not
trigger redistribution obligations. AGPL and source-available restrictions
remain high. MPL file-level copyleft is low when no package copy is
redistributed; LGPL and EPL remain review.distributed-app: you ship the package to users. GPL becomes high because
redistribution obligations apply. Weak copyleft (LGPL, MPL, EPL) is flagged as
review.Pick the profile that matches how the dependency reaches your users:
ohrisk scan --profile saas
ohrisk scan --profile distributed-app
Ohrisk 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:
package.json manifests, Bun bun.lock, npm package-lock.json, npm npm-shrinkwrap.json, pnpm pnpm-lock.yaml, Deno deno.lock, Rust Cargo.lock, Go go.work, Go go.mod, Python pyproject.toml, Python pylock.toml, named Python pylock.<name>.toml, Python uv.lock, Python Pipenv Pipfile.lock, Python PDM pdm.lock, Python poetry.lock, Python requirements.txt, Java Gradle gradle.lockfile, Java Gradle gradle/dependency-locks directories and gradle/dependency-locks/*.lockfile, Java Gradle gradle/libs.versions.toml, Java Maven pom.xml, Bazel MODULE.bazel, .NET NuGet packages.lock.json, .NET restored obj/project.assets.json, .NET NuGet packages.config, .NET *.csproj, Conan conan.lock, Conda environment.yml, Conda environment.yaml, Conda conda-lock.yml, Conda conda-lock.yaml, vcpkg vcpkg.json, Terraform .terraform.lock.hcl, Helm Chart.lock, Helm Chart.yaml, Nix flake.lock, Unity Package Manager Packages/packages-lock.json, R renv.lock, Julia Manifest.toml, Haskell Stack stack.yaml.lock, Perl Carton cpanfile.snapshot, LuaRocks luarocks.lock, Dart/Flutter pubspec.lock, SwiftPM Package.resolved, Carthage Cartfile.resolved, CocoaPods Podfile.lock, Elixir Mix mix.lock, Erlang Rebar3 rebar.lock, Ruby Bundler Gemfile.lock, PHP Composer composer.lock, CycloneDX JSON/XML, SPDX JSON/RDF, SPDX tag-value .spdx, Yarn classic/Berry yarn.lock, and Zig build.zig.zon project discovery--lockfile <path> for projects that contain more than one supported input file--all discovery that merges every supported input at one project root, deduplicates packages by Package URL, and preserves contributing-lockfile provenancecatalog: and catalog:<name> dependency specifiers are resolved from pnpm-workspace.yamldeno.lock projects are scanned for npm package dependencies recorded in npm: specifiers; root remote URL imports and JSR packages fail closed instead of being silently skippedCargo.lock projects are scanned for crates, using adjacent Cargo.toml root dependencies plus literal and segment */? Cargo workspace member manifests such as crates/*, crates/app-*, tools/?li, and crates/*/plugins/* when available, honoring workspace exclude entries, crate.workspace = true dependency keys, workspace dependency package aliases, and table-form dependency sections such as [dependencies.foo]; traversal is iterative, retains every reachable crate, and stores at most 64 dependency paths per crate with a typed truncation diagnostic; remote scans fetch crates.io archives only from the fixed public artifact host and trust them only after Cargo.lock SHA-256 plus archive identity verificationgo.work projects are scanned across workspace modules and apply workspace replace directives before module-level replacements; Go go.mod projects are scanned for required modules and Go replace directives, using adjacent go.sum entries as exact module-zip checksums rather than treating every historical checksum as a current dependency on Go 1.17 and laterpylock.toml and named pylock.<name>.toml projects are scanned for versioned PyPI package records and project-root-contained source-tree package records with local source metadatapyproject.toml projects without a companion lockfile are scanned for exact PEP 621 name==version direct dependency pinsuv.lock projects are scanned for PyPI package dependencies, project-root-contained directory or editable package source records, and remote Git records resolved to a full immutable commit; immutable Git records remain in the graph with unavailable evidence and are never cloned or replaced with same-name PyPI evidence, while iterative graph traversal retains every reachable package and stores at most 64 dependency paths per package with a typed truncation diagnosticpdm.lock and poetry.lock projects are scanned for PyPI package dependencies recorded in the lockfilePipfile.lock projects are scanned for exact ==version PyPI package entries and project-root-contained local path or editable source entries in the default and develop sectionspdm.lock projects use adjacent pyproject.toml root dependencies when available, infer roots from lockfile dependency references otherwise, and scan project-root-contained local path or relative file: source recordsrequirements.txt files are scanned for pinned PyPI packages, project-root-contained local source entries, editable local source entries, nested -r requirement files, and exact -c constraint pins; pip-compile # via annotations restore direct/transitive package paths when every named parent is presentgradle.lockfile and legacy gradle/dependency-locks directory projects are scanned for Maven coordinates recorded in dependency locking output; explicit gradle/dependency-locks/*.lockfile files are also accepted. Java Gradle gradle/libs.versions.toml projects are scanned for exact Maven library aliases from compact notation, module plus exact version, or module plus version.refpom.xml projects are scanned for direct dependencies with explicit, property-resolved, same-file dependencyManagement, local .m2/repository, or exact Maven Central parent/imported-BOM dependencyManagement versions; remote model resolution is limited to 32 identity-checked POMs of at most 2 MiB each and eight parent/BOM levels, and is reusable through the artifact cache in --offline modeMODULE.bazel projects are scanned for direct bazel_dep entries with literal exact version strings; nodep repo_name = None entries, include() expansion, overrides, module extensions, and MODULE.bazel.lock resolution output fail closed instead of being partial-scannedpackages.lock.json and restored obj/project.assets.json projects are scanned for direct and transitive package dependencies; .NET NuGet packages.config and *.csproj files are scanned for direct PackageReference and PackageDownload items, including central PackageVersion entries and exact PackageDownload ranges resolved from unconditional same-file propertiesconan.lock projects are scanned for recipe references from requires, build_requires, and python_requires; Conan binary package IDs, settings, options, user/channel, and recipe revisions are not modeled in Package URLs yetenvironment.yml and environment.yaml projects are scanned for exact Conda name=version pins and exact pip name==version pins; Conda conda-lock.yml and conda-lock.yaml projects are scanned for resolved conda and pip package entries and are preferred when both an environment spec and conda-lock output are present; unpinned environment files are skipped only during repository-wide automatic discovery while explicit --lockfile selection remains strictvcpkg.json projects are scanned from installed vcpkg_installed/vcpkg/status records when available, or from exact top-level overrides when installed status is absent; baseline and version>= constraints are not treated as resolved package versions, and unresolved manifests are skipped only during repository-wide automatic discovery while explicit --lockfile selection remains strict.terraform.lock.hcl projects are scanned for locked provider versions; provider constraints and platform hashes are not modeled in Package URLs yetChart.lock and Chart.yaml projects are scanned for chart dependency entries; Chart.lock is preferred when both files are presentflake.lock projects are scanned for reachable flake inputs from the root input graph; Nix derivation package graphs are not reconstructedPackages/packages-lock.json projects are scanned for non-built-in package entries; Unity built-in modules, Packages/manifest.json without a lockfile, Asset Store .unitypackage archives, Addressables catalogs, and remote UPM registry metadata fetch are not scanned yetrenv.lock projects are scanned for package records in the lockfile; adjacent root DESCRIPTION Depends, Imports, LinkingTo, Suggests, and Enhances fields 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 yetManifest.toml projects are scanned for versioned [[deps.Name]] records; unversioned standard libraries are skipped, adjacent Project.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 yetstack.yaml.lock projects 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 yetcpanfile.snapshot projects are scanned for Carton snapshot v1 distribution pins and dependency paths inferred from provides and requirements; local Carton cache archive META.json or META.yml license metadata is used when present, while MetaCPAN artifact fetch is not scanned yetluarocks.lock projects are scanned for literal dependencies table package pins; local .rockspec files 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 yetpubspec.lock projects are scanned for concrete Pub package versions recorded in the lockfile; modern pub.dev hosted records retain their archive SHA-256 for verified remote evidence, while custom registries and Git/path sources remain local-onlyPackage.resolved projects 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 typeCartfile.resolved projects 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 typePodfile.lock projects 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 groupsmix.lock projects are scanned for resolved Hex package pins; adjacent root mix.exs literal only: 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 yetrebar.lock projects are scanned for Hex pkg pins; 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 yetGemfile.lock projects are scanned for direct and transitive gem dependenciescomposer.lock projects are scanned for production and development package dependencies, using adjacent composer.json root dependencies when available and package license declarations embedded in the lockfile as local evidence--lockfile SBOM paths are sniffed by content when their filename does not use a supported SBOM name or suffixfile: package artifact evidencenode_modules package evidence, including npm alias install names, before network fallback.yarn/cache package zip evidence before registry fallback for PnP installs without node_modulesvendor/<crate> evidence before a fixed public crates.io artifact fallback; remote .crate archives are trusted only after their exact Cargo.lock SHA-256, package root, and Cargo.toml identity are verifiedvendor/<module>, and project-root-contained local replace path evidence before a fixed public Go module proxy fallback for go.work and go.mod scans; remote module ZIPs are trusted only after their exact go.sum h1 checksum is verified.venv and venv *.dist-info/METADATA package evidence, plus project-root-contained local source metadata and license files for uv.lock, pylock.toml, requirements.txt, Pipfile.lock, and pdm.lock local source entries, before unavailable fallbackPKG-INFO, METADATA, and license files; a verified distribution that exceeds archive inspection limits remains unknown without aborting unrelated package findings.m2/repository POMs for Maven parent/BOM version management and package license evidence, including bounded parent-POM license inheritancescan and ci runs, with a 32-document scan limit, 2 MiB per-POM limit, eight-level model depth, exact identity checks, cache reuse, and fail-closed incomplete-model handlingMETA-INF/maven/<groupId>/<artifactId>/pom.properties identity before root or META-INF license files are trustedlocal_path sources from file-based registries when present; remote Bazel registry metadata fetching is not scanned yet.nuspec evidence before exact public nuget.org fallback for hashed packages.lock.json and obj/project.assets.json packages; remote NUPKG evidence requires the selected input SHA-512 to match the catalog SHA-512, service-index discovery, exact normalized version selection, byte-size verification, matching package and structurally parsed nuspec identity, and bounded ZIP inspection; hashless packages.config and standalone *.csproj inputs remain unavailable, with repository-URL reports directing maintainers to commit a generated packages.lock.json and local reports directing users to restore and select obj/project.assets.json when availableconanfile.py metadata and package source license evidence before unavailable fallback for conan.lock recipesinfo/index.json metadata and license files before unavailable fallback for environment.yml, environment.yaml, conda-lock.yml, and conda-lock.yaml Conda packagesvcpkg_installed/<triplet>/share/<port>/copyright evidence before unavailable fallback for vcpkg.json packages.terraform/providers license file evidence before unavailable fallback for .terraform.lock.hcl providerscharts/ Chart.yaml metadata and license file evidence before unavailable fallback for Chart.lock and Chart.yaml dependenciesflake.lock inputsPackages/ and Library/PackageCache package source evidence before unavailable fallback for Packages/packages-lock.json packagesrenv/library DESCRIPTION metadata and license file evidence before unavailable fallback for renv.lock packagesProject.toml metadata and license file evidence before unavailable fallback for Manifest.toml packages.stack-work/install package database metadata before unavailable fallback for Hackage packagesMETA.json or META.yml metadata before unavailable fallback for CPAN distributionspubspec.lock records; remote archives are trusted only after the lockfile SHA-256 and pubspec.yaml package identity match.build/checkouts and Xcode SourcePackages/checkouts package source evidence before unavailable fallback for Package.resolved packagesCarthage/Checkouts package source evidence before unavailable fallback for Cartfile.resolved packagesPods/<pod> source and Pods/Local Podspecs/<pod>.podspec.json evidence before unavailable fallback for Podfile.lock packagesdeps/<package> source and mix.exs or rebar.config license metadata before unavailable fallback for Hex packagesGemfile.lock gemscomposer.lock package license declarations, followed by local vendor/<vendor>/<package>/composer.json evidence when lockfile metadata is absentproxy.golang.org module ZIP evidence for Go dependencies with an exact go.sum h1 checksum, including official storage.googleapis.com redirects, one bounded transient-failure retry, full module-ZIP checksum verification, and root license-file inspection without npm credentialsETag/Last-Modified revalidation, valid stale-entry support in --offline mode, and automatic 2 GiB LRU trimmingohrisk cache status|prune|clear commands for cache inspection, age/size cleanup, and bounded removallicense metadata embedded by npm in modern package-lock.json and npm-shrinkwrap.json; missing, conflicting, or non-SPDX declarations continue to local or verified-tarball evidencepackage.json license fieldsCargo.toml package license fieldsMETADATA License-Expression, License, and recognized license classifier fields<licenses> names from the package POM or a bounded inherited parent POM.nuspec <license> expressionslicense and licenses fieldscomposer.json license fieldsLICENSE, LICENCE, UNLICENSE, COPYING, and NOTICE file variantsLICENSE and COPYING file text, including SPDX identifiers, GPL-family v2/v3 text, Zlib text, public-domain-style text, and malformed metadata pointerssaas and distributed-app.ohrisk-waivers.json waivers by finding ID or fingerprint--output <file> paths--open through a temporary 127.0.0.1 URLohrisk help <command> and ohrisk <command> --helpdiff --all comparison of independently discovered current and baseline input sets, including added and removed lockfilesci --fail-on and diff --fail-onci --fail-on and diff --fail-on--no-waivers when waiver files should be ignoredCentral approval workflows, GitHub App checks, Go go.work use paths outside the project root, Go local replace paths outside the project root, pre-1.17 Go module graph reconstruction beyond the conservative go.sum fallback
reconstruction, unpinned or short-reference uv remote VCS entries, 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 parent/BOM model resolution from project-declared or alternate Maven repositories, remote Maven model resolution for diff and archive inputs, Maven transitive graph
resolution, unapproved project-declared Maven repositories, 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, alternate Cargo registries and Cargo Git/path source fetching, arbitrary or private Go proxies, Maven source archives or JARs without a same-repository SHA-256 sidecar and embedded exact identity, private or alternate NuGet feeds, custom Dart Pub registries and Dart Git/path sources, 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 ohrisk
Run once with a package-manager exec command:
pnpm dlx ohrisk scan
yarn dlx ohrisk scan
bunx ohrisk scan
Run a local scan from a supported project:
ohrisk scan
Scan a public GitHub repository through a temporary shallow clone:
ohrisk scan --html https://github.com/0disoft/laqu.git
ohrisk scan --repo https://github.com/0disoft/laqu.git --json
ohrisk scan --html https://github.com/Mbed-TLS/mbedtls.git
ohrisk scan --submodules reject https://github.com/0disoft/laqu.git
The first command writes laqu-ohrisk.html in the current invocation directory.
Only public https://github.com/<owner>/<repository>[.git] URLs are accepted.
The remote scan path requires a Git executable available on PATH. Git
submodules are never fetched: the default --submodules ignore mode skips their
gitlinks and records bounded paths plus incomplete coverage in every report,
while --submodules reject preserves strict failure. Private credentials, other
hosts or protocols, unsafe or oversized trees, and --offline are rejected.
Symbolic links are removed without following their targets and recorded as
incomplete coverage. Non-portable regular paths are excluded before checkout
and reported separately. The temporary checkout is removed
after the scan. Policy, waivers, cache, and report output stay rooted in the
directory where Ohrisk was invoked; checkout-local policy and waivers are never
trusted. When the repository root has no supported input, Ohrisk recursively
selects one nested dependency project or merges multiple nested project roots
into one repository-wide graph with per-lockfile provenance. Automatic fan-out
is capped at 64 project roots and 128 inputs; --lockfile <relative-path> narrows
the scan explicitly. Absolute and traversal paths are rejected. Remote repository input is supported by scan, not ci, diff, or
the composite GitHub Action. See the CLI command contract
for exact limits.
Scan a source archive without extracting it to disk:
ohrisk scan --archive artifacts/source.zip
ohrisk ci --archive artifacts/source.tar.gz --all --fail-on high
Archive mode is available for scan and ci, not diff. It accepts ZIP, TAR,
TAR.GZ, and TGZ through a strict read-only in-memory reader; nested archives are
not opened. --archive cannot be combined with --lockfile or
--workspace-root, but it can be combined with --all. Policy and waiver files
inside an archive are never auto-loaded: .ohrisk.yml and
.ohrisk-waivers.json from the host invocation directory remain authoritative.
Encrypted or ZIP64 archives, unsupported compression and entry types, unsafe
paths, integrity failures, and archives over the documented resource limits are
rejected. See the CLI command contract
for the supported subset and exact limits.
Beginner HTML report flow on Windows PowerShell:
npm install -g ohrisk@1.14.1
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 --open
The 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 version
Supported dependency input files:
package.json manifests, reported as an empty dependency graphbun.lockpackage-lock.json with either a modern packages section or an npm v1 dependency treenpm-shrinkwrap.json with the same package-lock parser supportpnpm-lock.yaml with importers, packages, and snapshots sections, including default and named catalogs from pnpm-workspace.yamldeno.lock npm package entries from Deno v3/v4-style lockfilesCargo.lock crate entries from Rust Cargo projects, using adjacent Cargo.toml root dependencies plus literal and segment */? Cargo workspace member manifests such as crates/*, crates/app-*, tools/?li, and crates/*/plugins/* when available, honoring workspace exclude entries, crate.workspace = true dependency keys, workspace dependency package aliases, and table-form dependency sections such as [dependencies.foo], plus local Cargo registry source for evidencego.work workspace modules, workspace replace directives, module go.mod requirements, module replace directives, and adjacent go.sum ZIP checksums when available, using local Go module cache, vendor/<module>, project-root-contained local replacement paths, or checksum-verified public Go module ZIP evidencego.mod module requirements and Go replace directives, using adjacent go.sum ZIP checksums for local or fixed-public-proxy evidence; Go 1.17 and later use the complete go.mod requirement graph while earlier or versionless modules conservatively retain go.sum-only fallback nodespylock.toml and pylock.<name>.toml versioned package entries and project-root-contained source-tree records from the PyPA lockfile specification, using dependency references for audit paths and installed .venv/venv dist-info metadata or local source metadata and license files for local evidencepyproject.toml exact PEP 621 direct dependency pins such as name==version, using installed .venv/venv dist-info metadata for local evidenceuv.lock package entries from Python uv projects plus project-root-contained directory and editable records and remote Git records resolved to a full immutable commit, using installed .venv/venv dist-info metadata or local source metadata for ordinary packages while immutable Git records stay unknown until their exact commit license is verified manuallyPipfile.lock exact ==version entries and project-root-contained local path or editable source entries from Python Pipenv projects, using installed .venv/venv dist-info metadata or local source metadata and license files for local evidencepdm.lock package entries and project-root-contained local path or relative file: source records from Python PDM projects, using adjacent pyproject.toml root dependencies when available and installed .venv/venv dist-info metadata or local source metadata and license files for local evidencepoetry.lock package entries from Python Poetry projects, using adjacent pyproject.toml root dependencies when available and installed .venv/venv dist-info metadata for local evidencerequirements.txt entries such as name==version, local source entries such as -e ./local-package, ./local-package, file:./local-package, and name @ file:./local-package, nested -r requirement files, and exact -c constraint pins for ranged entries, using pip-compile # via annotations for bounded dependency-path reconstruction and installed .venv/venv dist-info metadata or project-root-contained local source metadata and license files for local evidencegradle.lockfile, legacy gradle/dependency-locks directories, and explicit gradle/dependency-locks/*.lockfile Maven coordinates from Java Gradle dependency locking, using local .m2/repository POM metadata or exact-version Maven Central POM fallback for evidencegradle/libs.versions.toml Maven library aliases with exact versions or version.ref values from the same catalog, using local .m2/repository POM metadata or exact-version Maven Central POM fallback for evidencepom.xml direct dependencies with explicit versions or versions resolved from local <properties>, same-file dependencyManagement, local .m2/repository, or exact Maven Central parent/imported-BOM dependencyManagement during filesystem scan and ci; reactor-internal module dependencies are excluded, and package licenses use local POM metadata, Maven Central, or explicitly allowed project-declared repositories with bounded parent inheritance and checksum/identity-verified JAR fallbackMODULE.bazel direct bazel_dep entries with literal exact versions, failing closed on graph-expanding constructs and using local Bazel registry local_path source evidence when presentpackages.lock.json package entries, restored obj/project.assets.json package graph entries, packages.config package entries, and direct *.csproj PackageReference or PackageDownload entries, resolving central versions from the nearest Directory.Packages.props and exact PackageDownload ranges from unconditional same-file properties when present, and using local NuGet cache .nuspec metadata before lock-and-catalog-SHA-512-verified public nuget.org NUPKG evidence for hashed inputsconan.lock recipe references from requires, build_requires, and python_requires, using local Conan cache conanfile.py metadata and license files for evidenceenvironment.yml and environment.yaml exact package pins plus Conda conda-lock.yml and conda-lock.yaml package entries, using local Conda package cache info/index.json metadata and license files for Conda package evidencevcpkg.json manifest dependencies resolved from installed vcpkg_installed/vcpkg/status records or exact top-level overrides, using installed vcpkg_installed/<triplet>/share/<port>/copyright files for evidence.terraform.lock.hcl provider entries, using local .terraform/providers license files for evidenceChart.lock and Chart.yaml chart dependency entries, using local charts/ Chart.yaml metadata and license files for evidenceflake.lock reachable flake input entries, using local path input license files for evidencePackages/packages-lock.json package entries, using local Packages/ and Library/PackageCache source for evidencerenv.lock package records, using adjacent root DESCRIPTION dependency fields for production/development classification and local renv/library package source and DESCRIPTION metadata for evidenceManifest.toml versioned package records, using local Julia depot package source and Project.toml metadata for evidencestack.yaml.lock completed Hackage package pins, using local Stack package database metadata before unavailable evidence fallbackcpanfile.snapshot distribution pins, using local Carton cache archive metadata before unavailable evidence fallbackluarocks.lock dependency pins, using local .rockspec license metadata before unavailable evidence fallbackpubspec.lock package entries, using local .dart_tool/package_config.json and Pub cache package source for evidencePackage.resolved package pins, using local .build/checkouts or SourcePackages/checkouts package source for evidenceCartfile.resolved package pins, using local Carthage/Checkouts package source for evidencePodfile.lock pod entries, using local Pods/ source and Pods/Local Podspecs metadata for evidencemix.lock Hex package pins, using adjacent root mix.exs literal only: options for production/development classification and local deps/ package source and mix.exs metadata for evidence; Erlang Rebar3 rebar.lock Hex package pins, using depth-0 production root classification and local deps/ package source and rebar.config metadata for evidenceGemfile.lock gem entries, using literal companion Gemfile group blocks and inline group: options for development classification and local Bundler/RubyGems gemspec metadata for evidencecomposer.lock package entries, using adjacent composer.json root dependencies when available, embedded lockfile licenses first, and local vendor/ package metadata when lockfile license metadata is absentyarn.lock with root and workspace dependency sets from package.json manifests, plus local .yarn/cache zip evidence for Berry/PnP installsSelect 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.lock
ohrisk diff main --all
Pick the usage profile:
ohrisk scan --profile saas
ohrisk scan --profile distributed-app
Limit the scan to production-relevant dependencies by excluding development-only packages:
ohrisk scan --prod
Print machine-readable output:
ohrisk scan --json
Print SARIF output for code scanning upload:
ohrisk scan --sarif
Print a Markdown report:
ohrisk scan --markdown --prod
Print a CycloneDX SBOM:
ohrisk scan --cyclonedx --prod
Write 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 --all --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 high
Fail a local CI step when waiver files contain expired or unmatched waivers:
ohrisk ci --strict-waivers
Run a raw audit scan or CI step without reading local waiver files:
ohrisk scan --no-waivers
ohrisk ci --no-waivers --fail-on high
Waive 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 saas
ohrisk explain "GPL-2.0-only WITH Classpath-exception-2.0" --policy .ohrisk.yml --json
Compare the current findings against a baseline git ref:
ohrisk diff main --prod
ohrisk diff main --prod --fail-on unknown
ohrisk diff main --prod --markdown
Baseline 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. diff --all discovers and
merges the current and baseline input sets independently, then reports added and
removed lockfiles alongside separate new, changed, and resolved findings.
Inspect or clean the shared artifact cache:
ohrisk cache status
ohrisk cache prune --max-age 7d --max-size 1GiB
ohrisk cache clear
The default cache follows the platform cache directory, uses a 24-hour fallback
TTL, conditionally revalidates expired HTTP entries, and automatically trims
least-recently-used content above 2 GiB. Add --json for machine-readable cache
command output or --cache-dir <path> to target an explicit cache.
Print the package version:
ohrisk --version
The 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 marked UNLICENSEDunknown: missing, malformed, or unrecognized license evidenceFor example, GPL is treated differently for saas and distributed-app
because redistribution changes the risk profile.
Run the test suite:
bun test
Run the release-ready local gate:
bun run verify:release
Run the fixture scan manually:
cd test/fixtures/bun-project
bun run ../../../src/cli/main.ts scan --profile saas
FAQs
Catch open-source license risk before your PR ships.
The npm package ohrisk receives a total of 64 weekly downloads. As such, ohrisk popularity was classified as not popular.
We found that ohrisk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Product
Create and manage Asana tasks directly from Socket alerts, with manual task creation, automated ticketing rules, and two-way sync.

Security News
Open VSX has removed three extension IDs from its malicious-extension list as the legitimate publishers they impersonated move to claim the names for themselves.

Product
Socket’s PHP and Composer support is now in Beta for all customers, with PHP reachability analysis generally available.