From 47a9c7dcdb40d2c8b67fedb326ba64e6397fb36c Mon Sep 17 00:00:00 2001 From: Yarchik Date: Fri, 29 May 2026 22:38:44 +0100 Subject: [PATCH] fix(npmRegistryUtils): point users at the real env vars when integrity check fails When signature verification fails in fetchLatestStableVersion, the error tells users they can disable the check by setting COREPACK_INTEGRITY_CHECK=0 or fall back to the bundled latest release by setting COREPACK_USE_LATEST=0. Neither of those variables exists anywhere else in the codebase. The runtime actually reads COREPACK_INTEGRITY_KEYS (in shouldSkipIntegrityCheck) and COREPACK_DEFAULT_TO_LATEST (in corepackUtils.ts and Engine.ts), and both are the names documented in the README. So a user who hits the error and follows the suggestion sets two variables that have no effect, then keeps hitting the same wall. This patch rewords the error to name the variables the runtime actually checks. Behaviour of fetchLatestStableVersion is otherwise unchanged. Tests cover both axes: the failing path now mentions the real names and no longer mentions the phantom ones; and the happy path that sets COREPACK_INTEGRITY_KEYS=0 still skips verification and returns the resolved version (regression guard for the env-var name we now advertise). Fixes #849 Signed-off-by: Yarchik --- sources/npmRegistryUtils.ts | 2 +- tests/npmRegistryUtils.test.ts | 61 +++++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/sources/npmRegistryUtils.ts b/sources/npmRegistryUtils.ts index a7d110e95..f0881f024 100644 --- a/sources/npmRegistryUtils.ts +++ b/sources/npmRegistryUtils.ts @@ -80,7 +80,7 @@ export async function fetchLatestStableVersion(packageName: string) { }); } catch (cause) { // TODO: consider switching to `UsageError` when https://github.com/arcanis/clipanion/issues/157 is fixed - throw new Error(`Corepack cannot download the latest stable version of ${packageName}; you can disable signature verification by setting COREPACK_INTEGRITY_CHECK to 0 in your env, or instruct Corepack to use the latest stable release known by this version of Corepack by setting COREPACK_USE_LATEST to 0`, {cause}); + throw new Error(`Corepack cannot download the latest stable version of ${packageName}; you can disable signature verification by setting COREPACK_INTEGRITY_KEYS to 0 in your env, or instruct Corepack to use the latest stable release known by this version of Corepack by setting COREPACK_DEFAULT_TO_LATEST to 0`, {cause}); } } diff --git a/tests/npmRegistryUtils.test.ts b/tests/npmRegistryUtils.test.ts index 728d6f977..40f924a37 100644 --- a/tests/npmRegistryUtils.test.ts +++ b/tests/npmRegistryUtils.test.ts @@ -1,9 +1,9 @@ -import {Buffer} from 'node:buffer'; -import process from 'node:process'; -import {describe, beforeEach, it, expect, vi} from 'vitest'; +import {Buffer} from 'node:buffer'; +import process from 'node:process'; +import {describe, beforeEach, it, expect, vi} from 'vitest'; -import {fetchAsJson as httpFetchAsJson} from '../sources/httpUtils'; -import {DEFAULT_HEADERS, DEFAULT_NPM_REGISTRY_URL, fetchAsJson} from '../sources/npmRegistryUtils'; +import {fetchAsJson as httpFetchAsJson} from '../sources/httpUtils'; +import {DEFAULT_HEADERS, DEFAULT_NPM_REGISTRY_URL, fetchAsJson, fetchLatestStableVersion} from '../sources/npmRegistryUtils'; vi.mock(`../sources/httpUtils`); @@ -90,3 +90,54 @@ describe(`npm registry utils fetchAsJson`, () => { expect(httpFetchAsJson).lastCalledWith(`${DEFAULT_NPM_REGISTRY_URL}/package-name`, {headers: DEFAULT_HEADERS}); }); }); + +// https://github.com/nodejs/corepack/issues/849 +describe(`fetchLatestStableVersion`, () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + + it(`raises an error pointing at the real env vars when integrity verification fails`, async () => { + vi.mocked(httpFetchAsJson).mockResolvedValueOnce({ + version: `1.0.0`, + dist: { + integrity: `sha512-AAAA`, + signatures: [], + shasum: `abc`, + }, + }); + + let caught: Error | undefined; + try { + await fetchLatestStableVersion(`some-package`); + } catch (e) { + caught = e as Error; + } + expect(caught).toBeInstanceOf(Error); + // The error must steer users at the real env vars used by the runtime + // - COREPACK_INTEGRITY_KEYS is read by shouldSkipIntegrityCheck() + // - COREPACK_DEFAULT_TO_LATEST is read by the version-resolution path + expect(caught!.message).toContain(`COREPACK_INTEGRITY_KEYS to 0`); + expect(caught!.message).toContain(`COREPACK_DEFAULT_TO_LATEST to 0`); + // Neither of these names exist anywhere else in the codebase, so the + // error must not point users at them. + expect(caught!.message).not.toContain(`COREPACK_INTEGRITY_CHECK`); + expect(caught!.message).not.toContain(`COREPACK_USE_LATEST`); + expect(caught!.message).toContain(`some-package`); + }); + + it(`skips signature verification and returns when COREPACK_INTEGRITY_KEYS=0`, async () => { + process.env.COREPACK_INTEGRITY_KEYS = `0`; + vi.mocked(httpFetchAsJson).mockResolvedValueOnce({ + version: `2.3.4`, + dist: { + integrity: `sha512-BBBB`, + signatures: [], + shasum: `def`, + }, + }); + + const out = await fetchLatestStableVersion(`some-package`); + expect(out).toBe(`2.3.4+sha512.${Buffer.from(`BBBB`, `base64`).toString(`hex`)}`); + }); +});