Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: don't create symlinks after extracting zip #13791

Merged
merged 2 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
158 changes: 158 additions & 0 deletions packages/amplify-cli-core/src/extractZip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/* eslint-disable no-bitwise */
/* eslint-disable spellcheck/spell-checker */
Amplifiyer marked this conversation as resolved.
Show resolved Hide resolved
/**
* This file is copied from https://github.com/max-mapper/extract-zip
* and turned off creating symlinks on the machine where archive is extracted
*/
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) {
0618 marked this conversation as resolved.
Show resolved Hide resolved
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
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';
0618 marked this conversation as resolved.
Show resolved Hide resolved
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
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
Loading