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.
- package/CHANGELOG.md +18 -0
- package/dist/cli.js +104 -27
- 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.
|
|
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
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
|
16986
|
-
if (!isRecord(
|
|
17028
|
+
const versionDist = versionMetadata.dist;
|
|
17029
|
+
if (!isRecord(versionDist) || typeof versionDist.tarball !== "string") {
|
|
16987
17030
|
return;
|
|
16988
17031
|
}
|
|
16989
|
-
return
|
|
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
|
-
|
|
17032
|
-
|
|
17074
|
+
const selection = selectSecureArtifactLookupResponse(hostname, options, resolutions);
|
|
17075
|
+
if (!selection.ok) {
|
|
17076
|
+
respondToSecureArtifactLookupError(callback, options, selection.error);
|
|
17033
17077
|
return;
|
|
17034
17078
|
}
|
|
17035
|
-
|
|
17036
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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": "
|
|
12
|
+
"ohrisk": "dist/cli.js"
|
|
13
13
|
},
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|