Skip to content

Commit

Permalink
Fixes missing shell escape for git commit message
Browse files Browse the repository at this point in the history
  • Loading branch information
mikaelbr committed Jun 15, 2020
1 parent 04df60b commit 6c76c9e
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 100 deletions.
65 changes: 37 additions & 28 deletions lib/git.js
Original file line number Diff line number Diff line change
@@ -1,62 +1,71 @@
var contra = require('contra'),
path = require('path'),
fUtils = require('./files'),
cp = require('child_process');

var gitApp = 'git', gitExtra = { env: process.env };
var contra = require("contra"),
path = require("path"),
fUtils = require("./files"),
cp = require("child_process");

var gitApp = "git",
gitExtra = { env: process.env };

var escapeQuotes = function (str) {
if (typeof str === 'string') {
return str.replace(/(["$`\\])/g, '\\$1');
if (typeof str === "string") {
return '"' + str.replace(/(["'$`\\])/g, "\\$1") + '"';
} else {
return str;
}
};

module.exports.isRepositoryClean = function (callback) {
cp.exec(gitApp + ' ' + [ 'ls-files', '-m' ].join(' '), gitExtra, function (er, stdout, stderr) {
cp.exec(gitApp + " " + ["ls-files", "-m"].join(" "), gitExtra, function (
er,
stdout,
stderr
) {
// makeCommit parly inspired and taken from NPM version module
var lines = stdout.trim().split('\n').filter(function (line) {
var file = path.basename(line.replace(/.{1,2}\s+/, ''));
return line.trim() && !line.match(/^\?\? /) && !fUtils.isPackageFile(line);
}).map(function (line) {
return line.trim()
});
var lines = stdout
.trim()
.split("\n")
.filter(function (line) {
var file = path.basename(line.replace(/.{1,2}\s+/, ""));
return (
line.trim() && !line.match(/^\?\? /) && !fUtils.isPackageFile(line)
);
})
.map(function (line) {
return line.trim();
});

if (lines.length) {
return callback(new Error('Git working directory not clean.\n'+lines.join('\n')));
return callback(
new Error("Git working directory not clean.\n" + lines.join("\n"))
);
}
return callback();
});
};

module.exports.checkout = function (callback) {
cp.exec(gitApp + ' checkout -- .', gitExtra, callback);
cp.exec(gitApp + " checkout -- .", gitExtra, callback);
};

module.exports.commit = function (files, message, newVer, tagName, callback) {
message = message.replace('%s', newVer).replace('"', '').replace("'", '');
files = files.map(function (file) {
return '"' + escapeQuotes(file) + '"';
}).join(' ');
message = escapeQuotes(message.replace("%s", newVer));
files = files.map(escapeQuotes).join(" ");
var functionSeries = [
function (done) {
cp.exec(gitApp + ' add ' + files, gitExtra, done);
cp.exec(gitApp + " add " + files, gitExtra, done);
},

function (done) {
cp.exec([gitApp, 'commit', '-m', '"' + message + '"'].join(' '), gitExtra, done);
cp.exec([gitApp, "commit", "-m", message].join(" "), gitExtra, done);
},

function (done) {
cp.exec(
[
gitApp, 'tag', '-a', tagName, '-m', '"' + message + '"'
].join(' '),
gitExtra, done
[gitApp, "tag", "-a", tagName, "-m", message].join(" "),
gitExtra,
done
);
}
},
];
contra.series(functionSeries, callback);
};
176 changes: 104 additions & 72 deletions tests/git_test.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
var version = require('../'),
assert = require('assert'),
fs = require('fs'),
vinylFs = require('vinyl-fs'),
path = require('path'),
cp = require('child_process'),
File = require('vinyl'),
through = require('through2'),
fUtil = require('../lib/files'),
git = require('../lib/git');

describe('git', function () {
var filename = 'package.json';
var expectedPath = path.join(__dirname, './fixtures/', filename);
var version = require("../"),
assert = require("assert"),
fs = require("fs"),
vinylFs = require("vinyl-fs"),
path = require("path"),
cp = require("child_process"),
File = require("vinyl"),
through = require("through2"),
fUtil = require("../lib/files"),
git = require("../lib/git");

describe("git", function () {
var filename = "package.json";
var expectedPath = path.join(__dirname, "./fixtures/", filename);
var expectedContent = fs.readFileSync(expectedPath);

var original = fUtil.loadFiles;
Expand All @@ -21,19 +21,19 @@ describe('git', function () {
var originalCommit = git.commit;
var originalCheckout = git.checkout;

before(function () {
before(function () {
vinylFs.dest = function () {
return through.obj(function (file, enc, next) {
this.push(file);
next();
});
}
};

var expectedFile = new File({
base: __dirname,
cwd: __dirname,
path: expectedPath,
contents: expectedContent
contents: expectedContent,
});

fUtil.loadFiles = function () {
Expand All @@ -57,130 +57,162 @@ describe('git', function () {
cp.exec = exec;
});

describe('#Update()', function(){
it('should return error on unclean git repository when commit is given', function (done) {
describe("#Update()", function () {
it("should return error on unclean git repository when commit is given", function (done) {
git.isRepositoryClean = function (cb) {
return cb(new Error('Not clean'));
return cb(new Error("Not clean"));
};

version.update({
version: '1.0.0',
commitMessage: 'Message'
}, function (err, data) {
assert.ok(err);
assert.equal(err.message, 'Not clean', 'Error message should be set by isRepositoryClean');
version.update(
{
version: "1.0.0",
commitMessage: "Message",
},
function (err, data) {
assert.ok(err);
assert.equal(
err.message,
"Not clean",
"Error message should be set by isRepositoryClean"
);

done();
}
);
});

it("should return NOT error on unclean git repository when no commit message is given", function (done) {
git.isRepositoryClean = function (cb) {
return cb(new Error("Not clean"));
};

version.update("1.0.0", function (err, data) {
assert.ifError(err);
done();
});
});

it('should return NOT error on unclean git repository when no commit message is given', function (done) {
it("should sanitize commit message", function (done) {
git.isRepositoryClean = function (cb) {
return cb(new Error('Not clean'));
return cb(null);
};

version.update('1.0.0', function (err, data) {
assert.ifError(err);
cp.exec = function (cmd, extra, cb) {
if (cmd.indexOf("-a") === -1) return cb(null);
assert.equal('git tag -a v1.0.0 -m "Message \\`touch file\\`"', cmd);
done();
};

version.update({
version: "1.0.0",
commitMessage: "Message `touch file`",
});
});

it('should get updated version sent to commit when commit message is given', function (done) {
it("should get updated version sent to commit when commit message is given", function (done) {
git.isRepositoryClean = function (cb) {
return cb(null);
};

git.commit = function (files, message, newVer, tagName, callback) {
assert.equal(message, 'Message');
assert.equal(newVer, '1.0.0');
assert.equal(message, "Message");
assert.equal(newVer, "1.0.0");
assert.equal(files[0], expectedPath);
assert.equal(tagName, 'v1.0.0');
assert.equal(tagName, "v1.0.0");
return callback(null);
};

version.update({
version: '1.0.0',
commitMessage: 'Message'
}, function (err, data) {
assert.ifError(err);
done();
});
version.update(
{
version: "1.0.0",
commitMessage: "Message",
},
function (err, data) {
assert.ifError(err);
done();
}
);
});

it('should be able to override tagName', function (done) {
it("should be able to override tagName", function (done) {
git.isRepositoryClean = function (cb) {
return cb(null);
};

git.commit = function (files, message, newVer, tagName, callback) {
assert.equal(tagName, 'v1.0.0-src');
assert.equal(tagName, "v1.0.0-src");
return callback(null);
};

version.update({
version: '1.0.0',
commitMessage: 'Message',
tagName: 'v%s-src'
}, function (err, data) {
assert.ifError(err);
done();
});
version.update(
{
version: "1.0.0",
commitMessage: "Message",
tagName: "v%s-src",
},
function (err, data) {
assert.ifError(err);
done();
}
);
});

it('should get flag defining if v-prefix should be used or not', function (done) {
it("should get flag defining if v-prefix should be used or not", function (done) {
git.isRepositoryClean = function (cb) {
return cb(null);
};

git.commit = function (files, message, newVer, noPrefix, callback) {
assert.ok(noPrefix, 'No prefix should be true');
assert.ok(noPrefix, "No prefix should be true");
return callback(null);
};

version.update({
version: '1.0.0',
commitMessage: 'Message',
noPrefix: true
}, function (err, data) {
assert.ifError(err);
done();
});
version.update(
{
version: "1.0.0",
commitMessage: "Message",
noPrefix: true,
},
function (err, data) {
assert.ifError(err);
done();
}
);
});

it('should make tag with v-prefix per default', function (done) {
it("should make tag with v-prefix per default", function (done) {
git.isRepositoryClean = function (cb) {
return cb(null);
};

cp.exec = function (cmd, extra, cb) {
if (cmd.indexOf('-a') === -1) return cb(null);
if (cmd.indexOf("-a") === -1) return cb(null);
assert.equal('git tag -a v1.0.0 -m "Message"', cmd);
done();
};

version.update({
version: '1.0.0',
commitMessage: 'Message'
version: "1.0.0",
commitMessage: "Message",
});
});

it('should make tag without v-prefix if specified', function (done) {
it("should make tag without v-prefix if specified", function (done) {
git.isRepositoryClean = function (cb) {
return cb(null);
};

cp.exec = function (cmd, extra, cb) {
if (cmd.indexOf('-a') === -1) return cb(null);
if (cmd.indexOf("-a") === -1) return cb(null);
assert.equal('git tag -a 1.0.0 -m "Message"', cmd);
done();
};

version.update({
version: '1.0.0',
commitMessage: 'Message',
noPrefix: true
version: "1.0.0",
commitMessage: "Message",
noPrefix: true,
});
});
});

});
});

0 comments on commit 6c76c9e

Please sign in to comment.