From 1c17420e6cc50ba7a739cf474e32922b310b6332 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 24 Nov 2020 18:03:48 +0200 Subject: [PATCH] fix validation harder Signed-off-by: Roman --- src/common/utils/tar.ts | 32 +++++++++++-------- .../components/+extensions/extensions.tsx | 22 ++++++++----- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/common/utils/tar.ts b/src/common/utils/tar.ts index 60cf71d533..f0402b2d5e 100644 --- a/src/common/utils/tar.ts +++ b/src/common/utils/tar.ts @@ -4,23 +4,18 @@ import tar, { ExtractOptions, FileStat } from "tar"; import path from "path"; export interface ReadFileFromTarOpts { - fileName?: string; - fileMatcher?(path: string, entry: FileStat): boolean; - notFoundMessage?: string; + tarPath: string; + filePath: string; + parseJson?: boolean; } -export function readFileFromTar(tarFilePath: string, opts: ReadFileFromTarOpts): Promise { +export function readFileFromTar({tarPath, filePath, parseJson}: ReadFileFromTarOpts): Promise { return new Promise(async (resolve, reject) => { const fileChunks: Buffer[] = []; - const { - fileName, - fileMatcher = (path: string) => path === fileName, - notFoundMessage = "File not found", - } = opts; await tar.list({ - file: tarFilePath, - filter: fileMatcher, + file: tarPath, + filter: path => path === filePath, onentry(entry: FileStat) { entry.on("data", chunk => { fileChunks.push(chunk); @@ -29,17 +24,28 @@ export function readFileFromTar(tarFilePath: string, opts: ReadFileFromTarOpts): reject(`Reading ${entry.path} error: ${err}`); }); entry.once("end", () => { - resolve(Buffer.concat(fileChunks)); + const data = Buffer.concat(fileChunks); + const result = parseJson ? JSON.parse(data.toString("utf8")) : data; + resolve(result); }); }, }); if (!fileChunks.length) { - reject(notFoundMessage); + reject(null); } }); } +export async function listTarEntries(filePath: string): Promise { + const entries: string[] = []; + await tar.list({ + file: filePath, + onentry: (entry: FileStat) => entries.push(entry.path as any as string), + }); + return entries; +} + export function extractTar(filePath: string, opts: ExtractOptions & { sync?: boolean } = {}) { return tar.extract({ file: filePath, diff --git a/src/renderer/components/+extensions/extensions.tsx b/src/renderer/components/+extensions/extensions.tsx index 1514425de5..0fbdcd090c 100644 --- a/src/renderer/components/+extensions/extensions.tsx +++ b/src/renderer/components/+extensions/extensions.tsx @@ -20,8 +20,8 @@ import { extensionLoader } from "../../../extensions/extension-loader"; import { extensionDiscovery, manifestFilename } from "../../../extensions/extension-discovery"; import { LensExtensionManifest, sanitizeExtensionName } from "../../../extensions/lens-extension"; import { Notifications } from "../notifications"; -import { downloadFile, escapeRegExp } from "../../../common/utils"; -import { extractTar, readFileFromTar } from "../../../common/utils/tar"; +import { downloadFile } from "../../../common/utils"; +import { extractTar, listTarEntries, readFileFromTar } from "../../../common/utils/tar"; import { docsUrl } from "../../../common/vars"; interface InstallRequest { @@ -142,14 +142,20 @@ export class Extensions extends React.Component { } async validatePackage(filePath: string): Promise { - const manifestMatcher = RegExp(String.raw`^(\w+\/)?${escapeRegExp(manifestFilename)}$`); + const tarFiles = await listTarEntries(filePath); - const packageJson: Buffer = await readFileFromTar(filePath, { - notFoundMessage: `Invalid extension package, ${manifestFilename} not found`, - // tarball from npm contains single root folder "package/*" - fileMatcher: (path: string) => !!path.match(manifestMatcher), + // tarball from npm contains single root folder "package/*" + const hasSingleRootFolder = tarFiles.every(entry => entry.startsWith(tarFiles[0])); + const manifestLocation = hasSingleRootFolder ? path.join(tarFiles[0], manifestFilename) : manifestFilename; + + if(!tarFiles.includes(manifestLocation)) { + throw `invalid package, ${manifestFilename} not found`; + } + const manifest = await readFileFromTar({ + tarPath: filePath, + filePath: manifestLocation, + parseJson: true, }); - const manifest: LensExtensionManifest = JSON.parse(packageJson.toString("utf8")); if (!manifest.lens && !manifest.renderer) { throw `${manifestFilename} must specify "main" and/or "renderer" fields`; }