mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-21 13:06:58 +02:00
fix(create-docusaurus): potential security issue with command injection (#7507)
This commit is contained in:
parent
cd7cf781cd
commit
dbd161d67c
7 changed files with 59 additions and 22 deletions
|
@ -23,6 +23,7 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/logger": "2.0.0-beta.20",
|
"@docusaurus/logger": "2.0.0-beta.20",
|
||||||
|
"@docusaurus/utils": "2.0.0-beta.20",
|
||||||
"commander": "^5.1.0",
|
"commander": "^5.1.0",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
|
|
@ -13,6 +13,7 @@ import logger from '@docusaurus/logger';
|
||||||
import shell from 'shelljs';
|
import shell from 'shelljs';
|
||||||
import prompts, {type Choice} from 'prompts';
|
import prompts, {type Choice} from 'prompts';
|
||||||
import supportsColor from 'supports-color';
|
import supportsColor from 'supports-color';
|
||||||
|
import {escapeShellArg} from '@docusaurus/utils';
|
||||||
|
|
||||||
type CLIOptions = {
|
type CLIOptions = {
|
||||||
packageManager?: PackageManager;
|
packageManager?: PackageManager;
|
||||||
|
@ -463,9 +464,11 @@ export default async function init(
|
||||||
logger.info('Creating new Docusaurus project...');
|
logger.info('Creating new Docusaurus project...');
|
||||||
|
|
||||||
if (source.type === 'git') {
|
if (source.type === 'git') {
|
||||||
logger.info`Cloning Git template url=${source.url}...`;
|
const gitCommand = await getGitCommand(source.strategy);
|
||||||
const command = await getGitCommand(source.strategy);
|
const gitCloneCommand = `${gitCommand} ${escapeShellArg(
|
||||||
if (shell.exec(`${command} ${source.url} ${dest}`).code !== 0) {
|
source.url,
|
||||||
|
)} ${escapeShellArg(dest)}`;
|
||||||
|
if (shell.exec(gitCloneCommand).code !== 0) {
|
||||||
logger.error`Cloning Git template failed!`;
|
logger.error`Cloning Git template failed!`;
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
19
packages/docusaurus-utils/src/__tests__/shellUtils.test.ts
Normal file
19
packages/docusaurus-utils/src/__tests__/shellUtils.test.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {escapeShellArg} from '../shellUtils';
|
||||||
|
|
||||||
|
describe('shellUtils', () => {
|
||||||
|
it('escapeShellArg', () => {
|
||||||
|
expect(escapeShellArg('hello')).toBe("'hello'");
|
||||||
|
expect(escapeShellArg('*')).toBe("'*'");
|
||||||
|
expect(escapeShellArg('hello world')).toBe("'hello world'");
|
||||||
|
expect(escapeShellArg("'hello'")).toBe("\\''hello'\\'");
|
||||||
|
expect(escapeShellArg('$(pwd)')).toBe("'$(pwd)'");
|
||||||
|
expect(escapeShellArg('hello$(pwd)')).toBe("'hello$(pwd)'");
|
||||||
|
});
|
||||||
|
});
|
|
@ -97,26 +97,19 @@ export function getFileCommitDate(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let formatArg = '--format=%ct';
|
const args = [
|
||||||
if (includeAuthor) {
|
`--format=%ct${includeAuthor ? ',%an' : ''}`,
|
||||||
formatArg += ',%an';
|
'--max-count=1',
|
||||||
}
|
age === 'oldest' ? '--follow --diff-filter=A' : undefined,
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
let extraArgs = '--max-count=1';
|
const result = shell.exec(`git log ${args} -- "${path.basename(file)}"`, {
|
||||||
if (age === 'oldest') {
|
// Setting cwd is important, see: https://github.com/facebook/docusaurus/pull/5048
|
||||||
// --follow is necessary to follow file renames
|
cwd: path.dirname(file),
|
||||||
// --diff-filter=A ensures we only get the commit which (A)dded the file
|
silent: true,
|
||||||
extraArgs += ' --follow --diff-filter=A';
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const result = shell.exec(
|
|
||||||
`git log ${extraArgs} ${formatArg} -- "${path.basename(file)}"`,
|
|
||||||
{
|
|
||||||
// Setting cwd is important, see: https://github.com/facebook/docusaurus/pull/5048
|
|
||||||
cwd: path.dirname(file),
|
|
||||||
silent: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (result.code !== 0) {
|
if (result.code !== 0) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to retrieve the git history for file "${file}" with exit code ${result.code}: ${result.stderr}`,
|
`Failed to retrieve the git history for file "${file}" with exit code ${result.code}: ${result.stderr}`,
|
||||||
|
|
|
@ -96,6 +96,7 @@ export {
|
||||||
createAbsoluteFilePathMatcher,
|
createAbsoluteFilePathMatcher,
|
||||||
} from './globUtils';
|
} from './globUtils';
|
||||||
export {getFileLoaderUtils} from './webpackUtils';
|
export {getFileLoaderUtils} from './webpackUtils';
|
||||||
|
export {escapeShellArg} from './shellUtils';
|
||||||
export {
|
export {
|
||||||
getDataFilePath,
|
getDataFilePath,
|
||||||
getDataFileData,
|
getDataFileData,
|
||||||
|
|
18
packages/docusaurus-utils/src/shellUtils.ts
Normal file
18
packages/docusaurus-utils/src/shellUtils.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO move from shelljs to execa later?
|
||||||
|
// Execa is well maintained and widely used
|
||||||
|
// Even shelljs recommends execa for security / escaping:
|
||||||
|
// https://github.com/shelljs/shelljs/wiki/Security-guidelines
|
||||||
|
|
||||||
|
// Inspired by https://github.com/xxorax/node-shell-escape/blob/master/shell-escape.js
|
||||||
|
export function escapeShellArg(s: string): string {
|
||||||
|
let res = `'${s.replace(/'/g, "'\\''")}'`;
|
||||||
|
res = res.replace(/^(?:'')+/g, '').replace(/\\'''/g, "\\'");
|
||||||
|
return res;
|
||||||
|
}
|
|
@ -87,6 +87,8 @@ esbuild
|
||||||
eslintcache
|
eslintcache
|
||||||
estree
|
estree
|
||||||
evaluable
|
evaluable
|
||||||
|
execa
|
||||||
|
Execa
|
||||||
externalwaiting
|
externalwaiting
|
||||||
failfast
|
failfast
|
||||||
fbid
|
fbid
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue