60 lines
1.8 KiB
JavaScript
60 lines
1.8 KiB
JavaScript
const errors = require('@tryghost/errors');
|
|
|
|
const defaultOptions = {};
|
|
|
|
function throwOnSymlinks(entry) {
|
|
// Check if symlink
|
|
const mode = (entry.externalFileAttributes >> 16) & 0xFFFF;
|
|
// check if it's a symlink or dir (using stat mode constants)
|
|
const IFMT = 61440;
|
|
const IFLNK = 40960;
|
|
const symlink = (mode & IFMT) === IFLNK;
|
|
|
|
if (symlink) {
|
|
throw new errors.UnsupportedMediaTypeError({
|
|
message: 'Symlinks are not allowed in the zip folder.'
|
|
});
|
|
}
|
|
}
|
|
|
|
function throwOnLargeFilenames(entry) {
|
|
if (Buffer.byteLength(entry.fileName, 'utf8') >= 254) {
|
|
throw new errors.UnsupportedMediaTypeError({
|
|
message: 'File names in the zip folder must be shorter than 254 characters.'
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract
|
|
*
|
|
* - Unzip an archive to a folder
|
|
*
|
|
* @param {String} zipToExtract - full path to zip file that should be extracted
|
|
* @param {String} destination - full path of the extraction target
|
|
* @param {Object} [options]
|
|
* @param {Integer} options.defaultDirMode - Directory Mode (permissions), defaults to 0o755
|
|
* @param {Integer} options.defaultFileMode - File Mode (permissions), defaults to 0o644
|
|
* @param {Function} options.onEntry - if present, will be called with (entry, zipfile) for every entry in the zip
|
|
*/
|
|
module.exports = (zipToExtract, destination, options) => {
|
|
const opts = Object.assign({}, defaultOptions, options);
|
|
|
|
const extract = require('extract-zip');
|
|
|
|
opts.dir = destination;
|
|
|
|
const originalOnEntry = opts.onEntry;
|
|
opts.onEntry = (entry, zipfile) => {
|
|
throwOnSymlinks(entry);
|
|
throwOnLargeFilenames(entry);
|
|
if (originalOnEntry) {
|
|
originalOnEntry(entry, zipfile);
|
|
}
|
|
};
|
|
|
|
return extract(zipToExtract, opts).then(() => {
|
|
return {path: destination};
|
|
});
|
|
};
|