Skip to content

Commit

Permalink
fix: don't create symlinks after extracting zip
Browse files Browse the repository at this point in the history
  • Loading branch information
Amplifiyer committed May 22, 2024
1 parent d583756 commit 6aa73c9
Show file tree
Hide file tree
Showing 10 changed files with 7,929 additions and 7,642 deletions.
3 changes: 2 additions & 1 deletion packages/amplify-cli-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@
"semver": "^7.5.4",
"typescript-json-schema": "~0.52.0",
"which": "^2.0.2",
"yaml": "^2.2.2"
"yaml": "^2.2.2",
"yauzl": "^3.1.3"
},
"devDependencies": {
"@aws-amplify/amplify-function-plugin-interface": "1.10.3",
Expand Down
153 changes: 153 additions & 0 deletions packages/amplify-cli-core/src/extractZip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/* eslint-disable spellcheck/spell-checker */
const { createWriteStream, promises: fs } = require('fs');
const path = require('path');
const { promisify } = require('util');
const stream = require('stream');
const yauzl = require('yauzl');

const openZip = promisify(yauzl.open);
const pipeline = promisify(stream.pipeline);

class Extractor {
constructor(zipPath, opts) {
this.zipPath = zipPath;
this.opts = opts;
}

async extract() {
this.zipfile = await openZip(this.zipPath, { lazyEntries: true });
this.canceled = false;

return new Promise((resolve, reject) => {
this.zipfile.on('error', (err) => {
this.canceled = true;
reject(err);
});
this.zipfile.readEntry();

this.zipfile.on('close', () => {
if (!this.canceled) {
resolve();
}
});

// eslint-disable-next-line @typescript-eslint/no-misused-promises
this.zipfile.on('entry', async (entry) => {
if (this.canceled) {
return;
}

if (entry.fileName.startsWith('__MACOSX/')) {
this.zipfile.readEntry();
return;
}

const destDir = path.dirname(path.join(this.opts.dir, entry.fileName));

try {
await fs.mkdir(destDir, { recursive: true });

const canonicalDestDir = await fs.realpath(destDir);
const relativeDestDir = path.relative(this.opts.dir, canonicalDestDir);

if (relativeDestDir.split(path.sep).includes('..')) {
throw new Error(`Out of bound path "${canonicalDestDir}" found while processing file ${entry.fileName}`);
}

await this.extractEntry(entry);
this.zipfile.readEntry();
} catch (err) {
this.canceled = true;
this.zipfile.close();
reject(err);
}
});
});
}

async extractEntry(entry) {
/* istanbul ignore if */
if (this.canceled) {
return;
}

if (this.opts.onEntry) {
this.opts.onEntry(entry, this.zipfile);
}

const dest = path.join(this.opts.dir, entry.fileName);

// convert external file attr int into a fs stat mode int
const mode = (entry.externalFileAttributes >> 16) & 0xffff;
// check if it's a symlink or dir (using stat mode constants)
const IFMT = 61440;
const IFDIR = 16384;
const IFLNK = 40960;
const symlink = (mode & IFMT) === IFLNK;
let isDir = (mode & IFMT) === IFDIR;

// Failsafe, borrowed from jsZip
if (!isDir && entry.fileName.endsWith('/')) {
isDir = true;
}

// check for windows weird way of specifying a directory
// https://github.com/maxogden/extract-zip/issues/13#issuecomment-154494566
const madeBy = entry.versionMadeBy >> 8;
if (!isDir) isDir = madeBy === 0 && entry.externalFileAttributes === 16;

const procMode = this.getExtractedMode(mode, isDir) & 0o777;

// always ensure folders are created
const destDir = isDir ? dest : path.dirname(dest);

const mkdirOptions = { recursive: true };
if (isDir) {
mkdirOptions.mode = procMode;
}
await fs.mkdir(destDir, mkdirOptions);
if (isDir) return;

const readStream = await promisify(this.zipfile.openReadStream.bind(this.zipfile))(entry);

if (!symlink) {
await pipeline(readStream, createWriteStream(dest, { mode: procMode }));
}
}

getExtractedMode(entryMode, isDir) {
let mode = entryMode;
// Set defaults, if necessary
if (mode === 0) {
if (isDir) {
if (this.opts.defaultDirMode) {
mode = parseInt(this.opts.defaultDirMode, 10);
}

if (!mode) {
mode = 0o755;
}
} else {
if (this.opts.defaultFileMode) {
mode = parseInt(this.opts.defaultFileMode, 10);
}

if (!mode) {
mode = 0o644;
}
}
}

return mode;
}
}

module.exports = async function (zipPath, opts) {
if (!path.isAbsolute(opts.dir)) {
throw new Error('Target directory is expected to be absolute');
}

await fs.mkdir(opts.dir, { recursive: true });
opts.dir = await fs.realpath(opts.dir);
return new Extractor(zipPath, opts).extract();
};
3 changes: 2 additions & 1 deletion packages/amplify-cli-core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable import/no-cycle */
import * as ExeInfo from './exeInfo';
export { ExeInfo };
import extract from './extractZip';
export { ExeInfo, extract };
export * from './banner-message';
export * from './category-interfaces';
export * from './cfnUtilities';
Expand Down
1 change: 1 addition & 0 deletions packages/amplify-cli-core/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"compilerOptions": {
"outDir": "./lib",
"rootDir": "./src",
"allowJs": true,
"useUnknownInCatchVariables": false
}
}
1 change: 0 additions & 1 deletion packages/amplify-e2e-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
"dotenv": "^8.2.0",
"esm": "^3.2.25",
"execa": "^5.1.1",
"extract-zip": "^2.0.1",
"fs-extra": "^8.1.0",
"get-port": "^5.1.1",
"glob": "^8.0.3",
Expand Down
1 change: 0 additions & 1 deletion packages/amplify-provider-awscloudformation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
"constructs": "^10.0.5",
"cors": "^2.8.5",
"deep-diff": "^1.0.2",
"extract-zip": "^2.0.1",
"folder-hash": "^4.0.2",
"fs-extra": "^8.1.0",
"glob": "^7.2.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import aws from 'aws-sdk';
import fs from 'fs-extra';
import path from 'path';
import glob from 'glob';
import extract from 'extract-zip';
import inquirer from 'inquirer';
import _ from 'lodash';
import { exitOnNextTick, pathManager, PathConstants, AmplifyError } from '@aws-amplify/amplify-cli-core';
import { exitOnNextTick, pathManager, PathConstants, AmplifyError, extract } from '@aws-amplify/amplify-cli-core';
import * as configurationManager from './configuration-manager';
import { getConfiguredAmplifyClient } from './aws-utils/aws-amplify';
import { checkAmplifyServiceIAMPermission } from './amplify-service-permission-check';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { $TSAny, $TSContext, $TSObject, AmplifyError, AmplifyFrontend, pathManager } from '@aws-amplify/amplify-cli-core';
import { $TSAny, $TSContext, $TSObject, AmplifyError, AmplifyFrontend, pathManager, extract } from '@aws-amplify/amplify-cli-core';
import { printer } from '@aws-amplify/amplify-prompts';
import extract from 'extract-zip';
import * as fs from 'fs-extra';
import sequential from 'promise-sequential';
import { APIGateway } from './aws-utils/aws-apigw';
Expand Down
3 changes: 1 addition & 2 deletions packages/amplify-provider-awscloudformation/src/zip-util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { AmplifyFault } from '@aws-amplify/amplify-cli-core';
import extract from 'extract-zip';
import { AmplifyFault, extract } from '@aws-amplify/amplify-cli-core';
import fs from 'fs-extra';
import path from 'path';
import { S3 } from './aws-utils/aws-s3';
Expand Down
Loading

0 comments on commit 6aa73c9

Please sign in to comment.