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 8eb6dae
Show file tree
Hide file tree
Showing 12 changed files with 172 additions and 53 deletions.
3 changes: 3 additions & 0 deletions packages/amplify-cli-core/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,9 @@ export class ExportPathValidationError extends AmplifyError {
constructor(errMessage?: string);
}

// @public (undocumented)
export const extract: any;

// @public (undocumented)
export function fancy(message?: string): void;

Expand Down
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
154 changes: 154 additions & 0 deletions packages/amplify-cli-core/src/extractZip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/* eslint-disable no-bitwise */
/* 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 };
const extract = require('./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
2 changes: 1 addition & 1 deletion packages/amplify-e2e-tests/src/__tests__/diagnose.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import {
diagnoseSendReport,
diagnoseSendReport_ZipFailed,
} from '@aws-amplify/amplify-e2e-core';
import { extract } from '@aws-amplify/amplify-cli-core';
import * as fs from 'fs-extra';
import * as path from 'path';
import glob from 'glob';
import extract from 'extract-zip';

const PARAMETERS_JSON = 'parameters.json';
const BUILD = 'build';
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
48 changes: 6 additions & 42 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ __metadata:
uuid: ^8.3.2
which: ^2.0.2
yaml: ^2.2.2
yauzl: ^3.1.3
languageName: unknown
linkType: soft

Expand Down Expand Up @@ -824,7 +825,6 @@ __metadata:
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 Expand Up @@ -12858,15 +12858,6 @@ __metadata:
languageName: node
linkType: hard

"@types/yauzl@npm:^2.9.1":
version: 2.9.2
resolution: "@types/yauzl@npm:2.9.2"
dependencies:
"@types/node": "*"
checksum: 0b4a5db8b7b01e94d9c5f48b5043c22553313e9f31918a9755a4bc7875be92a99bf5f11aa260016f553410be517ce64f5a99b14226d878d65d6d1696869a08b1
languageName: node
linkType: hard

"@types/zen-observable@npm:0.8.0":
version: 0.8.0
resolution: "@types/zen-observable@npm:0.8.0"
Expand Down Expand Up @@ -13752,7 +13743,6 @@ __metadata:
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 Expand Up @@ -19213,23 +19203,6 @@ __metadata:
languageName: node
linkType: hard

"extract-zip@npm:^2.0.1":
version: 2.0.1
resolution: "extract-zip@npm:2.0.1"
dependencies:
"@types/yauzl": ^2.9.1
debug: ^4.1.1
get-stream: ^5.1.0
yauzl: ^2.10.0
dependenciesMeta:
"@types/yauzl":
optional: true
bin:
extract-zip: cli.js
checksum: 9afbd46854aa15a857ae0341a63a92743a7b89c8779102c3b4ffc207516b2019337353962309f85c66ee3d9092202a83cdc26dbf449a11981272038443974aee
languageName: node
linkType: hard

"extsprintf@npm:1.3.0":
version: 1.3.0
resolution: "extsprintf@npm:1.3.0"
Expand Down Expand Up @@ -19412,15 +19385,6 @@ __metadata:
languageName: node
linkType: hard

"fd-slicer@npm:~1.1.0":
version: 1.1.0
resolution: "fd-slicer@npm:1.1.0"
dependencies:
pend: ~1.2.0
checksum: 304dd70270298e3ffe3bcc05e6f7ade2511acc278bc52d025f8918b48b6aa3b77f10361bddfadfe2a28163f7af7adbdce96f4d22c31b2f648ba2901f0c5fc20e
languageName: node
linkType: hard

"fecha@npm:^4.2.0":
version: 4.2.1
resolution: "fecha@npm:4.2.1"
Expand Down Expand Up @@ -33040,13 +33004,13 @@ node-pty@beta:
languageName: node
linkType: hard

"yauzl@npm:^2.10.0":
version: 2.10.0
resolution: "yauzl@npm:2.10.0"
"yauzl@npm:^3.1.3":
version: 3.1.3
resolution: "yauzl@npm:3.1.3"
dependencies:
buffer-crc32: ~0.2.3
fd-slicer: ~1.1.0
checksum: f265002af7541b9ec3589a27f5fb8f11cf348b53cc15e2751272e3c062cd73f3e715bc72d43257de71bbaecae446c3f1b14af7559e8ab0261625375541816422
pend: ~1.2.0
checksum: e04a2567860e1337798cd2570d776b4040520b20660e7ec5dfcce24b8be2b134d6a5ae835804a0186b1a58cb8b1741b37eaa6a86f7546b6219b62a265dbaf3fc
languageName: node
linkType: hard

Expand Down

0 comments on commit 8eb6dae

Please sign in to comment.