Skip to content

Commit

Permalink
feat: add support for hash checking (#133)
Browse files Browse the repository at this point in the history
Fixes: #37
Co-authored-by: Maël Nison <nison.mael@gmail.com>
  • Loading branch information
aduh95 and arcanis authored Jul 8, 2022
1 parent 185da44 commit 6a480a7
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 8 deletions.
7 changes: 6 additions & 1 deletion sources/Engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export class Engine {
throw new UsageError(`This package manager (${descriptor.name}) isn't supported by this corepack build`);

let finalDescriptor = descriptor;
if (descriptor.range.match(/^[a-z-]+$/)) {
if (/^[a-z-]+$/.test(descriptor.range)) {
if (!allowTags)
throw new UsageError(`Packages managers can't be referended via tags in this context`);

Expand All @@ -151,6 +151,11 @@ export class Engine {
if (cachedVersion !== null && useCache)
return {name: finalDescriptor.name, reference: cachedVersion};

// If the user asked for a specific version, no need to request the list of
// available versions from the registry.
if (semver.valid(finalDescriptor.range))
return {name: finalDescriptor.name, reference: finalDescriptor.range};

const candidateRangeDefinitions = Object.keys(definition.ranges).filter(range => {
return semverUtils.satisfiesWithPrereleases(finalDescriptor.range, range);
});
Expand Down
16 changes: 13 additions & 3 deletions sources/corepackUtils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {createHash} from 'crypto';
import {once} from 'events';
import fs from 'fs';
import type {Dir} from 'fs';
Expand Down Expand Up @@ -81,21 +82,22 @@ export async function findInstalledVersion(installTarget: string, descriptor: De

export async function installVersion(installTarget: string, locator: Locator, {spec}: {spec: PackageManagerSpec}) {
const {default: tar} = await import(/* webpackMode: 'eager' */ `tar`);
const {version, build} = semver.parse(locator.reference)!;

const installFolder = path.join(installTarget, locator.name, locator.reference);
const installFolder = path.join(installTarget, locator.name, version);
if (fs.existsSync(installFolder)) {
debugUtils.log(`Reusing ${locator.name}@${locator.reference}`);
return installFolder;
}

const url = spec.url.replace(`{}`, locator.reference);
const url = spec.url.replace(`{}`, version);

// Creating a temporary folder inside the install folder means that we
// are sure it'll be in the same drive as the destination, so we can
// just move it there atomically once we are done

const tmpFolder = folderUtils.getTemporaryFolder(installTarget);
debugUtils.log(`Installing ${locator.name}@${locator.reference} from ${url} to ${tmpFolder}`);
debugUtils.log(`Installing ${locator.name}@${version} from ${url} to ${tmpFolder}`);
const stream = await httpUtils.fetchUrlStream(url);

const parsedUrl = new URL(url);
Expand All @@ -113,8 +115,16 @@ export async function installVersion(installTarget: string, locator: Locator, {s

stream.pipe(sendTo);

const hash = build[0]
? stream.pipe(createHash(build[0]))
: null;

await once(sendTo, `finish`);

const actualHash = hash?.digest(`hex`);
if (actualHash !== build[1])
throw new Error(`Mismatch hashes. Expected ${build[1]}, got ${actualHash}`);

await fs.promises.mkdir(path.dirname(installFolder), {recursive: true});
try {
await fs.promises.rename(tmpFolder, installFolder);
Expand Down
33 changes: 29 additions & 4 deletions tests/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,38 @@ beforeEach(async () => {
process.env.COREPACK_HOME = npath.fromPortablePath(await xfs.mktempPromise());
});

it(`should refuse to download a package manager if the hash doesn't match`, async () => {
await xfs.mktempPromise(async cwd => {
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
packageManager: `yarn@1.22.4+sha1.deadbeef`,
});

await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
exitCode: 1,
stdout: /Mismatch hashes/,
});
});
});

const testedPackageManagers: Array<[string, string]> = [
[`yarn`, `1.22.4`],
[`yarn`, `1.22.4+sha1.01c1197ca5b27f21edc8bc472cd4c8ce0e5a470e`],
[`yarn`, `1.22.4+sha224.0d6eecaf4d82ec12566fdd97143794d0f0c317e0d652bd4d1b305430`],
[`yarn`, `2.0.0-rc.30`],
[`yarn`, `2.0.0-rc.30+sha1.4f0423b01bcb57f8e390b4e0f1990831f92dd1da`],
[`yarn`, `2.0.0-rc.30+sha224.0e7a64468c358596db21c401ffeb11b6534fce7367afd3ae640eadf1`],
[`yarn`, `3.0.0-rc.2`],
[`yarn`, `3.0.0-rc.2+sha1.694bdad81703169e203febd57f9dc97d3be867bd`],
[`yarn`, `3.0.0-rc.2+sha224.f83f6d1cbfac10ba6b516a62ccd2a72ccd857aa6c514d1cd7185ec60`],
[`pnpm`, `4.11.6`],
[`pnpm`, `4.11.6+sha1.7cffc04295f4db4740225c6c37cc345eb923c06a`],
[`pnpm`, `4.11.6+sha224.7783c4b01916b7a69e6ff05d328df6f83cb7f127e9c96be88739386d`],
[`pnpm`, `6.6.2`],
[`pnpm`, `6.6.2+sha1.7b4d6b176c1b93b5670ed94c24babb7d80c13854`],
[`pnpm`, `6.6.2+sha224.eb5c0acad3b0f40ecdaa2db9aa5a73134ad256e17e22d1419a2ab073`],
[`npm`, `6.14.2`],
[`npm`, `6.14.2+sha1.f057d35cd4792c4c511bb1fa332edb43143d07b0`],
[`npm`, `6.14.2+sha224.50512c1eb404900ee78586faa6d756b8d867ff46a328e6fb4cdf3a87`],
];

for (const [name, version] of testedPackageManagers) {
Expand All @@ -26,7 +51,7 @@ for (const [name, version] of testedPackageManagers) {

await expect(runCli(cwd, [name, `--version`])).resolves.toMatchObject({
exitCode: 0,
stdout: `${version}\n`,
stdout: `${version.split(`+`, 1)[0]}\n`,
});
});
});
Expand Down Expand Up @@ -136,17 +161,17 @@ it(`should use the pinned version when local projects don't list any spec`, asyn
});

await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
stdout: `${config.definitions.yarn.default}\n`,
stdout: `${config.definitions.yarn.default.split(`+`, 1)[0]}\n`,
exitCode: 0,
});

await expect(runCli(cwd, [`pnpm`, `--version`])).resolves.toMatchObject({
stdout: `${config.definitions.pnpm.default}\n`,
stdout: `${config.definitions.pnpm.default.split(`+`, 1)[0]}\n`,
exitCode: 0,
});

await expect(runCli(cwd, [`npm`, `--version`])).resolves.toMatchObject({
stdout: `${config.definitions.npm.default}\n`,
stdout: `${config.definitions.npm.default.split(`+`, 1)[0]}\n`,
exitCode: 0,
});
});
Expand Down

0 comments on commit 6a480a7

Please sign in to comment.