From 32211d4303a413ebe8dce3000266ec768d72b49e Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Tue, 5 Sep 2023 00:35:32 -0700 Subject: [PATCH] fix: validate checksum of cached artifacts too (#212) --- src/index.ts | 122 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 50 deletions(-) diff --git a/src/index.ts b/src/index.ts index a506da57b..ff2323a16 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,6 +33,69 @@ if (process.env.ELECTRON_GET_USE_PROXY) { initializeProxy(); } +type ArtifactDownloader = ( + _artifactDetails: ElectronPlatformArtifactDetailsWithDefaults, +) => Promise; + +async function validateArtifact( + artifactDetails: ElectronArtifactDetails, + downloadedAssetPath: string, + _downloadArtifact: ArtifactDownloader, +) { + return await withTempDirectoryIn(artifactDetails.tempDirectory, async tempFolder => { + // Don't try to verify the hash of the hash file itself + // and for older versions that don't have a SHASUMS256.txt + if ( + !artifactDetails.artifactName.startsWith('SHASUMS256') && + !artifactDetails.unsafelyDisableChecksums && + semver.gte(artifactDetails.version, '1.3.2') + ) { + let shasumPath: string; + const checksums = artifactDetails.checksums; + if (checksums) { + shasumPath = path.resolve(tempFolder, 'SHASUMS256.txt'); + const fileNames: string[] = Object.keys(checksums); + if (fileNames.length === 0) { + throw new Error( + 'Provided "checksums" object is empty, cannot generate a valid SHASUMS256.txt', + ); + } + const generatedChecksums = fileNames + .map(fileName => `${checksums[fileName]} *${fileName}`) + .join('\n'); + await fs.writeFile(shasumPath, generatedChecksums); + } else { + shasumPath = await _downloadArtifact({ + isGeneric: true, + version: artifactDetails.version, + artifactName: 'SHASUMS256.txt', + force: artifactDetails.force, + downloadOptions: artifactDetails.downloadOptions, + cacheRoot: artifactDetails.cacheRoot, + downloader: artifactDetails.downloader, + mirrorOptions: artifactDetails.mirrorOptions, + }); + } + + // For versions 1.3.2 - 1.3.4, need to overwrite the `defaultTextEncoding` option: + // https://github.com/electron/electron/pull/6676#discussion_r75332120 + if (semver.satisfies(artifactDetails.version, '1.3.2 - 1.3.4')) { + const validatorOptions: sumchecker.ChecksumOptions = {}; + validatorOptions.defaultTextEncoding = 'binary'; + const checker = new sumchecker.ChecksumValidator('sha256', shasumPath, validatorOptions); + await checker.validate( + path.dirname(downloadedAssetPath), + path.basename(downloadedAssetPath), + ); + } else { + await sumchecker('sha256', shasumPath, path.dirname(downloadedAssetPath), [ + path.basename(downloadedAssetPath), + ]); + } + } + }); +} + /** * Downloads an artifact from an Electron release and returns an absolute path * to the downloaded file. @@ -74,7 +137,14 @@ export async function downloadArtifact( d('Cache miss'); } else { d('Cache hit'); - return cachedPath; + try { + await validateArtifact(artifactDetails, cachedPath, downloadArtifact); + + return cachedPath; + } catch (err) { + d("Artifact in cache didn't match checksums", err); + d('falling back to re-download'); + } } } @@ -102,55 +172,7 @@ export async function downloadArtifact( ); await downloader.download(url, tempDownloadPath, artifactDetails.downloadOptions); - // Don't try to verify the hash of the hash file itself - // and for older versions that don't have a SHASUMS256.txt - if ( - !artifactDetails.artifactName.startsWith('SHASUMS256') && - !artifactDetails.unsafelyDisableChecksums && - semver.gte(artifactDetails.version, '1.3.2') - ) { - await withTempDirectory(async tmpDir => { - let shasumPath: string; - const checksums = artifactDetails.checksums; - if (checksums) { - shasumPath = path.resolve(tmpDir, 'SHASUMS256.txt'); - const fileNames: string[] = Object.keys(checksums); - if (fileNames.length === 0) { - throw new Error( - 'Provided "checksums" object is empty, cannot generate a valid SHASUMS256.txt', - ); - } - const generatedChecksums = fileNames - .map(fileName => `${checksums[fileName]} *${fileName}`) - .join('\n'); - await fs.writeFile(shasumPath, generatedChecksums); - } else { - shasumPath = await downloadArtifact({ - isGeneric: true, - version: artifactDetails.version, - artifactName: 'SHASUMS256.txt', - force: artifactDetails.force, - downloadOptions: artifactDetails.downloadOptions, - cacheRoot: artifactDetails.cacheRoot, - downloader: artifactDetails.downloader, - mirrorOptions: artifactDetails.mirrorOptions, - }); - } - - // For versions 1.3.2 - 1.3.4, need to overwrite the `defaultTextEncoding` option: - // https://github.com/electron/electron/pull/6676#discussion_r75332120 - if (semver.satisfies(artifactDetails.version, '1.3.2 - 1.3.4')) { - const validatorOptions: sumchecker.ChecksumOptions = {}; - validatorOptions.defaultTextEncoding = 'binary'; - const checker = new sumchecker.ChecksumValidator('sha256', shasumPath, validatorOptions); - await checker.validate(path.dirname(tempDownloadPath), path.basename(tempDownloadPath)); - } else { - await sumchecker('sha256', shasumPath, path.dirname(tempDownloadPath), [ - path.basename(tempDownloadPath), - ]); - } - }); - } + await validateArtifact(artifactDetails, tempDownloadPath, downloadArtifact); return await cache.putFileInCache(url, tempDownloadPath, fileName); });