diff --git a/.yarn/patches/tuf-js-npm-3.0.1-9135d15fbd.patch b/.yarn/patches/tuf-js-npm-3.0.1-9135d15fbd.patch new file mode 100644 index 000000000..05b9ab7ac --- /dev/null +++ b/.yarn/patches/tuf-js-npm-3.0.1-9135d15fbd.patch @@ -0,0 +1,31 @@ +diff --git a/dist/fetcher.js b/dist/fetcher.js +index f966ce1bb0cdc6c785ce1263f1faea15d3fe764c..111588c64ba0cc049cabeb471d39f0fdc78bbb7e 100644 +--- a/dist/fetcher.js ++++ b/dist/fetcher.js +@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); + exports.DefaultFetcher = exports.BaseFetcher = void 0; + const debug_1 = __importDefault(require("debug")); + const fs_1 = __importDefault(require("fs")); +-const make_fetch_happen_1 = __importDefault(require("make-fetch-happen")); ++const stream = require("node:stream") + const util_1 = __importDefault(require("util")); + const error_1 = require("./error"); + const tmpfile_1 = require("./utils/tmpfile"); +@@ -61,14 +61,12 @@ class DefaultFetcher extends BaseFetcher { + } + async fetch(url) { + log('GET %s', url); +- const response = await (0, make_fetch_happen_1.default)(url, { +- timeout: this.timeout, +- retry: this.retry, +- }); ++ const response = await globalThis.tufJsFetch(url); ++ + if (!response.ok || !response?.body) { + throw new error_1.DownloadHTTPError('Failed to download', response.status); + } +- return response.body; ++ return stream.Readable.fromWeb(response.body); + } + } + exports.DefaultFetcher = DefaultFetcher; diff --git a/README.md b/README.md index 67d396d5c..d6342c94c 100644 --- a/README.md +++ b/README.md @@ -355,8 +355,17 @@ same major line. Should you need to upgrade to a new major, use an explicit [`proxy-from-env`](https://github.com/Rob--W/proxy-from-env). - `COREPACK_INTEGRITY_KEYS` can be set to an empty string or `0` to - instruct Corepack to skip integrity checks, or to a JSON string containing - custom keys. + instruct Corepack to skip signature verification, or to a JSON string + containing custom keys. The format based on the response of the + `GET /-/npm/v1/keys` endpoint of npm registry under the `npm` key. That is, + + ```bash + curl https://registry.npmjs.org/-/npm/v1/keys | jq -c '{npm: .keys}' + ``` + + See the [npm documentation on + signatures](https://docs.npmjs.com/about-registry-signatures) for more + information. ## Troubleshooting diff --git a/package.json b/package.json index 59162a07d..4909f09ad 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,8 @@ "license": "MIT", "packageManager": "yarn@4.6.0+sha512.5383cc12567a95f1d668fbe762dfe0075c595b4bfff433be478dbbe24e05251a8e8c3eb992a986667c1d53b6c3a9c85b8398c35a960587fbd9fa3a0915406728", "devDependencies": { + "@sigstore/tuf": "^3.1.0", + "@sinonjs/fake-timers": "^14.0.0", "@types/debug": "^4.1.5", "@types/node": "^20.4.6", "@types/proxy-from-env": "^1", @@ -43,7 +45,8 @@ "which": "^5.0.0" }, "resolutions": { - "undici-types": "6.x" + "undici-types": "6.x", + "tuf-js@npm:^3.0.1": "patch:tuf-js@npm%3A3.0.1#~/.yarn/patches/tuf-js-npm-3.0.1-9135d15fbd.patch" }, "scripts": { "build": "run clean && run build:bundle && tsx ./mkshims.ts", diff --git a/sources/corepackUtils.ts b/sources/corepackUtils.ts index 50a808808..212ef2ae7 100644 --- a/sources/corepackUtils.ts +++ b/sources/corepackUtils.ts @@ -289,7 +289,7 @@ export async function installVersion(installTarget: string, locator: Locator, {s if (signatures! == null || integrity! == null) ({signatures, integrity} = (await npmRegistryUtils.fetchTarballURLAndSignature(registry.package, version))); - npmRegistryUtils.verifySignature({signatures, integrity, packageName: registry.package, version}); + await npmRegistryUtils.verifySignature({signatures, integrity, packageName: registry.package, version}); // @ts-expect-error ignore readonly build[1] = Buffer.from(integrity.slice(`sha512-`.length), `base64`).toString(`hex`); } diff --git a/sources/httpUtils.ts b/sources/httpUtils.ts index 6c2dc9040..556bf8e84 100644 --- a/sources/httpUtils.ts +++ b/sources/httpUtils.ts @@ -92,7 +92,7 @@ export async function fetchUrlStream(input: string | URL, init?: RequestInit) { let ProxyAgent: typeof import('undici').ProxyAgent; -async function getProxyAgent(input: string | URL) { +export async function getProxyAgent(input: string | URL) { const {getProxyForUrl} = await import(`proxy-from-env`); // @ts-expect-error - The internal implementation is compatible with a WHATWG URL instance diff --git a/sources/npmRegistryUtils.ts b/sources/npmRegistryUtils.ts index a7d110e95..975d048d7 100644 --- a/sources/npmRegistryUtils.ts +++ b/sources/npmRegistryUtils.ts @@ -1,9 +1,14 @@ +import * as sigstoreTuf from '@sigstore/tuf'; import {UsageError} from 'clipanion'; -import {createVerify} from 'crypto'; +import assert from 'node:assert'; +import * as crypto from 'node:crypto'; +import * as path from 'node:path'; import defaultConfig from '../config.json'; import {shouldSkipIntegrityCheck} from './corepackUtils'; +import * as debugUtils from './debugUtils'; +import * as folderUtils from './folderUtils'; import * as httpUtils from './httpUtils'; // load abbreviated metadata as that's all we need for these calls @@ -32,7 +37,83 @@ export async function fetchAsJson(packageName: string, version?: string) { return httpUtils.fetchAsJson(`${npmRegistryUrl}/${packageName}${version ? `/${version}` : ``}`, {headers}); } -export function verifySignature({signatures, integrity, packageName, version}: { +interface KeyInfo { + keyid: string; + // base64 encoded DER SPKI + keyData: string; +} + +async function fetchSigstoreTufKeys(): Promise | null> { + // This follows the implementation for npm. + // See https://github.com/npm/cli/blob/3a80a7b7d168c23b5e297cba7b47ba5b9875934d/lib/utils/verify-signatures.js#L174 + let keysRaw: string; + try { + // @ts-expect-error inject custom fetch into monkey-patched `tuf-js` module. + globalThis.tufJsFetch = async (input: string) => { + const agent = await httpUtils.getProxyAgent(input); + return await globalThis.fetch(input, { + dispatcher: agent, + }); + }; + const sigstoreTufClient = await sigstoreTuf.initTUF({ + cachePath: path.join(folderUtils.getCorepackHomeFolder(), `_tuf`), + }); + keysRaw = await sigstoreTufClient.getTarget(`registry.npmjs.org/keys.json`); + } catch (error) { + console.warn(`Warning: Failed to get signing keys from Sigstore TUF repo`, error); + return null; + } + + // The format of the key file is undocumented but follows `PublicKey` from + // sigstore/protobuf-specs. + // See https://github.com/sigstore/protobuf-specs/blob/main/gen/pb-typescript/src/__generated__/sigstore_common.ts + const keysFromSigstore = JSON.parse(keysRaw) as {keys: Array<{keyId: string, publicKey: {rawBytes: string, keyDetails: string}}>}; + + return keysFromSigstore.keys.filter(key => { + if (key.publicKey.keyDetails === `PKIX_ECDSA_P256_SHA_256`) { + return true; + } else { + debugUtils.log(`Unsupported verification key type ${key.publicKey.keyDetails}`); + return false; + } + }).map(k => ({ + keyid: k.keyId, + keyData: k.publicKey.rawBytes, + })); +} + +async function getVerificationKeys(): Promise> { + let keys: Array<{keyid: string, key: string}>; + + if (process.env.COREPACK_INTEGRITY_KEYS) { + // We use the format of the `GET /-/npm/v1/keys` endpoint with `npm` instead + // of `keys` as the wrapping key. + const keysFromEnv = JSON.parse(process.env.COREPACK_INTEGRITY_KEYS) as {npm: Array<{keyid: string, key: string}>}; + keys = keysFromEnv.npm; + debugUtils.log(`Using COREPACK_INTEGRITY_KEYS to verify signatures: ${keys.map(k => k.keyid).join(`, `)}`); + return keys.map(k => ({ + keyid: k.keyid, + keyData: k.key, + })); + } + + + const sigstoreKeys = await fetchSigstoreTufKeys(); + if (sigstoreKeys) { + debugUtils.log(`Using NPM keys from @sigstore/tuf to verify signatures: ${sigstoreKeys.map(k => k.keyid).join(`, `)}`); + return sigstoreKeys; + } + + debugUtils.log(`Falling back to built-in npm verification keys`); + return defaultConfig.keys.npm.map(k => ({ + keyid: k.keyid, + keyData: k.key, + })); +} + +let verificationKeysCache: Promise> | null = null; + +export async function verifySignature({signatures, integrity, packageName, version}: { signatures: Array<{keyid: string, sig: string}>; integrity: string; packageName: string; @@ -40,30 +121,28 @@ export function verifySignature({signatures, integrity, packageName, version}: { }) { if (!Array.isArray(signatures) || !signatures.length) throw new Error(`No compatible signature found in package metadata`); - const {npm: trustedKeys} = process.env.COREPACK_INTEGRITY_KEYS ? - JSON.parse(process.env.COREPACK_INTEGRITY_KEYS) as typeof defaultConfig.keys : - defaultConfig.keys; - - let signature: typeof signatures[0] | undefined; - let key!: string; - for (const k of trustedKeys) { - signature = signatures.find(({keyid}) => keyid === k.keyid); - if (signature != null) { - key = k.key; - break; - } - } - if (signature?.sig == null) throw new UsageError(`The package was not signed by any trusted keys: ${JSON.stringify({signatures, trustedKeys}, undefined, 2)}`); - - const verifier = createVerify(`SHA256`); - verifier.end(`${packageName}@${version}:${integrity}`); - const valid = verifier.verify( - `-----BEGIN PUBLIC KEY-----\n${key}\n-----END PUBLIC KEY-----`, - signature.sig, - `base64`, - ); + if (!verificationKeysCache) + verificationKeysCache = getVerificationKeys(); + + const keys = await verificationKeysCache; + const keyInfo = keys.find(({keyid}) => signatures.some(s => s.keyid === keyid)); + if (keyInfo == null) + throw new Error(`Cannot find key to verify signature. signature keys: ${signatures.map(s => s.keyid)}, verification keys: ${keys.map(k => k.keyid)}`); + + const signature = signatures.find(({keyid}) => keyid === keyInfo.keyid); + assert(signature); + + const verifier = crypto.createVerify(`SHA256`); + const payload = `${packageName}@${version}:${integrity}`; + verifier.end(payload); + const key = crypto.createPublicKey({key: Buffer.from(keyInfo.keyData, `base64`), format: `der`, type: `spki`}); + const valid = verifier.verify(key, signature.sig, `base64`); + if (!valid) { - throw new Error(`Signature does not match`); + throw new Error( + `Signature verification failed for ${payload} with key ${keyInfo.keyid}\n` + + `If you are using a custom registry you can set COREPACK_INTEGRITY_KEYS.`, + ); } } @@ -74,7 +153,7 @@ export async function fetchLatestStableVersion(packageName: string) { if (!shouldSkipIntegrityCheck()) { try { - verifySignature({ + await verifySignature({ packageName, version, integrity, signatures, }); diff --git a/tests/_registryServer.mjs b/tests/_registryServer.mjs index f9e8b9db3..a366fb074 100644 --- a/tests/_registryServer.mjs +++ b/tests/_registryServer.mjs @@ -7,6 +7,14 @@ import {gzipSync} from 'node:zlib'; let privateKey, keyid; switch (process.env.TEST_INTEGRITY) { + case `invalid_npm_signature`: { + // Claim to use a known NPM signing key but provide a signature from a different key + keyid = `SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U`; + ({privateKey} = generateKeyPairSync(`ec`, { + namedCurve: `sect239k1`, + })); + break; + } case `invalid_signature`: { ({privateKey} = generateKeyPairSync(`ec`, { namedCurve: `sect239k1`, @@ -195,6 +203,7 @@ if (process.env.AUTH_TYPE === `PROXY`) { } server.listen(0, `localhost`); +server.unref(); await once(server, `listening`); const {address, port} = server.address(); @@ -222,17 +231,8 @@ switch (process.env.AUTH_TYPE) { default: throw new Error(`Invalid AUTH_TYPE in env`, {cause: process.env.AUTH_TYPE}); } -if (process.env.NOCK_ENV === `replay`) { - const originalFetch = globalThis.fetch; - globalThis.fetch = function fetch(i) { - if (!`${i}`.startsWith( - process.env.AUTH_TYPE === `PROXY` ? - `http://example.com` : - `http://${address.includes(`:`) ? `[${address}]` : address}:${port}`)) - throw new Error(`Unexpected request to ${i}`); - - return Reflect.apply(originalFetch, this, arguments); - }; -} - -server.unref(); +globalThis.fetch.passthroughUrls.push( + process.env.AUTH_TYPE === `PROXY` ? + `http://example.com` : + `http://${address.includes(`:`) ? `[${address}]` : address}:${port}`, +); diff --git a/tests/_runCli.ts b/tests/_runCli.ts index e53adec4a..2e4fad462 100644 --- a/tests/_runCli.ts +++ b/tests/_runCli.ts @@ -8,7 +8,13 @@ export async function runCli(cwd: PortablePath, argv: Array, withCustomR const err: Array = []; return new Promise((resolve, reject) => { - const child = spawn(process.execPath, [`--no-warnings`, ...(withCustomRegistry ? [`--import`, pathToFileURL(path.join(__dirname, `_registryServer.mjs`)) as any as string] : [`-r`, require.resolve(`./recordRequests.js`)]), require.resolve(`../dist/corepack.js`), ...argv], { + const child = spawn(process.execPath, [ + `--no-warnings`, + ...(withCustomRegistry ? [`--import`, pathToFileURL(path.join(__dirname, `_registryServer.mjs`)).toString()] : []), + `--require`, require.resolve(`./recordRequests.js`), + require.resolve(`../dist/corepack.js`), + ...argv, + ], { cwd: npath.fromPortablePath(cwd), env: process.env, stdio: `pipe`, diff --git a/tests/main.test.ts b/tests/main.test.ts index 256008943..3c0cae73a 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -1585,7 +1585,7 @@ describe(`handle integrity checks`, () => { await xfs.mktempPromise(async cwd => { await expect(runCli(cwd, [`pnpm@1.x`, `--version`], true)).resolves.toMatchObject({ exitCode: 1, - stderr: expect.stringContaining(`Signature does not match`), + stderr: expect.stringContaining(`Signature verification failed`), stdout: ``, }); }); @@ -1596,7 +1596,7 @@ describe(`handle integrity checks`, () => { await xfs.mktempPromise(async cwd => { await expect(runCli(cwd, [`pnpm@latest`, `--version`], true)).resolves.toMatchObject({ exitCode: 1, - stderr: expect.stringContaining(`Signature does not match`), + stderr: expect.stringContaining(`Signature verification failed`), stdout: ``, }); }); @@ -1636,23 +1636,23 @@ describe(`handle integrity checks`, () => { await xfs.mktempPromise(async cwd => { await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({ exitCode: 1, - stderr: expect.stringContaining(`Signature does not match`), + stderr: expect.stringContaining(`Signature verification failed for pnpm@1.9998.9999`), stdout: ``, }); // A second time to validate the invalid version was not cached. await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({ exitCode: 1, - stderr: expect.stringContaining(`Signature does not match`), + stderr: expect.stringContaining(`Signature verification failed for pnpm@1.9998.9999`), stdout: ``, }); await expect(runCli(cwd, [`yarn`, `--version`], true)).resolves.toMatchObject({ exitCode: 1, - stderr: expect.stringContaining(`Signature does not match`), + stderr: expect.stringContaining(`Signature verification failed for yarn@1.9998.9999`), stdout: ``, }); await expect(runCli(cwd, [`use`, `pnpm`], true)).resolves.toMatchObject({ exitCode: 1, - stdout: expect.stringContaining(`Signature does not match`), + stdout: expect.stringContaining(`Signature verification failed for pnpm@1.9998.9999`), stderr: ``, }); }); @@ -1663,12 +1663,12 @@ describe(`handle integrity checks`, () => { await xfs.mktempPromise(async cwd => { await expect(runCli(cwd, [`yarn@1.9998.9999`, `--version`], true)).resolves.toMatchObject({ exitCode: 1, - stderr: expect.stringContaining(`Signature does not match`), + stderr: expect.stringContaining(`Signature verification failed for yarn@1.9998.9999`), stdout: ``, }); await expect(runCli(cwd, [`use`, `yarn@1.9998.9999`], true)).resolves.toMatchObject({ exitCode: 1, - stdout: expect.stringContaining(`Signature does not match`), + stdout: expect.stringContaining(`Signature verification failed for yarn@1.9998.9999`), stderr: ``, }); }); @@ -1707,4 +1707,43 @@ describe(`handle integrity checks`, () => { }); }); }); + + it(`rejects invalid signatures for npm keys`, async () => { + process.env.TEST_INTEGRITY = `invalid_npm_signature`; // See `_registryServer.mjs` + process.env.COREPACK_DEFAULT_TO_LATEST = `1`; // Necessary as the version defined in `config.json` does not exist on the custom registry. + + await xfs.mktempPromise(async cwd => { + await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({ + exitCode: 1, + stderr: expect.stringContaining(`Signature verification failed for pnpm@1.9998.9999`), + stdout: ``, + }); + + await expect(runCli(cwd, [`use`, `pnpm`], true)).resolves.toMatchObject({ + exitCode: 1, + stdout: expect.stringContaining(`Signature verification failed for pnpm@1.9998.9999`), + stderr: ``, + }); + }); + }); + + it(`rejects invalid signatures when TUF repo cannot be reached`, async () => { + process.env.TEST_INTEGRITY = `invalid_npm_signature`; // See `_registryServer.mjs` + process.env.COREPACK_DEFAULT_TO_LATEST = `1`; // Necessary as the version defined in `config.json` does not exist on the custom registry. + process.env.BLOCK_SIGSTORE_TUF_REQUESTS = `1`; + + await xfs.mktempPromise(async cwd => { + await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({ + exitCode: 1, + stderr: expect.stringContaining(`Signature verification failed for pnpm@1.9998.9999`), + stdout: ``, + }); + + await expect(runCli(cwd, [`use`, `pnpm`], true)).resolves.toMatchObject({ + exitCode: 1, + stdout: expect.stringContaining(`Signature verification failed for pnpm@1.9998.9999`), + stderr: expect.stringContaining(`Failed to get signing keys from Sigstore TUF repo`), + }); + }); + }); }); diff --git a/tests/nocks.db b/tests/nocks.db index 26c4868d7..70e144b1f 100644 Binary files a/tests/nocks.db and b/tests/nocks.db differ diff --git a/tests/recordRequests.js b/tests/recordRequests.js index b78edad0e..284482440 100644 --- a/tests/recordRequests.js +++ b/tests/recordRequests.js @@ -2,6 +2,7 @@ const path = require(`node:path`); const crypto = require(`node:crypto`); const SQLite3 = require(`better-sqlite3`); +const FakeTimers = require(`@sinonjs/fake-timers`); const db = new SQLite3(path.join(__dirname, `nocks.db`)); process.once(`exit`, () => { @@ -26,7 +27,7 @@ function getRequestHash(input, init) { if (init) { for (const key in init) { - if (init[key] === undefined) continue; + if (init[key] === undefined || key === `signal`) continue; switch (key) { case `headers`: @@ -41,11 +42,20 @@ function getRequestHash(input, init) { return hash.digest(); } +const originalFetch = globalThis.fetch; +const passthroughUrls = []; + if (process.env.NOCK_ENV === `record`) { const insertNockStatement = db.prepare(`INSERT OR REPLACE INTO nocks (hash, body, headers, status) VALUES (?, ?, jsonb(?), ?)`); - const realFetch = globalThis.fetch; + globalThis.fetch = async (input, init) => { - const response = await realFetch(input, init); + if (passthroughUrls.some(passThrough => input.toString().startsWith(passThrough))) + return originalFetch(input, init); + if (process.env.BLOCK_SIGSTORE_TUF_REQUESTS && input.toString().startsWith(`https://tuf-repo-cdn.sigstore.dev`)) + throw new Error(`Request to Sigstore TUF repository are blocked`); + + + const response = await originalFetch(input, init); const data = await response.arrayBuffer(); const minimalHeaders = new Headers(); @@ -69,14 +79,30 @@ if (process.env.NOCK_ENV === `record`) { headers: minimalHeaders, }); }; -} else if (process.env.NOCK_ENV === `replay`) { +} else { + FakeTimers.install({ + // When you re-record requests this needs to be set to the time of the + // recording so that TUF accepts recorded requests. + now: new Date(`2025-04-09`), + toFake: [`Date`], + }); + const getNockStatement = db.prepare(`SELECT body, json(headers) as headers, status FROM nocks WHERE hash = ?`); globalThis.fetch = async (input, init) => { + if (passthroughUrls.some(passThrough => input.toString().startsWith(passThrough))) + return originalFetch(input, init); + if (process.env.BLOCK_SIGSTORE_TUF_REQUESTS && input.toString().startsWith(`https://tuf-repo-cdn.sigstore.dev`)) + throw new Error(`Request to Sigstore TUF repository are blocked`); + const requestHash = getRequestHash(input, init); const mock = getNockStatement.get(requestHash); - if (!mock) throw new Error(`No mock found for ${input}; run the tests with NOCK_ENV=record to generate one`); + if (!mock) { + // Crash process so that corepack cannot catch this error + console.error(Error(`No mock found for ${input}; run the tests with NOCK_ENV=record to generate one`)); + process.exit(10); + } return new Response(mock.body, { status: mock.status, @@ -84,3 +110,5 @@ if (process.env.NOCK_ENV === `record`) { }); }; } + +globalThis.fetch.passthroughUrls = passthroughUrls; diff --git a/yarn.lock b/yarn.lock index b2a26d9ad..653073329 100644 --- a/yarn.lock +++ b/yarn.lock @@ -523,6 +523,41 @@ __metadata: languageName: node linkType: hard +"@sigstore/protobuf-specs@npm:^0.4.0": + version: 0.4.0 + resolution: "@sigstore/protobuf-specs@npm:0.4.0" + checksum: 10c0/5b9e074ad132b977050cbd9431c09ea88b21af266dae91dda8d51e29c7b295e73e3be255c10d68874259326229dde1805dd1f5ff29082d2f3d32a932809816eb + languageName: node + linkType: hard + +"@sigstore/tuf@npm:^3.1.0": + version: 3.1.0 + resolution: "@sigstore/tuf@npm:3.1.0" + dependencies: + "@sigstore/protobuf-specs": "npm:^0.4.0" + tuf-js: "npm:^3.0.1" + checksum: 10c0/940237295bec3817ef4dbfd48de8b9a73b4e297966c05e81b6103747904def999f27499adb3de572407f2c72c6f28d2c699a6c8446be808b599c427a9903f081 + languageName: node + linkType: hard + +"@sinonjs/commons@npm:^3.0.1": + version: 3.0.1 + resolution: "@sinonjs/commons@npm:3.0.1" + dependencies: + type-detect: "npm:4.0.8" + checksum: 10c0/1227a7b5bd6c6f9584274db996d7f8cee2c8c350534b9d0141fc662eaf1f292ea0ae3ed19e5e5271c8fd390d27e492ca2803acd31a1978be2cdc6be0da711403 + languageName: node + linkType: hard + +"@sinonjs/fake-timers@npm:^14.0.0": + version: 14.0.0 + resolution: "@sinonjs/fake-timers@npm:14.0.0" + dependencies: + "@sinonjs/commons": "npm:^3.0.1" + checksum: 10c0/0abdbd8e202570ab1774f307b58f686838151c5842340b2022a81462437f3b923ae57f6662d1002d882189dd98300d5d4c5f13c11f40b2661bc559f26c4320e8 + languageName: node + linkType: hard + "@stylistic/eslint-plugin-jsx@npm:^3.1.0": version: 3.1.0 resolution: "@stylistic/eslint-plugin-jsx@npm:3.1.0" @@ -552,6 +587,23 @@ __metadata: languageName: node linkType: hard +"@tufjs/canonical-json@npm:2.0.0": + version: 2.0.0 + resolution: "@tufjs/canonical-json@npm:2.0.0" + checksum: 10c0/52c5ffaef1483ed5c3feedfeba26ca9142fa386eea54464e70ff515bd01c5e04eab05d01eff8c2593291dcaf2397ca7d9c512720e11f52072b04c47a5c279415 + languageName: node + linkType: hard + +"@tufjs/models@npm:3.0.1": + version: 3.0.1 + resolution: "@tufjs/models@npm:3.0.1" + dependencies: + "@tufjs/canonical-json": "npm:2.0.0" + minimatch: "npm:^9.0.5" + checksum: 10c0/0b2022589139102edf28f7fdcd094407fc98ac25bf530ebcf538dd63152baea9b6144b713c8dfc4f6b7580adeff706ab6ecc5f9716c4b816e58a04419abb1926 + languageName: node + linkType: hard + "@types/debug@npm:^4.1.5": version: 4.1.12 resolution: "@types/debug@npm:4.1.12" @@ -1301,6 +1353,8 @@ __metadata: version: 0.0.0-use.local resolution: "corepack@workspace:." dependencies: + "@sigstore/tuf": "npm:^3.1.0" + "@sinonjs/fake-timers": "npm:^14.0.0" "@types/debug": "npm:^4.1.5" "@types/node": "npm:^20.4.6" "@types/proxy-from-env": "npm:^1" @@ -1371,7 +1425,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.4.0": +"debug@npm:4, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.6, debug@npm:^4.4.0": version: 4.4.0 resolution: "debug@npm:4.4.0" dependencies: @@ -2819,7 +2873,7 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^14.0.3": +"make-fetch-happen@npm:^14.0.1, make-fetch-happen@npm:^14.0.3": version: 14.0.3 resolution: "make-fetch-happen@npm:14.0.3" dependencies: @@ -2878,7 +2932,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.4": +"minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": version: 9.0.5 resolution: "minimatch@npm:9.0.5" dependencies: @@ -4123,6 +4177,28 @@ __metadata: languageName: node linkType: hard +"tuf-js@npm:3.0.1": + version: 3.0.1 + resolution: "tuf-js@npm:3.0.1" + dependencies: + "@tufjs/models": "npm:3.0.1" + debug: "npm:^4.3.6" + make-fetch-happen: "npm:^14.0.1" + checksum: 10c0/4214dd6bb1ec8a6cadbc5690e5a8556de0306f0e95022e54fc7c0ff9dbcc229ab379fd4b048511387f9c0023ea8f8c35acd8f7313f6cbc94a1b8af8b289f62ad + languageName: node + linkType: hard + +"tuf-js@patch:tuf-js@npm%3A3.0.1#~/.yarn/patches/tuf-js-npm-3.0.1-9135d15fbd.patch": + version: 3.0.1 + resolution: "tuf-js@patch:tuf-js@npm%3A3.0.1#~/.yarn/patches/tuf-js-npm-3.0.1-9135d15fbd.patch::version=3.0.1&hash=208643" + dependencies: + "@tufjs/models": "npm:3.0.1" + debug: "npm:^4.3.6" + make-fetch-happen: "npm:^14.0.1" + checksum: 10c0/95371d3b42a808c85b139971f72f1f28e1f19ad04f6d82052b311c5cd47ec7e27718a2f5fe9a329f29bf433c958483f64e941d6193c75394c0cdf544eb4a43b1 + languageName: node + linkType: hard + "tunnel-agent@npm:^0.6.0": version: 0.6.0 resolution: "tunnel-agent@npm:0.6.0" @@ -4148,6 +4224,13 @@ __metadata: languageName: node linkType: hard +"type-detect@npm:4.0.8": + version: 4.0.8 + resolution: "type-detect@npm:4.0.8" + checksum: 10c0/8fb9a51d3f365a7de84ab7f73b653534b61b622aa6800aecdb0f1095a4a646d3f5eb295322127b6573db7982afcd40ab492d038cf825a42093a58b1e1353e0bd + languageName: node + linkType: hard + "typed-array-buffer@npm:^1.0.3": version: 1.0.3 resolution: "typed-array-buffer@npm:1.0.3"