Skip to content

Commit ce955a3

Browse files
committed
Bugfix: Fix command injection vulnerability in grunt tzdata pipeline
The grunt data pipeline was constructed using full bash strings with input coming from command line, which can lead to arbitrary code execution. Switch from exec to execFile, which lists all command arguments in an array, so no injection is possible. Advisory: GHSA-56x4-j7p9-fcf9
1 parent 9430b4c commit ce955a3

File tree

3 files changed

+20
-18
lines changed

3 files changed

+20
-18
lines changed

tasks/data-download.js

+12-11
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,33 @@
11
"use strict";
22

33
var path = require('path'),
4-
exec = require('child_process').exec;
4+
execFile = require('child_process').execFile;
55

66
module.exports = function (grunt) {
77
grunt.registerTask('data-download', '1. Download data from iana.org/time-zones.', function (version) {
88
version = version || 'latest';
99

1010
var done = this.async(),
11-
src = 'ftp://ftp.iana.org/tz/tzdata-latest.tar.gz',
11+
src = (version === 'latest' ?
12+
'ftp://ftp.iana.org/tz/tzdata-latest.tar.gz' :
13+
'https://data.iana.org/time-zones/releases/tzdata' + version + '.tar.gz'),
1214
curl = path.resolve('temp/curl', version, 'data.tar.gz'),
1315
dest = path.resolve('temp/download', version);
1416

15-
if (version !== 'latest') {
16-
src = 'https://data.iana.org/time-zones/releases/tzdata' + version + '.tar.gz';
17-
}
18-
1917
grunt.file.mkdir(path.dirname(curl));
2018
grunt.file.mkdir(dest);
2119

2220
grunt.log.ok('Downloading ' + src);
2321

24-
exec('curl ' + src + ' -o ' + curl + ' && cd ' + dest + ' && gzip -dc ' + curl + ' | tar -xf -', function (err) {
22+
execFile('curl', [src, '-o', curl], function (err) {
2523
if (err) { throw err; }
24+
grunt.log.ok('Downloaded ' + curl + ', extracting . . .');
25+
execFile('tar', ['-xzf', curl], { cwd: dest }, function (err) {
26+
if (err) { throw err; }
2627

27-
grunt.log.ok('Downloaded ' + src);
28-
29-
done();
28+
grunt.log.ok('Extracted ' + dest);
29+
done();
30+
});
3031
});
3132
});
32-
};
33+
};

tasks/data-zdump.js

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
"use strict";
22

33
var path = require('path'),
4-
exec = require('child_process').exec;
4+
execFile = require('child_process').execFile;
55

66
module.exports = function (grunt) {
77
grunt.registerTask('data-zdump', '3. Dump data with zdump(8).', function (version) {
88
version = version || 'latest';
99

10-
console.log(path.resolve('zdump'));
11-
1210
var done = this.async(),
1311
zicBase = path.resolve('temp/zic', version),
1412
zdumpBase = path.resolve('temp/zdump', version),
@@ -34,15 +32,15 @@ module.exports = function (grunt) {
3432
src = path.join(zicBase, file),
3533
dest = path.join(zdumpBase, file);
3634

37-
exec('zdump -v ' + src, { maxBuffer: 20*1024*1024 }, function (err, stdout) {
35+
execFile('zdump', ['-v', src], { maxBuffer: 20*1024*1024 }, function (err, stdout) {
3836
if (err) { throw err; }
3937

4038
grunt.file.mkdir(path.dirname(dest));
4139

4240
if (stdout.length === 0) {
4341
// on some systems, when there are no transitions then we have
4442
// to get creative to learn the offset and abbreviation
45-
exec('zdump UTC ' + src, { maxBuffer: 20*1024*1024 }, function (_err, _stdout) {
43+
execFile('zdump', ['UTC', src], { maxBuffer: 20*1024*1024 }, function (_err, _stdout) {
4644
if (_err) { throw _err; }
4745

4846
grunt.file.write(dest + '.zdump', normalizePaths(_stdout));

tasks/data-zic.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use strict";
22

33
var path = require('path'),
4-
exec = require('child_process').exec;
4+
execFile = require('child_process').execFile;
55

66
module.exports = function (grunt) {
77
grunt.registerTask('data-zic', '2. Compile data sources with zic(8).', function (version) {
@@ -22,7 +22,10 @@ module.exports = function (grunt) {
2222
var file = files.shift(),
2323
src = path.resolve('temp/download', version, file);
2424

25-
exec('zic -d ' + dest + ' ' + src, function (err) {
25+
if (!grunt.file.exists(src)) {
26+
throw new Error("Can't process " + src + " with zic. File doesn't exist");
27+
}
28+
execFile('zic', ['-d', dest, src], function (err) {
2629
if (err) { throw err; }
2730

2831
grunt.verbose.ok('Compiled zic ' + version + ':' + file);

0 commit comments

Comments
 (0)