Skip to content

Commit 6a480a7

Browse files
aduh95arcanis
andauthored
feat: add support for hash checking (#133)
Fixes: #37 Co-authored-by: Maël Nison <nison.mael@gmail.com>
1 parent 185da44 commit 6a480a7

File tree

3 files changed

+48
-8
lines changed

3 files changed

+48
-8
lines changed

sources/Engine.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export class Engine {
127127
throw new UsageError(`This package manager (${descriptor.name}) isn't supported by this corepack build`);
128128

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

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

154+
// If the user asked for a specific version, no need to request the list of
155+
// available versions from the registry.
156+
if (semver.valid(finalDescriptor.range))
157+
return {name: finalDescriptor.name, reference: finalDescriptor.range};
158+
154159
const candidateRangeDefinitions = Object.keys(definition.ranges).filter(range => {
155160
return semverUtils.satisfiesWithPrereleases(finalDescriptor.range, range);
156161
});

sources/corepackUtils.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {createHash} from 'crypto';
12
import {once} from 'events';
23
import fs from 'fs';
34
import type {Dir} from 'fs';
@@ -81,21 +82,22 @@ export async function findInstalledVersion(installTarget: string, descriptor: De
8182

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

85-
const installFolder = path.join(installTarget, locator.name, locator.reference);
87+
const installFolder = path.join(installTarget, locator.name, version);
8688
if (fs.existsSync(installFolder)) {
8789
debugUtils.log(`Reusing ${locator.name}@${locator.reference}`);
8890
return installFolder;
8991
}
9092

91-
const url = spec.url.replace(`{}`, locator.reference);
93+
const url = spec.url.replace(`{}`, version);
9294

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

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

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

114116
stream.pipe(sendTo);
115117

118+
const hash = build[0]
119+
? stream.pipe(createHash(build[0]))
120+
: null;
121+
116122
await once(sendTo, `finish`);
117123

124+
const actualHash = hash?.digest(`hex`);
125+
if (actualHash !== build[1])
126+
throw new Error(`Mismatch hashes. Expected ${build[1]}, got ${actualHash}`);
127+
118128
await fs.promises.mkdir(path.dirname(installFolder), {recursive: true});
119129
try {
120130
await fs.promises.rename(tmpFolder, installFolder);

tests/main.test.ts

+29-4
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,38 @@ beforeEach(async () => {
88
process.env.COREPACK_HOME = npath.fromPortablePath(await xfs.mktempPromise());
99
});
1010

11+
it(`should refuse to download a package manager if the hash doesn't match`, async () => {
12+
await xfs.mktempPromise(async cwd => {
13+
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
14+
packageManager: `yarn@1.22.4+sha1.deadbeef`,
15+
});
16+
17+
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
18+
exitCode: 1,
19+
stdout: /Mismatch hashes/,
20+
});
21+
});
22+
});
23+
1124
const testedPackageManagers: Array<[string, string]> = [
1225
[`yarn`, `1.22.4`],
26+
[`yarn`, `1.22.4+sha1.01c1197ca5b27f21edc8bc472cd4c8ce0e5a470e`],
27+
[`yarn`, `1.22.4+sha224.0d6eecaf4d82ec12566fdd97143794d0f0c317e0d652bd4d1b305430`],
1328
[`yarn`, `2.0.0-rc.30`],
29+
[`yarn`, `2.0.0-rc.30+sha1.4f0423b01bcb57f8e390b4e0f1990831f92dd1da`],
30+
[`yarn`, `2.0.0-rc.30+sha224.0e7a64468c358596db21c401ffeb11b6534fce7367afd3ae640eadf1`],
1431
[`yarn`, `3.0.0-rc.2`],
32+
[`yarn`, `3.0.0-rc.2+sha1.694bdad81703169e203febd57f9dc97d3be867bd`],
33+
[`yarn`, `3.0.0-rc.2+sha224.f83f6d1cbfac10ba6b516a62ccd2a72ccd857aa6c514d1cd7185ec60`],
1534
[`pnpm`, `4.11.6`],
35+
[`pnpm`, `4.11.6+sha1.7cffc04295f4db4740225c6c37cc345eb923c06a`],
36+
[`pnpm`, `4.11.6+sha224.7783c4b01916b7a69e6ff05d328df6f83cb7f127e9c96be88739386d`],
1637
[`pnpm`, `6.6.2`],
38+
[`pnpm`, `6.6.2+sha1.7b4d6b176c1b93b5670ed94c24babb7d80c13854`],
39+
[`pnpm`, `6.6.2+sha224.eb5c0acad3b0f40ecdaa2db9aa5a73134ad256e17e22d1419a2ab073`],
1740
[`npm`, `6.14.2`],
41+
[`npm`, `6.14.2+sha1.f057d35cd4792c4c511bb1fa332edb43143d07b0`],
42+
[`npm`, `6.14.2+sha224.50512c1eb404900ee78586faa6d756b8d867ff46a328e6fb4cdf3a87`],
1843
];
1944

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

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

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

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

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

0 commit comments

Comments
 (0)