core: install: strip zip prefix

master
undefined 10 months ago
parent 3fc18d7539
commit 607230c09e
No known key found for this signature in database

@ -6,7 +6,7 @@ import { CAC } from 'cac';
import fs from 'fs-extra'; import fs from 'fs-extra';
import superagent from 'superagent'; import superagent from 'superagent';
import tar from 'tar'; import tar from 'tar';
import { Logger, streamToBuffer } from '@hydrooj/utils'; import { extractZip, Logger } from '@hydrooj/utils';
const logger = new Logger('install'); const logger = new Logger('install');
let yarnVersion = 0; let yarnVersion = 0;
@ -32,8 +32,8 @@ function downloadAndExtractTgz(url: string, dest: string) {
}); });
} }
async function downloadAndExtractZip(url: string, dest: string) { async function downloadAndExtractZip(url: string, dest: string) {
const res = await streamToBuffer(await superagent.get(url).responseType('blob')); const res = await superagent.get(url).responseType('arraybuffer');
new AdmZip(res).extractAllTo(dest, true); await extractZip(new AdmZip(res.body), dest, true, true);
} }
const types = { const types = {
'.tgz': downloadAndExtractTgz, '.tgz': downloadAndExtractTgz,
@ -71,6 +71,7 @@ export function register(cli: CAC) {
newAddonPath = path.join(addonDir, name); newAddonPath = path.join(addonDir, name);
logger.info(`Downloading ${src} to ${newAddonPath}`); logger.info(`Downloading ${src} to ${newAddonPath}`);
fs.ensureDirSync(newAddonPath); fs.ensureDirSync(newAddonPath);
fs.emptyDirSync(newAddonPath);
const func = types[Object.keys(types).find((i) => filename.endsWith(i))]!; const func = types[Object.keys(types).find((i) => filename.endsWith(i))]!;
await func(src, newAddonPath); await func(src, newAddonPath);
} else throw new Error('Unsupported file type'); } else throw new Error('Unsupported file type');

@ -1,12 +1,14 @@
import crypto from 'crypto'; import crypto from 'crypto';
import os from 'os'; import os from 'os';
import path from 'path'; import path from 'path';
import { Duplex } from 'stream'; import { Duplex, PassThrough } from 'stream';
import { inspect } from 'util'; import { inspect } from 'util';
import type AdmZip from 'adm-zip';
import fs from 'fs-extra'; import fs from 'fs-extra';
import moment, { isMoment, Moment } from 'moment-timezone'; import moment, { isMoment, Moment } from 'moment-timezone';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
import Logger from 'reggol'; import Logger from 'reggol';
import type { Request } from 'superagent';
export * as yaml from 'js-yaml'; export * as yaml from 'js-yaml';
export * as fs from 'fs-extra'; export * as fs from 'fs-extra';
@ -80,7 +82,16 @@ export function isClass(obj: any, strict = false): obj is new (...args: any) =>
return false; return false;
} }
export function streamToBuffer(stream: any, maxSize = 0): Promise<Buffer> { function isSuperagentRequest(t: NodeJS.ReadableStream | Request): t is Request {
return 'req' in t;
}
export function streamToBuffer(input: NodeJS.ReadableStream | Request, maxSize = 0): Promise<Buffer> {
let stream: NodeJS.ReadableStream;
if (isSuperagentRequest(input)) {
const s = new PassThrough();
input.pipe(s);
stream = s;
} else stream = input;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const buffers = []; const buffers = [];
let length = 0; let length = 0;
@ -271,4 +282,45 @@ export function Counter<T extends (string | number) = string>() {
}) as Record<T, number>; }) as Record<T, number>;
} }
function canonical(p: string) {
if (!p) return '';
const safeSuffix = path.posix.normalize(`/${p.split('\\').join('/')}`);
return path.join('.', safeSuffix);
}
function sanitize(prefix: string, name: string) {
prefix = path.resolve(path.normalize(prefix));
const parts = name.split('/');
for (let i = 0, l = parts.length; i < l; i++) {
const p = path.normalize(path.join(prefix, parts.slice(i, l).join(path.sep)));
if (p.indexOf(prefix) === 0) {
return p;
}
}
return path.normalize(path.join(prefix, path.basename(name)));
}
export function sanitizePath(pathname: string) {
const parts = pathname.replace(/\\/g, '/').split('/').filter((i) => i && i !== '.' && i !== '..');
return parts.join(path.sep);
}
/* eslint-disable no-await-in-loop */
export async function extractZip(zip: AdmZip, dest: string, overwrite = false, strip = false) {
const entries = zip.getEntries();
const shouldStrip = strip ? entries.every((i) => i.entryName.startsWith(entries[0].entryName)) : false;
for (const entry of entries) {
const name = shouldStrip ? entry.entryName.substring(entries[0].entryName.length) : entry.entryName;
const d = sanitize(dest, canonical(name));
if (entry.isDirectory) {
await fs.mkdir(d, { recursive: true });
continue;
}
const content = entry.getData();
if (!content) throw new Error('CANT_EXTRACT_FILE');
if (!fs.existsSync(d) || overwrite) await fs.writeFile(d, content);
await fs.utimes(d, entry.header.time, entry.header.time);
}
}
export * from '@hydrooj/utils/lib/common'; export * from '@hydrooj/utils/lib/common';

Loading…
Cancel
Save