Skip to content

Commit 871201f

Browse files
jbottiglierobcoe
andauthored
Merge pull request from GHSA-7xcx-6wjh-7xp2
- Uses `execFile` with arguments instead of `exec` where possible. - See GHSL-2020-111 Co-authored-by: Benjamin E. Coe <[email protected]>
1 parent a0f0e81 commit 871201f

File tree

4 files changed

+67
-15
lines changed

4 files changed

+67
-15
lines changed

Diff for: lib/lifecycles/commit.js

+22-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const bump = require('../lifecycles/bump')
22
const checkpoint = require('../checkpoint')
33
const formatCommitMessage = require('../format-commit-message')
44
const path = require('path')
5-
const runExec = require('../run-exec')
5+
const runExecFile = require('../run-execFile')
66
const runLifecycleScript = require('../run-lifecycle-script')
77

88
module.exports = function (args, newVersion) {
@@ -20,20 +20,21 @@ module.exports = function (args, newVersion) {
2020
function execCommit (args, newVersion) {
2121
let msg = 'committing %s'
2222
let paths = []
23-
const verify = args.verify === false || args.n ? '--no-verify ' : ''
24-
let toAdd = ''
23+
const verify = args.verify === false || args.n ? ['--no-verify'] : []
24+
const sign = args.sign ? ['-S'] : []
25+
const toAdd = []
2526

2627
// only start with a pre-populated paths list when CHANGELOG processing is not skipped
2728
if (!args.skip.changelog) {
2829
paths = [args.infile]
29-
toAdd += ' ' + args.infile
30+
toAdd.push(args.infile)
3031
}
3132

3233
// commit any of the config files that we've updated
3334
// the version # for.
3435
Object.keys(bump.getUpdatedConfigs()).forEach(function (p) {
3536
paths.unshift(p)
36-
toAdd += ' ' + path.relative(process.cwd(), p)
37+
toAdd.push(path.relative(process.cwd(), p))
3738

3839
// account for multiple files in the output message
3940
if (paths.length > 1) {
@@ -53,8 +54,22 @@ function execCommit (args, newVersion) {
5354
return Promise.resolve()
5455
}
5556

56-
return runExec(args, 'git add' + toAdd)
57+
return runExecFile(args, 'git', ['add'].concat(toAdd))
5758
.then(() => {
58-
return runExec(args, 'git commit ' + verify + (args.sign ? '-S ' : '') + (args.commitAll ? '' : (toAdd)) + ' -m "' + formatCommitMessage(args.releaseCommitMessageFormat, newVersion) + '"')
59+
return runExecFile(
60+
args,
61+
'git',
62+
[
63+
'commit'
64+
].concat(
65+
verify,
66+
sign,
67+
args.commitAll ? [] : toAdd,
68+
[
69+
'-m',
70+
`"${formatCommitMessage(args.releaseCommitMessageFormat, newVersion)}"`
71+
]
72+
)
73+
)
5974
})
6075
}

Diff for: lib/lifecycles/tag.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const chalk = require('chalk')
33
const checkpoint = require('../checkpoint')
44
const figures = require('figures')
55
const formatCommitMessage = require('../format-commit-message')
6-
const runExec = require('../run-exec')
6+
const runExecFile = require('../run-execFile')
77
const runLifecycleScript = require('../run-lifecycle-script')
88

99
module.exports = function (newVersion, pkgPrivate, args) {
@@ -20,13 +20,13 @@ module.exports = function (newVersion, pkgPrivate, args) {
2020
function execTag (newVersion, pkgPrivate, args) {
2121
let tagOption
2222
if (args.sign) {
23-
tagOption = '-s '
23+
tagOption = '-s'
2424
} else {
25-
tagOption = '-a '
25+
tagOption = '-a'
2626
}
2727
checkpoint(args, 'tagging release %s%s', [args.tagPrefix, newVersion])
28-
return runExec(args, 'git tag ' + tagOption + args.tagPrefix + newVersion + ' -m "' + formatCommitMessage(args.releaseCommitMessageFormat, newVersion) + '"')
29-
.then(() => runExec('', 'git rev-parse --abbrev-ref HEAD'))
28+
return runExecFile(args, 'git', ['tag', tagOption, args.tagPrefix + newVersion, '-m', `"${formatCommitMessage(args.releaseCommitMessageFormat, newVersion)}"`])
29+
.then(() => runExecFile('', 'git', ['rev-parse', '--abbrev-ref', 'HEAD']))
3030
.then((currentBranch) => {
3131
let message = 'git push --follow-tags origin ' + currentBranch.trim()
3232
if (pkgPrivate !== true && bump.getUpdatedConfigs()['package.json']) {

Diff for: lib/run-execFile.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const { execFile } = require('child_process')
2+
const printError = require('./print-error')
3+
4+
module.exports = function (args, cmd, cmdArgs) {
5+
if (args.dryRun) return Promise.resolve()
6+
return new Promise((resolve, reject) => {
7+
// Exec given cmd and handle possible errors
8+
execFile(cmd, cmdArgs, function (err, stdout, stderr) {
9+
// If exec returns content in stderr, but no error, print it as a warning
10+
// If exec returns an error, print it and exit with return code 1
11+
if (err) {
12+
printError(args, stderr || err.message)
13+
return reject(err)
14+
} else if (stderr) {
15+
printError(args, stderr, { level: 'warn', color: 'yellow' })
16+
}
17+
return resolve(stdout)
18+
})
19+
})
20+
}

Diff for: test.js

+20-3
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,10 @@ describe('cli', function () {
258258
const captured = shell.cat('gitcapture.log').stdout.split('\n').map(function (line) {
259259
return line ? JSON.parse(line) : line
260260
})
261-
captured[captured.length - 4].should.deep.equal(['commit', '-S', 'CHANGELOG.md', 'package.json', '-m', 'chore(release): 1.0.1'])
262-
captured[captured.length - 3].should.deep.equal(['tag', '-s', 'v1.0.1', '-m', 'chore(release): 1.0.1'])
263-
261+
/* eslint-disable no-useless-escape */
262+
captured[captured.length - 4].should.deep.equal(['commit', '-S', 'CHANGELOG.md', 'package.json', '-m', '\"chore(release): 1.0.1\"'])
263+
captured[captured.length - 3].should.deep.equal(['tag', '-s', 'v1.0.1', '-m', '\"chore(release): 1.0.1\"'])
264+
/* eslint-enable no-useless-escape */
264265
unmock()
265266
})
266267
})
@@ -1314,3 +1315,19 @@ describe('standard-version', function () {
13141315
})
13151316
})
13161317
})
1318+
1319+
describe('GHSL-2020-111', function () {
1320+
beforeEach(initInTempFolder)
1321+
afterEach(finishTemp)
1322+
it('does not allow command injection via basic configuration', function () {
1323+
return standardVersion({
1324+
silent: true,
1325+
noVerify: true,
1326+
infile: 'foo.txt',
1327+
releaseCommitMessageFormat: 'bla `touch exploit`'
1328+
}).then(function () {
1329+
const stat = shell.test('-f', './exploit')
1330+
stat.should.equal(false)
1331+
})
1332+
})
1333+
})

0 commit comments

Comments
 (0)