ohrisk 0.127.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. package/CHANGELOG.md +571 -0
  2. package/LICENSE +21 -0
  3. package/README.md +416 -0
  4. package/dist/cli.js +21875 -0
  5. package/package.json +53 -0
package/README.md ADDED
@@ -0,0 +1,416 @@
1
+ # Ohrisk
2
+
3
+ Ohrisk catches open-source license risk before your PR ships.
4
+
5
+ It is a local CLI for developers who need a quick answer to questions like:
6
+
7
+ - Did this dependency bring in AGPL, GPL, BUSL, or unknown license evidence?
8
+ - Is the risky package production-relevant or dev-only?
9
+ - Which parent package introduced the transitive risk?
10
+ - Does the answer change for SaaS versus distributed app usage?
11
+
12
+ Ohrisk is a risk decision aid, not legal advice. It reports `low`, `review`,
13
+ `high`, and `unknown` findings for the selected usage profile.
14
+
15
+ ## When to use it
16
+
17
+ Run Ohrisk when you are about to add or upgrade a dependency and want a fast,
18
+ local read on whether the license evidence introduces risk for your shipping
19
+ model. It sits between "I just installed a package" and "legal review."
20
+
21
+ - before opening a PR that adds or changes dependencies
22
+ - before cutting a release or tagging a build
23
+ - when a transitive dependency surprise appears in a lockfile diff
24
+ - when you need a SARIF or SBOM artifact for a compliance pipeline
25
+
26
+ Ohrisk does not approve or block packages on its own. It gives you the
27
+ evidence and a profile-aware severity so you can decide.
28
+
29
+ ## Commands
30
+
31
+ | Command | What it answers |
32
+ | --- | --- |
33
+ | `ohrisk scan` | What does my dependency tree look like right now? Non-failing local decision aid. |
34
+ | `ohrisk ci` | Should this PR fail the build? Runs a scan and exits non-zero when findings meet `--fail-on`. |
35
+ | `ohrisk diff <ref>` | What changed since the baseline git ref? Surfaces only new or meaningfully changed findings. |
36
+ | `ohrisk explain <expr>` | How would Ohrisk classify this license expression for a profile, without scanning a project? |
37
+
38
+ ## Usage profiles
39
+
40
+ Ohrisk evaluates the same dependency tree differently depending on how you ship
41
+ software, because redistribution changes license obligations.
42
+
43
+ - `saas` (default): you run the service and do not redistribute the package
44
+ binaries to users. GPL-only copyleft such as GPL-2.0 and GPL-3.0 is treated
45
+ as `review` rather than an immediate block, because SaaS usage does not
46
+ trigger redistribution obligations. AGPL and source-available restrictions
47
+ remain `high`.
48
+ - `distributed-app`: you ship the package to users. GPL becomes `high` because
49
+ redistribution obligations apply. Weak copyleft (LGPL, MPL, EPL) is flagged as
50
+ `review`.
51
+
52
+ Pick the profile that matches how the dependency reaches your users:
53
+
54
+ ```bash
55
+ ohrisk scan --profile saas
56
+ ohrisk scan --profile distributed-app
57
+ ```
58
+
59
+ ## Runtime
60
+
61
+ Ohrisk is distributed as an npm package, and the packaged CLI runs on Node.js
62
+ `>=20.0.0`. Bun is used for Ohrisk development, tests, and packaging, but users
63
+ do not need Bun installed to run the published CLI.
64
+
65
+ Ohrisk scans Bun, npm package-lock/shrinkwrap, pnpm, Deno npm, and Yarn v1
66
+ lockfiles regardless of which package manager you use to install the CLI.
67
+
68
+ ## Current Scope
69
+
70
+ The current implementation is the first npm-style vertical slice:
71
+
72
+ - Bun `bun.lock`, npm `package-lock.json`, npm `npm-shrinkwrap.json`, pnpm `pnpm-lock.yaml`, Deno `deno.lock`, and Yarn v1 `yarn.lock` project discovery
73
+ - Node-compatible packaged CLI entrypoint for npm, pnpm, Yarn, npx, pnpm dlx, and yarn dlx users
74
+ - explicit lockfile selection with `--lockfile <path>` for projects that contain more than one supported lockfile
75
+ - direct and transitive dependency graph extraction
76
+ - Bun, npm, pnpm, and Yarn v1 workspace projects are scanned from every workspace/importer package root
77
+ - Deno `deno.lock` projects are scanned for npm package dependencies recorded in `npm:` specifiers; remote URL imports and JSR packages are not scanned yet
78
+ - npm alias dependency resolution, including pnpm alias package keys, with alias context preserved in dependency paths
79
+ - production, development, optional, and peer dependency classification
80
+ - local `file:` package artifact evidence
81
+ - installed `node_modules` package evidence, including npm alias install names, before network fallback
82
+ - remote HTTP(S) package tarball evidence when the lockfile points to a tarball, with credential-bearing URLs, obvious local, private, special-purpose, and DNS-resolved internal hosts blocked before fetch, DNS answers rechecked at the default connection boundary, and redirects followed only after each target is validated
83
+ - lockfile integrity verification for local and remote package tarballs
84
+ - npm registry metadata lookup when the lockfile does not include a direct tarball URL
85
+ - gzipped package tarball evidence
86
+ - `package.json` license fields
87
+ - common root-level `LICENSE`, `LICENCE`, `UNLICENSE`, `COPYING`, and `NOTICE` file variants
88
+ - 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
89
+ - SPDX-like license expression parsing
90
+ - common human-readable license metadata alias normalization, including slash and comma dual-license shorthands
91
+ - low-risk classification for common permissive, Zlib, and public-domain-style SPDX licenses
92
+ - NOTICE evidence is surfaced as attribution-preservation action text without raising severity
93
+ - high-risk classification for common source-available restriction licenses
94
+ - explicit commercial restriction text detection in license evidence and package metadata
95
+ - profile-aware risk evaluation for `saas` and `distributed-app`
96
+ - terminal and JSON reports
97
+ - SARIF 2.1.0 reports for code scanning upload
98
+ - waived findings in SARIF output as externally suppressed results
99
+ - Markdown reports for PR comments and release notes
100
+ - CycloneDX 1.5 JSON SBOM reports with dependency relationships and Ohrisk risk decision properties
101
+ - stable finding IDs for PR comments and local waiver workflows
102
+ - local `.ohrisk-waivers.json` waivers by finding ID or fingerprint
103
+ - stable diff matching that ignores reason and evidence prose churn while surfacing severity, recommendation, and action changes
104
+ - exact finding fingerprints for SARIF partial fingerprints and audit trails
105
+ - finding fingerprints in terminal and Markdown reports for waiver and audit workflows
106
+ - structured dependency type and direct/transitive scope in findings
107
+ - report file output with `--output <file>`
108
+ - command-specific help with `ohrisk help <command>` and `ohrisk <command> --help`
109
+ - standalone license expression explanation
110
+ - git ref diff reports that show only new or meaningfully changed findings
111
+ - JSON threshold outcomes for `ci --fail-on` and `diff --fail-on`
112
+ - terminal and Markdown threshold outcomes for `ci --fail-on` and `diff --fail-on`
113
+ - strict CI waiver drift checks for expired or unmatched local waivers
114
+ - raw scan and CI mode with `--no-waivers` when waiver files should be ignored
115
+ - explicit waiver mode in JSON, terminal, Markdown, and SARIF reports
116
+ - explicit waiver mode in CycloneDX SBOM metadata
117
+
118
+ Central approval workflows, GitHub App checks, and ecosystem adapters beyond
119
+ npm-style lockfiles are not part of this slice yet.
120
+
121
+ ## Usage
122
+
123
+ Install globally after the package is published:
124
+
125
+ ```bash
126
+ npm install -g ohrisk
127
+ pnpm add -g ohrisk
128
+ yarn global add ohrisk
129
+ bun add -g ohrisk
130
+ ```
131
+
132
+ Run once without a global install:
133
+
134
+ ```bash
135
+ npx ohrisk scan
136
+ pnpm dlx ohrisk scan
137
+ yarn dlx ohrisk scan
138
+ bunx ohrisk scan
139
+ ```
140
+
141
+ Run a local scan from a supported project:
142
+
143
+ ```bash
144
+ ohrisk scan
145
+ ```
146
+
147
+ Print command help or the installed package version:
148
+
149
+ ```bash
150
+ ohrisk help
151
+ ohrisk help scan
152
+ ohrisk version
153
+ ```
154
+
155
+ Supported lockfiles:
156
+
157
+ - `bun.lock`
158
+ - `package-lock.json` with either a modern `packages` section or an npm v1 dependency tree
159
+ - `npm-shrinkwrap.json` with the same package-lock parser support
160
+ - `pnpm-lock.yaml` with `importers`, `packages`, and `snapshots` sections
161
+ - `deno.lock` npm package entries from Deno v3/v4-style lockfiles
162
+ - Yarn v1 `yarn.lock` with root and workspace dependency sets from `package.json` manifests
163
+
164
+ Select a specific lockfile when a project contains more than one supported lockfile:
165
+
166
+ ```bash
167
+ ohrisk scan --lockfile package-lock.json
168
+ ohrisk scan --lockfile npm-shrinkwrap.json
169
+ ohrisk ci --lockfile pnpm-lock.yaml --fail-on high
170
+ ohrisk scan --lockfile deno.lock
171
+ ohrisk diff main --lockfile bun.lock
172
+ ```
173
+
174
+ Pick the usage profile:
175
+
176
+ ```bash
177
+ ohrisk scan --profile saas
178
+ ohrisk scan --profile distributed-app
179
+ ```
180
+
181
+ Limit the scan to production-relevant dependencies by excluding development-only packages:
182
+
183
+ ```bash
184
+ ohrisk scan --prod
185
+ ```
186
+
187
+ Print machine-readable output:
188
+
189
+ ```bash
190
+ ohrisk scan --json
191
+ ```
192
+
193
+ Print SARIF output for code scanning upload:
194
+
195
+ ```bash
196
+ ohrisk scan --sarif
197
+ ```
198
+
199
+ Print a Markdown report:
200
+
201
+ ```bash
202
+ ohrisk scan --markdown --prod
203
+ ```
204
+
205
+ Print a CycloneDX SBOM:
206
+
207
+ ```bash
208
+ ohrisk scan --cyclonedx --prod
209
+ ```
210
+
211
+ Write a report to a file:
212
+
213
+ ```bash
214
+ ohrisk scan --sarif --output reports/ohrisk.sarif
215
+ ohrisk scan --cyclonedx --output reports/ohrisk.cdx.json
216
+ ohrisk diff main --prod --markdown --output reports/ohrisk-pr.md
217
+ ```
218
+
219
+ Fail a local CI step when findings meet a threshold:
220
+
221
+ ```bash
222
+ ohrisk ci --fail-on high
223
+ ```
224
+
225
+ Fail a local CI step when waiver files contain expired or unmatched waivers:
226
+
227
+ ```bash
228
+ ohrisk ci --strict-waivers
229
+ ```
230
+
231
+ Run a raw audit scan or CI step without reading local waiver files:
232
+
233
+ ```bash
234
+ ohrisk scan --no-waivers
235
+ ohrisk ci --no-waivers --fail-on high
236
+ ```
237
+
238
+ Waive a finding locally by ID or fingerprint in `.ohrisk-waivers.json`:
239
+
240
+ ```json
241
+ {
242
+ "waivers": [
243
+ {
244
+ "id": "agpl-child@0.1.0::production::transitive::fixture-bun-project>permissive-parent@1.0.0>agpl-child@0.1.0",
245
+ "reason": "Accepted for this release after internal review.",
246
+ "expiresOn": "2026-09-30"
247
+ }
248
+ ]
249
+ }
250
+ ```
251
+
252
+ Waived findings are excluded from `ci --fail-on` threshold failures, but scan
253
+ and CI JSON, terminal, Markdown, and SARIF reports still show them. Terminal
254
+ and Markdown reports include finding fingerprints so waiver files can target
255
+ either `id` or `fingerprint`. Expired waivers and unmatched active waivers are
256
+ reported separately in JSON, terminal, and Markdown reports and are not applied.
257
+ `ci --strict-waivers` exits non-zero when either expired or unmatched waivers
258
+ are present, even if active findings stay below the `--fail-on` threshold. JSON,
259
+ terminal, Markdown, and SARIF outputs include the strict waiver drift result
260
+ when that option is enabled. `scan --no-waivers` and `ci --no-waivers` do not
261
+ read or apply local waiver files; `ci --no-waivers` cannot be combined with
262
+ `--strict-waivers`. Reports include a waiver mode field or summary line so raw
263
+ audits can distinguish ignored waiver files from projects with no waivers.
264
+
265
+ Explain a license expression without scanning a project:
266
+
267
+ ```bash
268
+ ohrisk explain AGPL-3.0-only --profile saas
269
+ ```
270
+
271
+ Compare the current findings against a baseline git ref:
272
+
273
+ ```bash
274
+ ohrisk diff main --prod
275
+ ohrisk diff main --prod --fail-on unknown
276
+ ohrisk diff main --prod --markdown
277
+ ```
278
+
279
+ Print the package version:
280
+
281
+ ```bash
282
+ ohrisk --version
283
+ ```
284
+
285
+ Once installed as a package, the intended command shape is:
286
+
287
+ ```bash
288
+ ohrisk scan --profile saas --prod
289
+ ohrisk scan --lockfile package-lock.json
290
+ ohrisk scan --lockfile npm-shrinkwrap.json
291
+ ohrisk ci --fail-on high
292
+ ohrisk ci --strict-waivers
293
+ ohrisk scan --no-waivers
294
+ ohrisk ci --no-waivers --fail-on high
295
+ ohrisk scan --sarif
296
+ ohrisk scan --markdown --prod
297
+ ohrisk scan --cyclonedx --prod
298
+ ohrisk scan --sarif --output reports/ohrisk.sarif
299
+ ohrisk explain AGPL-3.0-only --profile saas
300
+ ohrisk diff main --prod --fail-on unknown
301
+ ohrisk --version
302
+ ```
303
+
304
+ ## Report Shape
305
+
306
+ The terminal report is designed to show the highest-risk findings first:
307
+
308
+ ```text
309
+ Ohrisk scan
310
+ Profile: saas
311
+ Production only: yes
312
+ Risks: 1 high, 1 review, 1 unknown, 2 low
313
+ Waiver mode: local (.ohrisk-waivers.json)
314
+ Waived: 0 applied, 0 expired, 0 unmatched
315
+
316
+ Findings:
317
+ - [high] agpl-child@0.1.0
318
+ id: agpl-child@0.1.0::production::transitive::fixture-bun-project>permissive-parent@1.0.0>agpl-child@0.1.0
319
+ 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)
320
+ License expression is high risk for saas.
321
+ recommendation: replace
322
+ action: Replace this package or escalate before shipping.
323
+ dependency: production transitive
324
+ path: fixture-bun-project -> permissive-parent@1.0.0 -> agpl-child@0.1.0
325
+ evidence: license: AGPL-3.0-only; dependency: production; transitive dependency; source: local; package.json license: AGPL-3.0-only; file: COPYING (copying)
326
+ ```
327
+
328
+ JSON output reuses the same finding model:
329
+
330
+ ```json
331
+ {
332
+ "status": "profile_risk_evaluated",
333
+ "profile": "saas",
334
+ "prodOnly": true,
335
+ "waiverMode": "local",
336
+ "nextAction": "Replace or escalate high-risk dependencies before shipping.",
337
+ "failOn": "high",
338
+ "failed": true,
339
+ "failingFindingCount": 1,
340
+ "findings": [
341
+ {
342
+ "id": "agpl-child@0.1.0::production::transitive::fixture-bun-project>permissive-parent@1.0.0>agpl-child@0.1.0",
343
+ "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",
344
+ "packageId": "agpl-child@0.1.0",
345
+ "severity": "high",
346
+ "reason": "License expression is high risk for saas.",
347
+ "recommendation": "replace",
348
+ "action": "Replace this package or escalate before shipping.",
349
+ "dependencyType": "production",
350
+ "dependencyScope": "transitive",
351
+ "paths": [
352
+ [
353
+ "fixture-bun-project",
354
+ "permissive-parent@1.0.0",
355
+ "agpl-child@0.1.0"
356
+ ]
357
+ ]
358
+ }
359
+ ]
360
+ }
361
+ ```
362
+
363
+ Markdown output keeps the scan summary and PR-facing decision fields together:
364
+
365
+ ```markdown
366
+ - Licenses: `4 high-confidence`, `0 medium-confidence`, `1 low-confidence`
367
+ - License issues: `1 missing`, `0 malformed`
368
+ - Waiver mode: `local (.ohrisk-waivers.json)`
369
+ - Threshold: failed on high (1 finding at or above threshold)
370
+
371
+ | ID | Fingerprint | Severity | Package | Dependency | Reason | Recommendation | Action | Path |
372
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- |
373
+ | `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 |
374
+ ```
375
+
376
+ ## Risk Language
377
+
378
+ Ohrisk intentionally avoids legal `safe` or `unsafe` verdicts.
379
+
380
+ - `low`: known low-risk license expression for the selected profile
381
+ - `review`: review before shipping under the selected profile
382
+ - `high`: replace or escalate before shipping under the selected profile, including explicit commercial-use restrictions and packages marked `UNLICENSED`
383
+ - `unknown`: missing, malformed, or unrecognized license evidence
384
+
385
+ For example, GPL is treated differently for `saas` and `distributed-app`
386
+ because redistribution changes the risk profile.
387
+
388
+ ## Development
389
+
390
+ Run the test suite:
391
+
392
+ ```bash
393
+ bun test
394
+ ```
395
+
396
+ Run the release-ready local gate:
397
+
398
+ ```bash
399
+ bun run verify:release
400
+ ```
401
+
402
+ Run the fixture scan manually:
403
+
404
+ ```bash
405
+ cd test/fixtures/bun-project
406
+ bun run ../../../src/cli/main.ts scan --profile saas
407
+ ```
408
+
409
+ ## Documentation
410
+
411
+ - [Documentation Index](https://github.com/0disoft/ohrisk/blob/main/docs/README.md) — All guides in one place
412
+ - [CI Usage Guide](https://github.com/0disoft/ohrisk/blob/main/docs/ci.md) — GitHub Actions examples for PR gates and artifacts
413
+ - [Waiver Guide](https://github.com/0disoft/ohrisk/blob/main/docs/waivers.md) — Managing license risk waivers safely
414
+ - [Profile Guide](https://github.com/0disoft/ohrisk/blob/main/docs/profiles.md) — Choosing between saas and distributed-app
415
+ - [Report Formats Guide](https://github.com/0disoft/ohrisk/blob/main/docs/report-formats.md) — What each output format includes
416
+ - [한국어 사용 가이드](https://github.com/0disoft/ohrisk/blob/main/docs/ko/README.md) — Korean usage guide for developers