ohrisk 0.127.0 → 0.127.1

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 (3) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/cli.js +104 -27
  3. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.127.1 - 2026-06-20
4
+
5
+ ### Fixed
6
+
7
+ - The default Node artifact fetcher now returns DNS lookup results in the shape
8
+ requested by Node's HTTP client, fixing `Invalid IP address: undefined`
9
+ failures when scanning real remote package tarballs.
10
+ - Package tarball evidence now accepts npm tarballs that use a custom
11
+ top-level directory, such as `bun/package.json`, instead of only
12
+ `package/package.json`.
13
+ - Registry metadata fallback now requests the exact package version endpoint
14
+ instead of the full package metadata document, avoiding oversized metadata
15
+ failures for packages with long histories such as `@types/node`.
16
+ - Remote package tarballs that exceed Ohrisk's size limits now produce
17
+ unavailable package evidence instead of aborting the whole repository scan.
18
+ - Package metadata now uses npm's normalized `bin` path form, avoiding publish
19
+ auto-correction warnings for the CLI entry.
20
+
3
21
  ## 0.127.0 - 2026-06-20
4
22
 
5
23
  ### Fixed
package/dist/cli.js CHANGED
@@ -15175,7 +15175,7 @@ function parseDiffArgs(argv) {
15175
15175
  }
15176
15176
 
15177
15177
  // src/cli/version.ts
15178
- var OHRISK_VERSION = "0.127.0";
15178
+ var OHRISK_VERSION = "0.127.1";
15179
15179
 
15180
15180
  // src/diff/compare.ts
15181
15181
  function diffRiskFindings(input) {
@@ -15480,7 +15480,8 @@ function collectTarballEvidence(input) {
15480
15480
  tarball: unpacked.value,
15481
15481
  maxEntries: input.maxEntries ?? PACKAGE_TARBALL_MAX_ENTRIES
15482
15482
  });
