{"id":51181906,"url":"https://github.com/0disoft/ohrisk","last_synced_at":"2026-07-04T07:00:57.864Z","repository":{"id":366083843,"uuid":"1273164502","full_name":"0disoft/ohrisk","owner":"0disoft","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-26T10:39:47.000Z","size":1881,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-27T07:19:27.129Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/0disoft.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-18T09:09:54.000Z","updated_at":"2026-06-26T10:39:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/0disoft/ohrisk","commit_stats":null,"previous_names":["0disoft/ohrisk"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/0disoft/ohrisk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0disoft%2Fohrisk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0disoft%2Fohrisk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0disoft%2Fohrisk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0disoft%2Fohrisk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0disoft","download_url":"https://codeload.github.com/0disoft/ohrisk/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0disoft%2Fohrisk/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35112708,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-04T02:00:05.987Z","response_time":113,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-27T07:04:35.692Z","updated_at":"2026-07-04T07:00:57.844Z","avatar_url":"https://github.com/0disoft.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ohrisk\n\nOhrisk catches open-source license risk before your PR ships.\n\nIt is a local CLI for developers who need a quick answer to questions like:\n\n- Did this dependency bring in AGPL, GPL, BUSL, or unknown license evidence?\n- Is the risky package production-relevant or dev-only?\n- Which parent package introduced the transitive risk?\n- Does the answer change for SaaS versus distributed app usage?\n\nOhrisk is a risk decision aid, not legal advice. It reports `low`, `review`,\n`high`, and `unknown` findings for the selected usage profile.\n\n## Quickstart\n\nInstall and run your first scan in under a minute:\n\n```bash\nnpm install -g ohrisk@latest\ncd your-project\nohrisk scan\n```\n\nThe terminal report shows findings sorted by severity:\n\n```text\nRisks: 1 high, 1 review, 1 unknown, 2 low\n```\n\n- `high` — replace or escalate before shipping\n- `review` — check before shipping under the selected profile\n- `unknown` — license evidence is missing or unrecognized\n- `low` — known low-risk license expression\n\nGate a production SaaS build by narrowing to production dependencies and the SaaS usage profile:\n\n```bash\nohrisk scan --profile saas --prod\n```\n\nOpen a browser-friendly HTML report:\n\n```bash\nohrisk scan --html --output ohrisk-report.html --open\n```\n\nPrefer not to install globally? Use `npx ohrisk scan` instead.\n\nReady to gate PRs? Run `ohrisk ci --fail-on high` locally, or see the [GitHub Actions guide](https://github.com/0disoft/ohrisk/blob/main/docs/github-actions.md) to wire it into CI.\n\n## When to use it\n\nRun Ohrisk when you are about to add or upgrade a dependency and want a fast,\nlocal read on whether the license evidence introduces risk for your shipping\nmodel. It sits between \"I just installed a package\" and \"legal review.\"\n\n- before opening a PR that adds or changes dependencies\n- before cutting a release or tagging a build\n- when a transitive dependency surprise appears in a lockfile diff\n- when you need a SARIF or SBOM artifact for a compliance pipeline\n\nOhrisk does not approve or block packages on its own. It gives you the\nevidence and a profile-aware severity so you can decide.\n\n## Commands\n\n| Command | What it answers |\n| --- | --- |\n| `ohrisk scan` | What does my dependency tree look like right now? Non-failing local decision aid. |\n| `ohrisk ci` | Should this PR fail the build? Runs a scan and exits non-zero when findings meet `--fail-on`. |\n| `ohrisk diff \u003cref\u003e` | What changed since the baseline git ref? Surfaces only new or meaningfully changed findings. |\n| `ohrisk explain \u003cexpr\u003e` | How would Ohrisk classify this license expression for a profile, without scanning a project? |\n\n## Usage profiles\n\nOhrisk evaluates the same dependency tree differently depending on how you ship\nsoftware, because redistribution changes license obligations.\n\n- `saas` (default): you run the service and do not redistribute the package\n  binaries to users. GPL-only copyleft such as GPL-2.0 and GPL-3.0 is treated\n  as `review` rather than an immediate block, because SaaS usage does not\n  trigger redistribution obligations. AGPL and source-available restrictions\n  remain `high`.\n- `distributed-app`: you ship the package to users. GPL becomes `high` because\n  redistribution obligations apply. Weak copyleft (LGPL, MPL, EPL) is flagged as\n  `review`.\n\nPick the profile that matches how the dependency reaches your users:\n\n```bash\nohrisk scan --profile saas\nohrisk scan --profile distributed-app\n```\n\n## Runtime\n\nOhrisk is distributed as an npm package, and the packaged CLI runs on Node.js\n`\u003e=24.0.0`. Bun is used for Ohrisk development, tests, and packaging, but users\ndo not need Bun installed to run the published CLI.\n\nOhrisk scans dependency-free `package.json` manifests, Bun, npm package-lock/shrinkwrap, pnpm, Deno npm, Yarn, Rust Cargo,\nGo modules and workspaces, Python pyproject/pylock/uv/Pipenv/PDM/Poetry/requirements.txt, Java Gradle lockfiles and version catalogs,\nMaven `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,\nTerraform provider locks, Helm chart dependency metadata, Nix flake locks,\nUnity Package Manager locks, R renv locks, Julia manifests, SwiftPM pins,\nCarthage pins, CocoaPods locks, Elixir Mix locks, Erlang Rebar3 locks, Ruby Bundler lockfiles, and PHP Composer lockfiles, plus\nCycloneDX JSON/XML, SPDX JSON/RDF, and SPDX tag-value SBOM inputs, regardless of which\npackage manager you use to install the CLI.\n\n## Current Scope\n\nThe current implementation is the first local dependency-risk vertical slice:\n\n- dependency-free `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.\u003cname\u003e.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`, and Yarn classic/Berry `yarn.lock` project discovery\n- Node-compatible packaged CLI entrypoint for npm, pnpm, Yarn, npx, pnpm dlx, and yarn dlx users\n- explicit dependency input selection with `--lockfile \u003cpath\u003e` for projects that contain more than one supported input file\n- direct and transitive dependency graph extraction when the dependency input records parent/child relationships\n- Bun, npm, pnpm, and Yarn classic/Berry workspace projects are scanned from every workspace/importer package root\n- pnpm `catalog:` and `catalog:\u003cname\u003e` dependency specifiers are resolved from `pnpm-workspace.yaml`\n- Deno `deno.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 skipped\n- Rust `Cargo.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]`\n- Go `go.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, Go `replace` directives, and adjacent `go.sum` module versions when available\n- Python `pylock.toml` and named `pylock.\u003cname\u003e.toml` projects are scanned for versioned PyPI package records and project-root-contained source-tree package records with local source metadata\n- Python `pyproject.toml` projects without a companion lockfile are scanned for exact PEP 621 `name==version` direct dependency pins\n- Python `uv.lock` projects are scanned for PyPI package dependencies recorded in the lockfile and project-root-contained `directory` or `editable` package source records\n- Python PDM `pdm.lock` and `poetry.lock` projects are scanned for PyPI package dependencies recorded in the lockfile\n- Python Pipenv `Pipfile.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` sections\n- Python PDM `pdm.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 records\n- Python `requirements.txt` files are scanned for pinned direct PyPI package dependencies, project-root-contained local source entries, editable local source entries, nested `-r` requirement files, and exact `-c` constraint pins\n- Java Gradle `gradle.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.ref`\n- Java Maven `pom.xml` projects are scanned for direct dependencies with explicit, property-resolved, same-file `dependencyManagement`, or local `.m2/repository` parent/imported-BOM `dependencyManagement` versions\n- Bazel `MODULE.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-scanned\n- .NET NuGet `packages.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 package references, including versions resolved from the nearest `Directory.Packages.props` `PackageVersion` entries\n- Conan 2 `conan.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 yet\n- Conda `environment.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\n- vcpkg `vcpkg.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\u003e=` constraints are not treated as resolved package versions\n- Terraform `.terraform.lock.hcl` projects are scanned for locked provider versions; provider constraints and platform hashes are not modeled in Package URLs yet\n- Helm `Chart.lock` and `Chart.yaml` projects are scanned for chart dependency entries; `Chart.lock` is preferred when both files are present\n- Nix `flake.lock` projects are scanned for reachable flake inputs from the root input graph; Nix derivation package graphs are not reconstructed\n- Unity Package Manager `Packages/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 yet\n- R `renv.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 yet\n- Julia `Manifest.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 yet\n- Haskell Stack `stack.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 yet\n- Perl Carton `cpanfile.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 yet\n- LuaRocks `luarocks.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 yet\n- Dart and Flutter `pubspec.lock` projects are scanned for concrete Pub package versions recorded in the lockfile\n- Swift Package Manager `Package.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 type\n- Carthage `Cartfile.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 type\n- CocoaPods `Podfile.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 groups\n- Elixir Mix `mix.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 yet\n- Erlang Rebar3 `rebar.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 yet\n- Ruby Bundler `Gemfile.lock` projects are scanned for direct and transitive gem dependencies\n- PHP Composer `composer.lock` projects are scanned for production and development package dependencies, using adjacent `composer.json` root dependencies when available\n- 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\n- explicit `--lockfile` SBOM paths are sniffed by content when their filename does not use a supported SBOM name or suffix\n- npm alias dependency resolution, including pnpm alias package keys, with alias context preserved in dependency paths\n- production, development, optional, and peer dependency classification\n- local `file:` package artifact evidence\n- installed `node_modules` package evidence, including npm alias install names, before network fallback\n- Yarn Berry `.yarn/cache` package zip evidence before registry fallback for PnP installs without `node_modules`\n- local Cargo registry source and `vendor/\u003ccrate\u003e` package evidence before unavailable fallback\n- local Go module cache, `vendor/\u003cmodule\u003e`, and project-root-contained local `replace` path evidence before unavailable fallback for `go.work` and `go.mod` scans\n- Python `.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 fallback\n- local Maven `.m2/repository` POMs for Maven parent/BOM version management and package license evidence before unavailable fallback for Gradle lockfile and Maven `pom.xml` coordinates\n- Bazel module license evidence uses local Bazel registry `local_path` sources from file-based registries when present; remote Bazel registry metadata fetching is not scanned yet\n- local NuGet package cache `.nuspec` evidence before unavailable fallback for `packages.lock.json`, `obj/project.assets.json`, `packages.config`, and `*.csproj` packages\n- local Conan cache `conanfile.py` metadata and package source license evidence before unavailable fallback for `conan.lock` recipes\n- local Conda package cache `info/index.json` metadata and license files before unavailable fallback for `environment.yml`, `environment.yaml`, `conda-lock.yml`, and `conda-lock.yaml` Conda packages\n- local vcpkg `vcpkg_installed/\u003ctriplet\u003e/share/\u003cport\u003e/copyright` evidence before unavailable fallback for `vcpkg.json` packages\n- local Terraform `.terraform/providers` license file evidence before unavailable fallback for `.terraform.lock.hcl` providers\n- local Helm `charts/` `Chart.yaml` metadata and license file evidence before unavailable fallback for `Chart.lock` and `Chart.yaml` dependencies\n- local Nix path input license file evidence before unavailable fallback for `flake.lock` inputs\n- local Unity `Packages/` and `Library/PackageCache` package source evidence before unavailable fallback for `Packages/packages-lock.json` packages\n- local R `renv/library` DESCRIPTION metadata and license file evidence before unavailable fallback for `renv.lock` packages\n- local Julia depot `Project.toml` metadata and license file evidence before unavailable fallback for `Manifest.toml` packages\n- local Stack `.stack-work/install` package database metadata before unavailable fallback for Hackage packages\n- local Carton cache archive `META.json` or `META.yml` metadata before unavailable fallback for CPAN distributions\n- local Dart Pub cache package source evidence before unavailable fallback for `pubspec.lock` packages\n- local SwiftPM `.build/checkouts` and Xcode `SourcePackages/checkouts` package source evidence before unavailable fallback for `Package.resolved` packages\n- local Carthage `Carthage/Checkouts` package source evidence before unavailable fallback for `Cartfile.resolved` packages\n- local CocoaPods `Pods/\u003cpod\u003e` source and `Pods/Local Podspecs/\u003cpod\u003e.podspec.json` evidence before unavailable fallback for `Podfile.lock` packages\n- local Elixir/Erlang `deps/\u003cpackage\u003e` source and `mix.exs` or `rebar.config` license metadata before unavailable fallback for Hex packages\n- local Bundler/RubyGems install path gemspec evidence before unavailable fallback for `Gemfile.lock` gems\n- local Composer `vendor/\u003cvendor\u003e/\u003cpackage\u003e/composer.json` evidence before unavailable fallback for `composer.lock` packages\n- 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\n- lockfile integrity verification for local and remote package tarballs; remote tarballs without integrity are reported as unavailable instead of being trusted as license evidence\n- npm registry metadata lookup when the lockfile does not include a direct tarball URL\n- gzipped package tarball evidence\n- `package.json` license fields\n- Cargo `Cargo.toml` package `license` fields\n- Python `METADATA` `License-Expression`, `License`, and recognized license classifier fields\n- Maven POM `\u003clicenses\u003e` names\n- NuGet `.nuspec` `\u003clicense\u003e` expressions\n- Ruby gemspec `license` and `licenses` fields\n- Composer package `composer.json` `license` fields\n- CycloneDX JSON/XML and SPDX JSON/RDF/tag-value package license declarations from SBOM metadata\n- common root-level `LICENSE`, `LICENCE`, `UNLICENSE`, `COPYING`, and `NOTICE` file variants\n- medium-confidence standard license detection from recognizable `LICENSE` and `COPYING` file text, including SPDX identifiers, GPL-family v2/v3 text, Zlib text, public-domain-style text, and malformed metadata pointers\n- SPDX-like license expression parsing\n- common human-readable license metadata alias normalization, including slash and comma dual-license shorthands\n- low-risk classification for common permissive, Zlib, and public-domain-style SPDX licenses\n- NOTICE evidence is surfaced as attribution-preservation action text without raising severity\n- high-risk classification for common source-available restriction licenses\n- explicit commercial restriction text detection in license evidence and package metadata\n- profile-aware risk evaluation for `saas` and `distributed-app`\n- terminal, JSON, and HTML reports\n- SARIF 2.1.0 reports for code scanning upload\n- waived findings in SARIF output as externally suppressed results\n- Markdown reports for PR comments and release notes\n- browser-friendly HTML reports for local review\n- CycloneDX 1.5 JSON SBOM reports with dependency relationships and Ohrisk risk decision properties\n- stable finding IDs for PR comments and local waiver workflows\n- local `.ohrisk-waivers.json` waivers by finding ID or fingerprint\n- stable diff matching that uses finding fingerprints so severity, recommendation, reason, or evidence changes surface without being triggered by action prose churn\n- exact finding fingerprints for SARIF partial fingerprints and audit trails\n- finding fingerprints in terminal and Markdown reports for waiver and audit workflows\n- structured dependency type and direct/transitive scope in findings\n- report file output with project-relative `--output \u003cfile\u003e` paths\n- optional browser opening for written HTML reports with `--open` through a temporary `127.0.0.1` URL\n- command-specific help with `ohrisk help \u003ccommand\u003e` and `ohrisk \u003ccommand\u003e --help`\n- standalone license expression explanation\n- git ref diff reports that show only new or meaningfully changed findings\n- JSON threshold outcomes for `ci --fail-on` and `diff --fail-on`\n- terminal and Markdown threshold outcomes for `ci --fail-on` and `diff --fail-on`\n- strict CI waiver drift checks for expired or unmatched local waivers\n- raw scan and CI mode with `--no-waivers` when waiver files should be ignored\n- explicit waiver mode in JSON, terminal, Markdown, HTML, and SARIF reports\n- explicit waiver mode in CycloneDX SBOM metadata\n\nCentral 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\nreconstruction, 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,\nremote Maven parent/BOM fetching, Maven transitive graph\nresolution, 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\nartifact fetching, unpinned or ranged Conda `environment.yml` specs, Conda environment transitive dependency reconstruction, explicit per-platform `conda-\u003cplatform\u003e.lock` exports, remote Conda channel artifact fetching,\nConda build/channel/subdir Package URL qualifiers, Terraform module scanning, remote Terraform Registry metadata fetching, Helm transitive chart graph\nreconstruction, remote Helm repository chart fetching, Nix derivation package graph reconstruction,\nNixpkgs package license extraction, vcpkg baseline-only resolution without installed status,\nvcpkg feature/platform selection reconstruction, remote vcpkg registry metadata fetching, SwiftPM parent graph reconstruction,\nCarthage parent graph reconstruction, remote Swift package checkout fetching,\nCarthage remote checkout or binary framework license fetching, CocoaPods remote podspec or source\nfetching, Mix and Rebar3 dependency graph reconstruction, Rebar3 git/path deps, Rebar3 plugin locks, remote Hex.pm artifact fetching,\nComposer plugin/platform repository resolution, remote\ncrates.io, Go proxy, PyPI, Maven, NuGet, pub.dev, RubyGems, or Packagist artifact\nfetching are not part of this slice yet.\n\n## Usage\n\nInstall with another package manager if you do not want npm:\n\n```bash\npnpm add -g ohrisk\nyarn global add ohrisk\nbun add -g ohrisk\n```\n\nRun once with a package-manager exec command:\n\n```bash\npnpm dlx ohrisk scan\nyarn dlx ohrisk scan\nbunx ohrisk scan\n```\n\nRun a local scan from a supported project:\n\n```bash\nohrisk scan\n```\n\nBeginner HTML report flow on Windows PowerShell:\n\n```powershell\nnpm install -g ohrisk@latest\nohrisk version\ncd C:\\path\\to\\your\\project\nohrisk scan --html --output reports\\ohrisk-report.html --open\n```\n\nThe scan prints live terminal progress while it reads the project, collects\nlicense evidence, evaluates risk, and writes the report. In CI or redirected\nstderr, Ohrisk keeps the plain append-only progress lines so logs stay readable.\nWhen the scan succeeds, the terminal prints `Wrote report to ...` so you can see\nthe exact saved file path. With `--open`, Ohrisk opens the report through a\ntemporary `127.0.0.1` browser URL after the HTML file is written. If the browser\ndoes not open, the scan can still succeed; open the printed HTML file path\nmanually.\n\nIf a lockfile contains local `file:` package dependencies that point to sibling\npackages outside the current project repository, keep the default fail-closed\nboundary and declare the monorepo/workspace root explicitly:\n\n```bash\nohrisk scan --workspace-root .. --html --output reports/ohrisk-report.html\nohrisk ci --workspace-root C:\\path\\to\\workspace --fail-on high\nohrisk diff main --workspace-root .. --prod\n```\n\n`--workspace-root` must point to an existing directory. Local package evidence\nis then trusted only when the resolved artifact stays inside the project,\nrepository root, or that explicit workspace root. Local packages marked\n`\"private\": true` in `package.json` are treated as internal package evidence\nwhen they omit public license metadata, so they do not appear as `unknown`\nexternal OSS findings solely because they have no license field.\n\nPrint command help or the installed package version:\n\n```bash\nohrisk help\nohrisk help scan\nohrisk version\n```\n\nSupported dependency input files:\n\n- dependency-free `package.json` manifests, reported as an empty dependency graph\n- `bun.lock`\n- `package-lock.json` with either a modern `packages` section or an npm v1 dependency tree\n- `npm-shrinkwrap.json` with the same package-lock parser support\n- `pnpm-lock.yaml` with `importers`, `packages`, and `snapshots` sections, including default and named catalogs from `pnpm-workspace.yaml`\n- `deno.lock` npm package entries from Deno v3/v4-style lockfiles\n- `Cargo.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 evidence\n- `go.work` workspace modules, workspace `replace` directives, module `go.mod` requirements, module `replace` directives, and adjacent `go.sum` module versions when available, using local Go module cache, `vendor/\u003cmodule\u003e`, and project-root-contained local replacement path evidence\n- `go.mod` module requirements, Go `replace` directives, and adjacent `go.sum` module versions when available, using local Go module cache, `vendor/\u003cmodule\u003e`, and project-root-contained local replacement path evidence\n- `pylock.toml` and `pylock.\u003cname\u003e.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 evidence\n- `pyproject.toml` exact PEP 621 direct dependency pins such as `name==version`, using installed `.venv`/`venv` dist-info metadata for local evidence\n- `uv.lock` package entries from Python uv projects plus project-root-contained `directory` and `editable` package source records, using installed `.venv`/`venv` dist-info metadata or local source metadata and license files for local evidence\n- `Pipfile.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 evidence\n- `pdm.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 evidence\n- `poetry.lock` package entries from Python Poetry projects, using adjacent `pyproject.toml` root dependencies when available and installed `.venv`/`venv` dist-info metadata for local evidence\n- pinned `requirements.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 installed `.venv`/`venv` dist-info metadata or project-root-contained local source metadata and license files for local evidence\n- `gradle.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 for evidence\n- `gradle/libs.versions.toml` Maven library aliases with exact versions or `version.ref` values from the same catalog, using local `.m2/repository` POM metadata for evidence\n- Maven `pom.xml` direct dependencies with explicit versions or versions resolved from local `\u003cproperties\u003e`, same-file `dependencyManagement`, or local `.m2/repository` parent/imported-BOM `dependencyManagement`, using local `.m2/repository` POM metadata for evidence\n- Bazel `MODULE.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 present\n- .NET NuGet `packages.lock.json` package entries, restored `obj/project.assets.json` package graph entries, `packages.config` package entries, and direct `*.csproj` `PackageReference` entries, resolving central versions from the nearest `Directory.Packages.props` when present and using local NuGet cache `.nuspec` metadata for evidence\n- Conan 2 `conan.lock` recipe references from `requires`, `build_requires`, and `python_requires`, using local Conan cache `conanfile.py` metadata and license files for evidence\n- Conda `environment.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 evidence\n- vcpkg `vcpkg.json` manifest dependencies resolved from installed `vcpkg_installed/vcpkg/status` records or exact top-level overrides, using installed `vcpkg_installed/\u003ctriplet\u003e/share/\u003cport\u003e/copyright` files for evidence\n- Terraform `.terraform.lock.hcl` provider entries, using local `.terraform/providers` license files for evidence\n- Helm `Chart.lock` and `Chart.yaml` chart dependency entries, using local `charts/` `Chart.yaml` metadata and license files for evidence\n- Nix `flake.lock` reachable flake input entries, using local path input license files for evidence\n- Unity Package Manager `Packages/packages-lock.json` package entries, using local `Packages/` and `Library/PackageCache` source for evidence\n- R `renv.lock` package records, using adjacent root `DESCRIPTION` dependency fields for production/development classification and local `renv/library` package source and DESCRIPTION metadata for evidence\n- Julia `Manifest.toml` versioned package records, using local Julia depot package source and `Project.toml` metadata for evidence\n- Haskell Stack `stack.yaml.lock` completed Hackage package pins, using local Stack package database metadata before unavailable evidence fallback\n- Perl Carton `cpanfile.snapshot` distribution pins, using local Carton cache archive metadata before unavailable evidence fallback\n- LuaRocks `luarocks.lock` dependency pins, using local `.rockspec` license metadata before unavailable evidence fallback\n- Dart/Flutter `pubspec.lock` package entries, using local `.dart_tool/package_config.json` and Pub cache package source for evidence\n- SwiftPM `Package.resolved` package pins, using local `.build/checkouts` or `SourcePackages/checkouts` package source for evidence\n- Carthage `Cartfile.resolved` package pins, using local `Carthage/Checkouts` package source for evidence\n- CocoaPods `Podfile.lock` pod entries, using local `Pods/` source and `Pods/Local Podspecs` metadata for evidence\n- Elixir Mix `mix.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 evidence\n- Ruby Bundler `Gemfile.lock` gem entries, using literal companion `Gemfile` group blocks and inline `group:` options for development classification and local Bundler/RubyGems gemspec metadata for evidence\n- PHP Composer `composer.lock` package entries, using adjacent `composer.json` root dependencies when available and local `vendor/` package metadata for evidence\n- CycloneDX JSON/XML SBOM package entries with Package URL identities, dependency relationships, and embedded license evidence\n- SPDX JSON/RDF and tag-value SBOM package entries with Package URL external refs, dependency relationships, and embedded license evidence\n- Yarn classic/Berry `yarn.lock` with root and workspace dependency sets from `package.json` manifests, plus local `.yarn/cache` zip evidence for Berry/PnP installs\n\nSelect a specific dependency input when a project contains more than one supported input file:\n\n```bash\nohrisk scan --lockfile package-lock.json\nohrisk scan --lockfile npm-shrinkwrap.json\nohrisk ci --lockfile pnpm-lock.yaml --fail-on high\nohrisk scan --lockfile deno.lock\nohrisk scan --lockfile Cargo.lock\nohrisk scan --lockfile go.work\nohrisk scan --lockfile go.mod\nohrisk scan --lockfile pylock.toml\nohrisk scan --lockfile pyproject.toml\nohrisk scan --lockfile uv.lock\nohrisk scan --lockfile Pipfile.lock\nohrisk scan --lockfile pdm.lock\nohrisk scan --lockfile poetry.lock\nohrisk scan --lockfile requirements.txt\nohrisk scan --lockfile gradle.lockfile\nohrisk scan --lockfile gradle/dependency-locks\nohrisk scan --lockfile gradle/dependency-locks/runtimeClasspath.lockfile\nohrisk scan --lockfile gradle/libs.versions.toml\nohrisk scan --lockfile pom.xml\nohrisk scan --lockfile MODULE.bazel\nohrisk scan --lockfile packages.lock.json\nohrisk scan --lockfile obj/project.assets.json\nohrisk scan --lockfile packages.config\nohrisk scan --lockfile MyApp.csproj\nohrisk scan --lockfile conan.lock\nohrisk scan --lockfile environment.yml\nohrisk scan --lockfile conda-lock.yml\nohrisk scan --lockfile vcpkg.json\nohrisk scan --lockfile .terraform.lock.hcl\nohrisk scan --lockfile Chart.lock\nohrisk scan --lockfile Chart.yaml\nohrisk scan --lockfile flake.lock\nohrisk scan --lockfile Packages/packages-lock.json\nohrisk scan --lockfile renv.lock\nohrisk scan --lockfile Manifest.toml\nohrisk scan --lockfile stack.yaml.lock\nohrisk scan --lockfile cpanfile.snapshot\nohrisk scan --lockfile luarocks.lock\nohrisk scan --lockfile pubspec.lock\nohrisk scan --lockfile Package.resolved\nohrisk scan --lockfile Cartfile.resolved\nohrisk scan --lockfile Podfile.lock\nohrisk scan --lockfile mix.lock\nohrisk scan --lockfile rebar.lock\nohrisk scan --lockfile Gemfile.lock\nohrisk scan --lockfile composer.lock\nohrisk scan --lockfile cyclonedx.json\nohrisk scan --lockfile licenses.cdx.json\nohrisk scan --lockfile cyclonedx.xml\nohrisk scan --lockfile sbom.cdx.xml\nohrisk scan --lockfile spdx.json\nohrisk scan --lockfile licenses.spdx.json\nohrisk scan --lockfile spdx.rdf\nohrisk scan --lockfile sbom.spdx.rdf.xml\nohrisk scan --lockfile sbom.spdx\nohrisk diff main --lockfile bun.lock\n```\n\nPick the usage profile:\n\n```bash\nohrisk scan --profile saas\nohrisk scan --profile distributed-app\n```\n\nLimit the scan to production-relevant dependencies by excluding development-only packages:\n\n```bash\nohrisk scan --prod\n```\n\nPrint machine-readable output:\n\n```bash\nohrisk scan --json\n```\n\nPrint SARIF output for code scanning upload:\n\n```bash\nohrisk scan --sarif\n```\n\nPrint a Markdown report:\n\n```bash\nohrisk scan --markdown --prod\n```\n\nPrint a CycloneDX SBOM:\n\n```bash\nohrisk scan --cyclonedx --prod\n```\n\nWrite browser, SARIF, SBOM, or PR reports to files:\n\n```bash\nohrisk scan --html --output reports/ohrisk.html --open\nohrisk scan --sarif --output reports/ohrisk.sarif\nohrisk scan --cyclonedx --output reports/ohrisk.cdx.json\nohrisk diff main --prod --markdown --output reports/ohrisk-pr.md\n```\n\n`--output` accepts project-relative file paths only. Absolute paths,\ndrive-relative paths, UNC paths, and `.` or `..` path segments are rejected.\n\nFail a local CI step when findings meet a threshold:\n\n```bash\nohrisk ci --fail-on high\n```\n\nFail a local CI step when waiver files contain expired or unmatched waivers:\n\n```bash\nohrisk ci --strict-waivers\n```\n\nRun a raw audit scan or CI step without reading local waiver files:\n\n```bash\nohrisk scan --no-waivers\nohrisk ci --no-waivers --fail-on high\n```\n\nWaive a finding locally by ID or fingerprint in `.ohrisk-waivers.json`:\n\n```json\n{\n  \"waivers\": [\n    {\n      \"id\": \"agpl-child@0.1.0::production::transitive::fixture-bun-project\u003epermissive-parent@1.0.0\u003eagpl-child@0.1.0\",\n      \"reason\": \"Accepted for this release after internal review.\",\n      \"expiresOn\": \"2026-09-30\"\n    }\n  ]\n}\n```\n\nWaived findings are excluded from `ci --fail-on` threshold failures, but scan\nand CI JSON, terminal, Markdown, HTML, and SARIF reports still show them. Terminal,\nMarkdown, and HTML reports include finding fingerprints so waiver files can target\neither `id` or `fingerprint`. Expired waivers and unmatched active waivers are\nreported separately in JSON, terminal, Markdown, and HTML reports and are not applied.\n`ci --strict-waivers` exits non-zero when either expired or unmatched waivers\nare present, even if active findings stay below the `--fail-on` threshold. JSON,\nterminal, Markdown, HTML, and SARIF outputs include the strict waiver drift result\nwhen that option is enabled. `scan --no-waivers` and `ci --no-waivers` do not\nread or apply local waiver files; `ci --no-waivers` cannot be combined with\n`--strict-waivers`. Reports include a waiver mode field or summary line so raw\naudits can distinguish ignored waiver files from projects with no waivers.\nIf package names, versions, paths, reasons, or evidence text contain finding\ndelimiters such as `::`, `\u003e`, or `|`, Ohrisk percent-escapes those characters in\nthe generated IDs and fingerprints to keep waiver matching unambiguous.\n\nExplain a license expression without scanning a project:\n\n```bash\nohrisk explain AGPL-3.0-only --profile saas\n```\n\nCompare the current findings against a baseline git ref:\n\n```bash\nohrisk diff main --prod\nohrisk diff main --prod --fail-on unknown\nohrisk diff main --prod --markdown\n```\n\nBaseline refs must be branch, tag, or commit-like names such as `main`,\n`origin/main`, `release/v1.2.3`, or a commit hash. Git rev syntax such as\n`HEAD@{1}`, `HEAD~1`, and `main:path` is rejected.\n\nPrint the package version:\n\n```bash\nohrisk --version\n```\n\n## Report Shape\n\nThe terminal report is designed to show the highest-risk findings first:\n\n```text\nOhrisk scan\nProfile: saas\nProduction only: yes\nRisks: 1 high, 1 review, 1 unknown, 2 low\nWaiver mode: local (.ohrisk-waivers.json)\nWaived: 0 applied, 0 expired, 0 unmatched\n\nFindings:\n- [high] agpl-child@0.1.0\n  id: agpl-child@0.1.0::production::transitive::fixture-bun-project\u003epermissive-parent@1.0.0\u003eagpl-child@0.1.0\n  fingerprint: agpl-child@0.1.0::production::transitive::fixture-bun-project\u003epermissive-parent@1.0.0\u003eagpl-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)\n  License expression is high risk for saas.\n  recommendation: replace\n  action: Replace this package or escalate before shipping.\n  dependency: production transitive\n  path: fixture-bun-project -\u003e permissive-parent@1.0.0 -\u003e agpl-child@0.1.0\n  evidence: license: AGPL-3.0-only; dependency: production; transitive dependency; source: local; package.json license: AGPL-3.0-only; file: COPYING (copying)\n```\n\nJSON output reuses the same finding model:\n\n```json\n{\n  \"status\": \"profile_risk_evaluated\",\n  \"profile\": \"saas\",\n  \"prodOnly\": true,\n  \"waiverMode\": \"local\",\n  \"nextAction\": \"Replace or escalate high-risk dependencies before shipping.\",\n  \"failOn\": \"high\",\n  \"failed\": true,\n  \"failingFindingCount\": 1,\n  \"findings\": [\n    {\n      \"id\": \"agpl-child@0.1.0::production::transitive::fixture-bun-project\u003epermissive-parent@1.0.0\u003eagpl-child@0.1.0\",\n      \"fingerprint\": \"agpl-child@0.1.0::production::transitive::fixture-bun-project\u003epermissive-parent@1.0.0\u003eagpl-child@0.1.0::high::replace::License expression is high risk for saas.::license: AGPL-3.0-only|dependency: production|transitive dependency\",\n      \"packageId\": \"agpl-child@0.1.0\",\n      \"severity\": \"high\",\n      \"reason\": \"License expression is high risk for saas.\",\n      \"recommendation\": \"replace\",\n      \"action\": \"Replace this package or escalate before shipping.\",\n      \"dependencyType\": \"production\",\n      \"dependencyScope\": \"transitive\",\n      \"paths\": [\n        [\n          \"fixture-bun-project\",\n          \"permissive-parent@1.0.0\",\n          \"agpl-child@0.1.0\"\n        ]\n      ]\n    }\n  ]\n}\n```\n\nMarkdown output keeps the scan summary and PR-facing decision fields together:\n\n```markdown\n- Licenses: `4 high-confidence`, `0 medium-confidence`, `1 low-confidence`\n- License issues: `1 missing`, `0 malformed`\n- Waiver mode: `local (.ohrisk-waivers.json)`\n- Threshold: failed on high (1 finding at or above threshold)\n\n| ID | Fingerprint | Severity | Package | Dependency | Reason | Recommendation | Action | Path |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| `agpl-child@0.1.0::production::transitive::fixture-bun-project\u003epermissive-parent@1.0.0\u003eagpl-child@0.1.0` | `agpl-child@0.1.0::production::transitive::fixture-bun-project\u003epermissive-parent@1.0.0\u003eagpl-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 -\u003e permissive-parent@1.0.0 -\u003e agpl-child@0.1.0 |\n```\n\n## Risk Language\n\nOhrisk intentionally avoids legal `safe` or `unsafe` verdicts.\n\n- `low`: known low-risk license expression for the selected profile\n- `review`: review before shipping under the selected profile\n- `high`: replace or escalate before shipping under the selected profile, including explicit commercial-use restrictions and packages marked `UNLICENSED`\n- `unknown`: missing, malformed, or unrecognized license evidence\n\nFor example, GPL is treated differently for `saas` and `distributed-app`\nbecause redistribution changes the risk profile.\n\n## Development\n\nRun the test suite:\n\n```bash\nbun test\n```\n\nRun the release-ready local gate:\n\n```bash\nbun run verify:release\n```\n\nRun the fixture scan manually:\n\n```bash\ncd test/fixtures/bun-project\nbun run ../../../src/cli/main.ts scan --profile saas\n```\n\n## Documentation\n\n- [Documentation Index](https://github.com/0disoft/ohrisk/blob/main/docs/README.md) — All guides in one place\n- [GitHub Actions Guide](https://github.com/0disoft/ohrisk/blob/main/docs/github-actions.md) — PR gates, comments, SARIF upload, and waiver drift checks\n- [Risky Demo](https://github.com/0disoft/ohrisk/blob/main/docs/risky-demo.md) — Run the bundled fixture to see high, review, unknown, and low findings\n- [CI Usage Guide](https://github.com/0disoft/ohrisk/blob/main/docs/ci.md) — GitHub Actions examples for PR gates and artifacts\n- [Waiver Guide](https://github.com/0disoft/ohrisk/blob/main/docs/waivers.md) — Managing license risk waivers safely\n- [Profile Guide](https://github.com/0disoft/ohrisk/blob/main/docs/profiles.md) — Choosing between saas and distributed-app\n- [Report Formats Guide](https://github.com/0disoft/ohrisk/blob/main/docs/report-formats.md) — What each output format includes\n- [Remote Fetching Boundary](https://github.com/0disoft/ohrisk/blob/main/docs/remote-fetching.md) — Remote evidence scope and safety rules\n- [한국어 사용 가이드](https://github.com/0disoft/ohrisk/blob/main/docs/ko/README.md) — Korean usage guide for developers\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0disoft%2Fohrisk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0disoft%2Fohrisk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0disoft%2Fohrisk/lists"}