15483
- const packageJsonEntry = entries.find((entry) => normalizePackagePath(entry.path) === "package.json");
15483
+ const packageRoot = findPackageRoot(entries);
15484
+ const packageJsonEntry = packageRoot === undefined ? undefined : entries.find((entry) => normalizePackagePath(entry.path, packageRoot) === "package.json");
15484
15485
  if (!packageJsonEntry) {
15485
15486
  return err(createError({
15486
15487
  code: "PACKAGE_JSON_PARSE_FAILED",
@@ -15498,7 +15499,7 @@ function collectTarballEvidence(input) {
15498
15499
  if (!packageJson.ok) {
15499
15500
  return err(packageJson.error);
15500
15501
  }
15501
- const files = collectTarEvidenceFiles(entries);
15502
+ const files = collectTarEvidenceFiles(entries, packageRoot);
15502
15503
  const warnings = files.length === 0 ? ["No LICENSE, LICENCE, UNLICENSE, COPYING, or NOTICE file found."] : [];
15503
15504
  return ok({
15504
15505
  packageId: input.packageId,
@@ -15589,9 +15590,22 @@ function parseTarEntries(input) {
15589
15590
  }
15590
15591
  return entries;
15591
15592
  }
15592
- function collectTarEvidenceFiles(entries) {
15593
+ function findPackageRoot(entries) {
15594
+ if (entries.some((entry) => entry.path === "package.json")) {
15595
+ return "";
15596
+ }
15597
+ const roots = entries.map((entry) => {
15598
+ const match = /^([^/]+)\/package\.json$/.exec(entry.path);
15599
+ return match?.[1];
15600
+ }).filter((root) => root !== undefined).sort();
15601
+ if (roots.includes("package")) {
15602
+ return "package";
15603
+ }
15604
+ return roots[0];
15605
+ }
15606
+ function collectTarEvidenceFiles(entries, packageRoot) {
15593
15607
  return entries.map((entry) => {
15594
- const normalized = normalizePackagePath(entry.path);
15608
+ const normalized = normalizePackagePath(entry.path, packageRoot);
15595
15609
  if (!isRootPackageFile(normalized)) {
15596
15610
  return;
15597
15611
  }
@@ -15622,8 +15636,11 @@ function readLicenseFields2(packageJson) {
15622
15636
  function isObjectRecord2(value) {
15623
15637
  return typeof value === "object" && value !== null && !Array.isArray(value);
15624
15638
  }
15625
- function normalizePackagePath(path2) {
15626
- return path2.replace(/^package\//, "");
15639
+ function normalizePackagePath(path2, packageRoot) {
15640
+ if (packageRoot === "") {
15641
+ return path2;
15642
+ }
15643
+ return path2.startsWith(`${packageRoot}/`) ? path2.slice(packageRoot.length + 1) : path2;
15627
15644
  }
15628
15645
  function readNullTerminated(buffer, start, length) {
15629
15646
  const slice = buffer.subarray(start, start + length);
@@ -15911,7 +15928,7 @@ function localArtifactTooLargeError(input) {
15911
15928
  });
15912
15929
  }
15913
15930
  async function collectRegistryTarballEvidence(input) {
15914
- const metadataUrl = npmRegistryPackageUrl(input.node.name);
15931
+ const metadataUrl = npmRegistryPackageVersionUrl(input.node.name, input.node.version);
15915
15932
  const metadataUrlPreflight = await preflightRemoteArtifactFetchTarget({
15916
15933
  code: "REGISTRY_METADATA_FETCH_FAILED",
15917
15934
  packageId: input.node.id,
@@ -16250,6 +16267,9 @@ async function collectRemoteTarballEvidence(input) {
16250
16267
  }
16251
16268
  });
16252
16269
  if (!tarball.ok) {
16270
+ if (isPackageTarballTooLargeError(tarball.error)) {
16271
+ return ok(unavailableOversizedTarballEvidence(input.packageId));
16272
+ }
16253
16273
  return err(tarball.error);
16254
16274
  }
16255
16275
  const verified = verifyPackageIntegrity({
@@ -16266,6 +16286,9 @@ async function collectRemoteTarballEvidence(input) {
16266
16286
  tarball: tarball.value
16267
16287
  });
16268
16288
  if (!evidence.ok) {
16289
+ if (isPackageTarballTooLargeError(evidence.error)) {
16290
+ return ok(unavailableOversizedTarballEvidence(input.packageId));
16291
+ }
16269
16292
  return err(evidence.error);
16270
16293
  }
16271
16294
  return ok(addIntegrityWarningWhenUnverified({
@@ -16285,6 +16308,19 @@ async function collectRemoteTarballEvidence(input) {
16285
16308
  }));
16286
16309
  }
16287
16310
  }
16311
+ function isPackageTarballTooLargeError(error) {
16312
+ return error.code === "TARBALL_FETCH_FAILED" && error.message === "Package tarball response exceeded the maximum supported size." || error.code === "TARBALL_PARSE_FAILED" && error.message === "Failed to decompress package tarball evidence." && typeof error.details.maxUnpackedBytes === "number";
16313
+ }
16314
+ function unavailableOversizedTarballEvidence(packageId) {
16315
+ return {
16316
+ packageId,
16317
+ files: [],
16318
+ source: "unavailable",
16319
+ warnings: [
16320
+ "Package tarball evidence exceeded Ohrisk's size limit and was not scanned."
16321
+ ]
16322
+ };
16323
+ }
16288
16324
  function resolveExistingLocalArtifactPath(input) {
16289
16325
  const allowedRoot = realpathSync(resolveLocalArtifactRoot(input.projectRoot));
16290
16326
  const artifactPath = realpathSync(input.artifactPath);
@@ -16967,6 +17003,9 @@ function decodeIntegrityDigest(input) {
16967
17003
  const normalizedDecoded = decoded.toString("base64").replace(/=+$/, "");
16968
17004
  return normalizedDecoded === normalizedInput ? decoded : undefined;
16969
17005
  }
17006
+ function npmRegistryPackageVersionUrl(name, version) {
17007
+ return `${npmRegistryPackageUrl(name)}/${encodeURIComponent(version)}`;
17008
+ }
16970
17009
  function npmRegistryPackageUrl(name) {
16971
17010
  return `https://registry.npmjs.org/${encodeURIComponent(name).replace(/^%40/, "@")}`;
16972
17011
  }
@@ -16974,6 +17013,10 @@ function readRegistryTarballUrl(metadata, version) {
16974
17013
  if (!isRecord(metadata)) {
16975
17014
  return;
16976
17015
  }
17016
+ const dist = metadata.dist;
17017
+ if (isRecord(dist) && typeof dist.tarball === "string") {
17018
+ return dist.tarball;
17019
+ }
16977
17020
  const versions = metadata.versions;
16978
17021
  if (!isRecord(versions)) {
16979
17022
  return;
@@ -16982,11 +17025,11 @@ function readRegistryTarballUrl(metadata, version) {
16982
17025
  if (!isRecord(versionMetadata)) {
16983
17026
  return;
16984
17027
  }
16985
- const dist = versionMetadata.dist;
16986
- if (!isRecord(dist) || typeof dist.tarball !== "string") {
17028
+ const versionDist = versionMetadata.dist;
17029
+ if (!isRecord(versionDist) || typeof versionDist.tarball !== "string") {
16987
17030
  return;
16988
17031
  }
16989
- return dist.tarball;
17032
+ return versionDist.tarball;
16990
17033
  }
16991
17034
  function isRecord(value) {
16992
17035
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -17028,29 +17071,63 @@ async function defaultArtifactHostResolver(hostname) {
17028
17071
  }
17029
17072
  function secureArtifactLookup(hostname, options, callback) {
17030
17073
  defaultArtifactHostResolver(hostname).then((resolutions) => {
17031
- if (resolutions.length === 0) {
17032
- callback(new Error(`Artifact host ${normalizeUrlHostname(hostname)} returned no DNS addresses.`), "", 0);
17074
+ const selection = selectSecureArtifactLookupResponse(hostname, options, resolutions);
17075
+ if (!selection.ok) {
17076
+ respondToSecureArtifactLookupError(callback, options, selection.error);
17033
17077
  return;
17034
17078
  }
17035
- const requestedFamily = options.family === 4 || options.family === 6 ? options.family : undefined;
17036
- const familyResolutions = requestedFamily === undefined ? resolutions : resolutions.filter((resolution) => resolution.family === requestedFamily);
17037
- if (familyResolutions.length === 0) {
17038
- callback(new Error(`Artifact host ${normalizeUrlHostname(hostname)} returned no matching DNS addresses.`), "", 0);
17079
+ if (selection.value.all) {
17080
+ callback(null, selection.value.resolutions);
17039
17081
  return;
17040
17082
  }
17041
- for (const resolution of familyResolutions) {
17042
- const blockedReason = blockedRemoteArtifactHostReason(resolution.address);
17043
- if (blockedReason) {
17044
- callback(new Error(`Blocked artifact host resolution for ${normalizeUrlHostname(hostname)}: ${normalizeUrlHostname(resolution.address)} (${blockedReason}).`), "", 0);
17045
- return;
17046
- }
17047
- }
17048
- const selected = familyResolutions[0];
17049
- callback(null, selected.address, selected.family);
17083
+ callback(null, selection.value.address, selection.value.family);
17050
17084
  }).catch((cause) => {
17051
- callback(cause instanceof Error ? cause : new Error(String(cause)), "", 0);
17085
+ respondToSecureArtifactLookupError(callback, options, cause instanceof Error ? cause : new Error(String(cause)));
17086
+ });
17087
+ }
17088
+ function selectSecureArtifactLookupResponse(hostname, options, resolutions) {
17089
+ const normalizedOptions = normalizeArtifactLookupOptions(options);
17090
+ const normalizedHostname = normalizeUrlHostname(hostname);
17091
+ if (resolutions.length === 0) {
17092
+ return err(new Error(`Artifact host ${normalizedHostname} returned no DNS addresses.`));
17093
+ }
17094
+ const familyResolutions = normalizedOptions.family === undefined ? resolutions : resolutions.filter((resolution) => resolution.family === normalizedOptions.family);
17095
+ if (familyResolutions.length === 0) {
17096
+ return err(new Error(`Artifact host ${normalizedHostname} returned no matching DNS addresses.`));
17097
+ }
17098
+ for (const resolution of familyResolutions) {
17099
+ const blockedReason = blockedRemoteArtifactHostReason(resolution.address);
17100
+ if (blockedReason) {
17101
+ return err(new Error(`Blocked artifact host resolution for ${normalizedHostname}: ${normalizeUrlHostname(resolution.address)} (${blockedReason}).`));
17102
+ }
17103
+ }
17104
+ if (normalizedOptions.all) {
17105
+ return ok({
17106
+ all: true,
17107
+ resolutions: familyResolutions
17108
+ });
17109
+ }
17110
+ const selected = familyResolutions[0];
17111
+ return ok({
17112
+ all: false,
17113
+ address: selected.address,
17114
+ family: selected.family
17052
17115
  });
17053
17116
  }
17117
+ function normalizeArtifactLookupOptions(options) {
17118
+ const family = typeof options === "number" ? options : options?.family;
17119
+ return {
17120
+ all: typeof options === "object" && options?.all === true,
17121
+ family: family === 4 || family === 6 ? family : undefined
17122
+ };
17123
+ }
17124
+ function respondToSecureArtifactLookupError(callback, options, error) {
17125
+ if (normalizeArtifactLookupOptions(options).all) {
17126
+ callback(error, []);
17127
+ return;
17128
+ }
17129
+ callback(error, "", 0);
17130
+ }
17054
17131
  function headersForIncomingMessage(headers) {
17055
17132
  return {
17056
17133
  get: (name) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ohrisk",
3
- "version": "0.127.0",
3
+ "version": "0.127.1",
4
4
  "description": "Catch open-source license risk before your PR ships.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,7 +9,7 @@
9
9
  "node": ">=20.0.0"
10
10
  },
11
11
  "bin": {
12
- "ohrisk": "./dist/cli.js"
12
+ "ohrisk": "dist/cli.js"
13
13
  },
14
14
  "repository": {
15
15
  "type": "git",