mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Harden creation of child processes (#55697)
Add general protection against RCE vulnerabilities similar to the one described in CVE-2019-7609. Closes #49605
This commit is contained in:
parent
3f365a82f8
commit
a663f65bcc
12 changed files with 809 additions and 10 deletions
10
.eslintrc.js
10
.eslintrc.js
|
@ -514,6 +514,16 @@ module.exports = {
|
|||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Harden specific rules
|
||||
*/
|
||||
{
|
||||
files: ['test/harden/*.js'],
|
||||
rules: {
|
||||
'mocha/handle-done-callback': 'off', // TODO: Find a way to disable all mocha rules
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* APM overrides
|
||||
*/
|
||||
|
|
|
@ -246,6 +246,7 @@
|
|||
"regenerator-runtime": "^0.13.3",
|
||||
"regression": "2.0.1",
|
||||
"request": "^2.88.0",
|
||||
"require-in-the-middle": "^5.0.2",
|
||||
"reselect": "^4.0.0",
|
||||
"resize-observer-polyfill": "^1.5.0",
|
||||
"rison-node": "1.0.2",
|
||||
|
@ -480,6 +481,7 @@
|
|||
"strip-ansi": "^3.0.1",
|
||||
"supertest": "^3.1.0",
|
||||
"supertest-as-promised": "^4.0.2",
|
||||
"tape": "^4.13.0",
|
||||
"tree-kill": "^1.2.2",
|
||||
"typescript": "3.7.2",
|
||||
"typings-tester": "^0.3.2",
|
||||
|
|
41
scripts/test_hardening.js
Normal file
41
scripts/test_hardening.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
var execFileSync = require('child_process').execFileSync;
|
||||
var path = require('path');
|
||||
var syncGlob = require('glob').sync;
|
||||
var program = require('commander');
|
||||
|
||||
program
|
||||
.name('node scripts/test_hardening.js')
|
||||
.arguments('[file...]')
|
||||
.description(
|
||||
'Run the tests in test/harden directory. If no files are provided, all files within the directory will be run.'
|
||||
)
|
||||
.action(function(globs) {
|
||||
if (globs.length === 0) globs.push(path.join('test', 'harden', '*'));
|
||||
globs.forEach(function(glob) {
|
||||
syncGlob(glob).forEach(function(filename) {
|
||||
if (path.basename(filename)[0] === '_') return;
|
||||
console.log(process.argv[0], filename);
|
||||
execFileSync(process.argv[0], [filename], { stdio: 'inherit' });
|
||||
});
|
||||
});
|
||||
})
|
||||
.parse(process.argv);
|
24
src/setup_node_env/harden.js
Normal file
24
src/setup_node_env/harden.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
var hook = require('require-in-the-middle');
|
||||
|
||||
hook(['child_process'], function(exports, name) {
|
||||
return require(`./patches/${name}`)(exports); // eslint-disable-line import/no-dynamic-require
|
||||
});
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
require('./harden'); // this require MUST be executed before any others
|
||||
require('symbol-observable');
|
||||
require('./root');
|
||||
require('./node_version_validator');
|
||||
|
|
76
src/setup_node_env/patches/child_process.js
Normal file
76
src/setup_node_env/patches/child_process.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
// Ensure, when spawning a new child process, that the `options` and the
|
||||
// `options.env` object passed to the child process function doesn't inherit
|
||||
// from `Object.prototype`. This protects against similar RCE vulnerabilities
|
||||
// as described in CVE-2019-7609
|
||||
module.exports = function(cp) {
|
||||
// The `exec` function is currently just a wrapper around `execFile`. So for
|
||||
// now there's no need to patch it. If this changes in the future, our tests
|
||||
// will fail and we can uncomment the line below.
|
||||
//
|
||||
// cp.exec = new Proxy(cp.exec, { apply: patchOptions() });
|
||||
|
||||
cp.execFile = new Proxy(cp.execFile, { apply: patchOptions(true) });
|
||||
cp.fork = new Proxy(cp.fork, { apply: patchOptions(true) });
|
||||
cp.spawn = new Proxy(cp.spawn, { apply: patchOptions(true) });
|
||||
cp.execFileSync = new Proxy(cp.execFileSync, { apply: patchOptions(true) });
|
||||
cp.execSync = new Proxy(cp.execSync, { apply: patchOptions() });
|
||||
cp.spawnSync = new Proxy(cp.spawnSync, { apply: patchOptions(true) });
|
||||
|
||||
return cp;
|
||||
};
|
||||
|
||||
function patchOptions(hasArgs) {
|
||||
return function apply(target, thisArg, args) {
|
||||
var pos = 1;
|
||||
if (pos === args.length) {
|
||||
// fn(arg1)
|
||||
args[pos] = prototypelessSpawnOpts();
|
||||
} else if (pos < args.length) {
|
||||
if (hasArgs && (Array.isArray(args[pos]) || args[pos] == null)) {
|
||||
// fn(arg1, args, ...)
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (typeof args[pos] === 'object' && args[pos] !== null) {
|
||||
// fn(arg1, {}, ...)
|
||||
// fn(arg1, args, {}, ...)
|
||||
args[pos] = prototypelessSpawnOpts(args[pos]);
|
||||
} else if (args[pos] == null) {
|
||||
// fn(arg1, null/undefined, ...)
|
||||
// fn(arg1, args, null/undefined, ...)
|
||||
args[pos] = prototypelessSpawnOpts();
|
||||
} else if (typeof args[pos] === 'function') {
|
||||
// fn(arg1, callback)
|
||||
// fn(arg1, args, callback)
|
||||
args.splice(pos, 0, prototypelessSpawnOpts());
|
||||
}
|
||||
}
|
||||
|
||||
return target.apply(thisArg, args);
|
||||
};
|
||||
}
|
||||
|
||||
function prototypelessSpawnOpts(obj) {
|
||||
var prototypelessObj = Object.assign(Object.create(null), obj);
|
||||
prototypelessObj.env = Object.assign(Object.create(null), prototypelessObj.env || process.env);
|
||||
return prototypelessObj;
|
||||
}
|
|
@ -210,6 +210,12 @@ module.exports = function(grunt) {
|
|||
args: ['scripts/notice', '--validate'],
|
||||
}),
|
||||
|
||||
test_hardening: scriptWithGithubChecks({
|
||||
title: 'Node.js hardening tests',
|
||||
cmd: NODE,
|
||||
args: ['scripts/test_hardening.js'],
|
||||
}),
|
||||
|
||||
apiIntegrationTests: scriptWithGithubChecks({
|
||||
title: 'API integration tests',
|
||||
cmd: NODE,
|
||||
|
|
|
@ -37,6 +37,7 @@ module.exports = function(grunt) {
|
|||
'run:test_jest_integration',
|
||||
'run:test_projects',
|
||||
'run:test_karma_ci',
|
||||
'run:test_hardening',
|
||||
'run:apiIntegrationTests',
|
||||
]);
|
||||
};
|
||||
|
|
3
test/harden/_echo.sh
Executable file
3
test/harden/_echo.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
echo $POLLUTED$custom
|
20
test/harden/_fork.js
Normal file
20
test/harden/_fork.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
console.log(`${process.env.POLLUTED || ''}${process.env.custom || ''}`);
|
587
test/harden/child_process.js
Normal file
587
test/harden/child_process.js
Normal file
|
@ -0,0 +1,587 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
require('../../src/setup_node_env');
|
||||
|
||||
const cp = require('child_process');
|
||||
const path = require('path');
|
||||
const test = require('tape');
|
||||
|
||||
Object.prototype.POLLUTED = 'polluted!'; // eslint-disable-line no-extend-native
|
||||
|
||||
const notSet = [null, undefined];
|
||||
|
||||
test.onFinish(() => {
|
||||
delete Object.prototype.POLLUTED;
|
||||
});
|
||||
|
||||
test('test setup ok', t => {
|
||||
t.equal({}.POLLUTED, 'polluted!');
|
||||
t.end();
|
||||
});
|
||||
|
||||
// TODO: fork() has been omitted as it doesn't validate its arguments in
|
||||
// Node.js 10 and will throw an internal error asynchronously. This is fixed in
|
||||
// newer versions. See https://github.com/elastic/kibana/issues/59628
|
||||
const functions = ['exec', 'execFile', 'spawn', 'execFileSync', 'execSync', 'spawnSync'];
|
||||
for (const name of functions) {
|
||||
test(`${name}()`, t => {
|
||||
t.throws(() => cp[name](), /argument must be of type string/);
|
||||
t.end();
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const command = 'echo $POLLUTED$custom';
|
||||
|
||||
test('exec(command)', t => {
|
||||
assertProcess(t, cp.exec(command));
|
||||
});
|
||||
|
||||
test('exec(command, callback)', t => {
|
||||
cp.exec(command, (err, stdout, stderr) => {
|
||||
t.error(err);
|
||||
t.equal(stdout.trim(), '');
|
||||
t.equal(stderr.trim(), '');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('exec(command, options)', t => {
|
||||
assertProcess(t, cp.exec(command, {}));
|
||||
});
|
||||
|
||||
test('exec(command, options) - with custom env', t => {
|
||||
assertProcess(t, cp.exec(command, { env: { custom: 'custom' } }), { stdout: 'custom' });
|
||||
});
|
||||
|
||||
test('exec(command, options, callback)', t => {
|
||||
cp.exec(command, {}, (err, stdout, stderr) => {
|
||||
t.error(err);
|
||||
t.equal(stdout.trim(), '');
|
||||
t.equal(stderr.trim(), '');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('exec(command, options, callback) - with custom env', t => {
|
||||
cp.exec(command, { env: { custom: 'custom' } }, (err, stdout, stderr) => {
|
||||
t.error(err);
|
||||
t.equal(stdout.trim(), 'custom');
|
||||
t.equal(stderr.trim(), '');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
for (const unset of notSet) {
|
||||
test(`exec(command, ${unset})`, t => {
|
||||
assertProcess(t, cp.exec(command, unset));
|
||||
});
|
||||
|
||||
test(`exec(command, ${unset}, callback)`, t => {
|
||||
cp.exec(command, unset, (err, stdout, stderr) => {
|
||||
t.error(err);
|
||||
t.equal(stdout.trim(), '');
|
||||
t.equal(stderr.trim(), '');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const file = path.join('test', 'harden', '_echo.sh');
|
||||
|
||||
test('execFile(file)', t => {
|
||||
assertProcess(t, cp.execFile(file));
|
||||
});
|
||||
|
||||
test('execFile(file, args)', t => {
|
||||
assertProcess(t, cp.execFile(file, []));
|
||||
});
|
||||
|
||||
test('execFile(file, callback)', t => {
|
||||
cp.execFile(file, (err, stdout, stderr) => {
|
||||
t.error(err);
|
||||
t.equal(stdout.trim(), '');
|
||||
t.equal(stderr.trim(), '');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('execFile(file, options)', t => {
|
||||
assertProcess(t, cp.execFile(file, {}));
|
||||
});
|
||||
|
||||
test('execFile(file, options) - with custom env', t => {
|
||||
assertProcess(t, cp.execFile(file, { env: { custom: 'custom' } }), { stdout: 'custom' });
|
||||
});
|
||||
|
||||
test('execFile(file, options, callback)', t => {
|
||||
cp.execFile(file, {}, (err, stdout, stderr) => {
|
||||
t.error(err);
|
||||
t.equal(stdout.trim(), '');
|
||||
t.equal(stderr.trim(), '');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('execFile(file, options, callback) - with custom env', t => {
|
||||
cp.execFile(file, { env: { custom: 'custom' } }, (err, stdout, stderr) => {
|
||||
t.error(err);
|
||||
t.equal(stdout.trim(), 'custom');
|
||||
t.equal(stderr.trim(), '');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('execFile(file, args, callback)', t => {
|
||||
cp.execFile(file, [], (err, stdout, stderr) => {
|
||||
t.error(err);
|
||||
t.equal(stdout.trim(), '');
|
||||
t.equal(stderr.trim(), '');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('execFile(file, args, options)', t => {
|
||||
assertProcess(t, cp.execFile(file, [], {}));
|
||||
});
|
||||
|
||||
test('execFile(file, args, options) - with custom env', t => {
|
||||
assertProcess(t, cp.execFile(file, [], { env: { custom: 'custom' } }), { stdout: 'custom' });
|
||||
});
|
||||
|
||||
test('execFile(file, args, options, callback)', t => {
|
||||
cp.execFile(file, [], {}, (err, stdout, stderr) => {
|
||||
t.error(err);
|
||||
t.equal(stdout.trim(), '');
|
||||
t.equal(stderr.trim(), '');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('execFile(file, args, options, callback) - with custom env', t => {
|
||||
cp.execFile(file, [], { env: { custom: 'custom' } }, (err, stdout, stderr) => {
|
||||
t.error(err);
|
||||
t.equal(stdout.trim(), 'custom');
|
||||
t.equal(stderr.trim(), '');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
for (const unset of notSet) {
|
||||
test(`execFile(file, ${unset})`, t => {
|
||||
assertProcess(t, cp.execFile(file, unset));
|
||||
});
|
||||
|
||||
test(`execFile(file, ${unset}, ${unset})`, t => {
|
||||
assertProcess(t, cp.execFile(file, unset, unset));
|
||||
});
|
||||
|
||||
test(`execFile(file, ${unset}, callback)`, t => {
|
||||
cp.execFile(file, unset, (err, stdout, stderr) => {
|
||||
t.error(err);
|
||||
t.equal(stdout.trim(), '');
|
||||
t.equal(stderr.trim(), '');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test(`execFile(file, ${unset}, ${unset}, callback)`, t => {
|
||||
cp.execFile(file, unset, unset, (err, stdout, stderr) => {
|
||||
t.error(err);
|
||||
t.equal(stdout.trim(), '');
|
||||
t.equal(stderr.trim(), '');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test(`execFile(file, ${unset}, options)`, t => {
|
||||
assertProcess(t, cp.execFile(file, unset, {}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const modulePath = path.join('test', 'harden', '_fork.js');
|
||||
|
||||
// NOTE: Forked processes don't have any stdout we can monitor without providing options
|
||||
test.skip('fork(modulePath)', t => {
|
||||
assertProcess(t, cp.fork(modulePath));
|
||||
});
|
||||
|
||||
// NOTE: Forked processes don't have any stdout we can monitor without providing options
|
||||
test.skip('execFile(file, args)', t => {
|
||||
assertProcess(t, cp.fork(modulePath, []));
|
||||
});
|
||||
|
||||
test('fork(modulePath, options)', t => {
|
||||
assertProcess(
|
||||
t,
|
||||
cp.fork(modulePath, {
|
||||
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('fork(modulePath, options) - with custom env', t => {
|
||||
assertProcess(
|
||||
t,
|
||||
cp.fork(modulePath, {
|
||||
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
|
||||
env: { custom: 'custom' },
|
||||
}),
|
||||
{ stdout: 'custom' }
|
||||
);
|
||||
});
|
||||
|
||||
test('fork(modulePath, args, options)', t => {
|
||||
assertProcess(
|
||||
t,
|
||||
cp.fork(modulePath, [], {
|
||||
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('fork(modulePath, args, options) - with custom env', t => {
|
||||
assertProcess(
|
||||
t,
|
||||
cp.fork(modulePath, [], {
|
||||
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
|
||||
env: { custom: 'custom' },
|
||||
}),
|
||||
{ stdout: 'custom' }
|
||||
);
|
||||
});
|
||||
|
||||
for (const unset of notSet) {
|
||||
// NOTE: Forked processes don't have any stdout we can monitor without providing options
|
||||
test.skip(`fork(modulePath, ${unset})`, t => {
|
||||
assertProcess(t, cp.fork(modulePath, unset));
|
||||
});
|
||||
|
||||
// NOTE: Forked processes don't have any stdout we can monitor without providing options
|
||||
test.skip(`fork(modulePath, ${unset}, ${unset})`, t => {
|
||||
assertProcess(t, cp.fork(modulePath, unset, unset));
|
||||
});
|
||||
|
||||
test(`fork(modulePath, ${unset}, options)`, t => {
|
||||
assertProcess(
|
||||
t,
|
||||
cp.fork(modulePath, unset, {
|
||||
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const command = path.join('test', 'harden', '_echo.sh');
|
||||
|
||||
test('spawn(command)', t => {
|
||||
assertProcess(t, cp.spawn(command));
|
||||
});
|
||||
|
||||
test('spawn(command, args)', t => {
|
||||
assertProcess(t, cp.spawn(command, []));
|
||||
});
|
||||
|
||||
test('spawn(command, options)', t => {
|
||||
assertProcess(t, cp.spawn(command, {}));
|
||||
});
|
||||
|
||||
test('spawn(command, options) - with custom env', t => {
|
||||
assertProcess(t, cp.spawn(command, { env: { custom: 'custom' } }), { stdout: 'custom' });
|
||||
});
|
||||
|
||||
test('spawn(command, args, options)', t => {
|
||||
assertProcess(t, cp.spawn(command, [], {}));
|
||||
});
|
||||
|
||||
test('spawn(command, args, options) - with custom env', t => {
|
||||
assertProcess(t, cp.spawn(command, [], { env: { custom: 'custom' } }), { stdout: 'custom' });
|
||||
});
|
||||
|
||||
for (const unset of notSet) {
|
||||
test(`spawn(command, ${unset})`, t => {
|
||||
assertProcess(t, cp.spawn(command, unset));
|
||||
});
|
||||
|
||||
test(`spawn(command, ${unset}, ${unset})`, t => {
|
||||
assertProcess(t, cp.spawn(command, unset, unset));
|
||||
});
|
||||
|
||||
test(`spawn(command, ${unset}, options)`, t => {
|
||||
assertProcess(t, cp.spawn(command, unset, {}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const file = path.join('test', 'harden', '_echo.sh');
|
||||
|
||||
test('execFileSync(file)', t => {
|
||||
t.equal(
|
||||
cp
|
||||
.execFileSync(file)
|
||||
.toString()
|
||||
.trim(),
|
||||
''
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('execFileSync(file, args)', t => {
|
||||
t.equal(
|
||||
cp
|
||||
.execFileSync(file, [])
|
||||
.toString()
|
||||
.trim(),
|
||||
''
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('execFileSync(file, options)', t => {
|
||||
t.equal(
|
||||
cp
|
||||
.execFileSync(file, {})
|
||||
.toString()
|
||||
.trim(),
|
||||
''
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('execFileSync(file, options) - with custom env', t => {
|
||||
t.equal(
|
||||
cp
|
||||
.execFileSync(file, { env: { custom: 'custom' } })
|
||||
.toString()
|
||||
.trim(),
|
||||
'custom'
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('execFileSync(file, args, options)', t => {
|
||||
t.equal(
|
||||
cp
|
||||
.execFileSync(file, [], {})
|
||||
.toString()
|
||||
.trim(),
|
||||
''
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('execFileSync(file, args, options) - with custom env', t => {
|
||||
t.equal(
|
||||
cp
|
||||
.execFileSync(file, [], { env: { custom: 'custom' } })
|
||||
.toString()
|
||||
.trim(),
|
||||
'custom'
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
for (const unset of notSet) {
|
||||
test(`execFileSync(file, ${unset})`, t => {
|
||||
t.equal(
|
||||
cp
|
||||
.execFileSync(file, unset)
|
||||
.toString()
|
||||
.trim(),
|
||||
''
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test(`execFileSync(file, ${unset}, ${unset})`, t => {
|
||||
t.equal(
|
||||
cp
|
||||
.execFileSync(file, unset, unset)
|
||||
.toString()
|
||||
.trim(),
|
||||
''
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test(`execFileSync(file, ${unset}, options)`, t => {
|
||||
t.equal(
|
||||
cp
|
||||
.execFileSync(file, unset, {})
|
||||
.toString()
|
||||
.trim(),
|
||||
''
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const command = 'echo $POLLUTED$custom';
|
||||
|
||||
test('execSync(command)', t => {
|
||||
t.equal(
|
||||
cp
|
||||
.execSync(command)
|
||||
.toString()
|
||||
.trim(),
|
||||
''
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('execSync(command, options)', t => {
|
||||
t.equal(
|
||||
cp
|
||||
.execSync(command, {})
|
||||
.toString()
|
||||
.trim(),
|
||||
''
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('execSync(command, options) - with custom env', t => {
|
||||
t.equal(
|
||||
cp
|
||||
.execSync(command, { env: { custom: 'custom' } })
|
||||
.toString()
|
||||
.trim(),
|
||||
'custom'
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
for (const unset of notSet) {
|
||||
test(`execSync(command, ${unset})`, t => {
|
||||
t.equal(
|
||||
cp
|
||||
.execSync(command, unset)
|
||||
.toString()
|
||||
.trim(),
|
||||
''
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const command = path.join('test', 'harden', '_echo.sh');
|
||||
|
||||
test('spawnSync(command)', t => {
|
||||
const result = cp.spawnSync(command);
|
||||
t.error(result.error);
|
||||
t.equal(result.stdout.toString().trim(), '');
|
||||
t.equal(result.stderr.toString().trim(), '');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('spawnSync(command, args)', t => {
|
||||
const result = cp.spawnSync(command, []);
|
||||
t.error(result.error);
|
||||
t.equal(result.stdout.toString().trim(), '');
|
||||
t.equal(result.stderr.toString().trim(), '');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('spawnSync(command, options)', t => {
|
||||
const result = cp.spawnSync(command, {});
|
||||
t.error(result.error);
|
||||
t.equal(result.stdout.toString().trim(), '');
|
||||
t.equal(result.stderr.toString().trim(), '');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('spawnSync(command, options) - with custom env', t => {
|
||||
const result = cp.spawnSync(command, { env: { custom: 'custom' } });
|
||||
t.error(result.error);
|
||||
t.equal(result.stdout.toString().trim(), 'custom');
|
||||
t.equal(result.stderr.toString().trim(), '');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('spawnSync(command, args, options)', t => {
|
||||
const result = cp.spawnSync(command, [], {});
|
||||
t.error(result.error);
|
||||
t.equal(result.stdout.toString().trim(), '');
|
||||
t.equal(result.stderr.toString().trim(), '');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('spawnSync(command, args, options) - with custom env', t => {
|
||||
const result = cp.spawnSync(command, [], { env: { custom: 'custom' } });
|
||||
t.error(result.error);
|
||||
t.equal(result.stdout.toString().trim(), 'custom');
|
||||
t.equal(result.stderr.toString().trim(), '');
|
||||
t.end();
|
||||
});
|
||||
|
||||
for (const unset of notSet) {
|
||||
test(`spawnSync(command, ${unset})`, t => {
|
||||
const result = cp.spawnSync(command, unset);
|
||||
t.error(result.error);
|
||||
t.equal(result.stdout.toString().trim(), '');
|
||||
t.equal(result.stderr.toString().trim(), '');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test(`spawnSync(command, ${unset}, ${unset})`, t => {
|
||||
const result = cp.spawnSync(command, unset, unset);
|
||||
t.error(result.error);
|
||||
t.equal(result.stdout.toString().trim(), '');
|
||||
t.equal(result.stderr.toString().trim(), '');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test(`spawnSync(command, ${unset}, options)`, t => {
|
||||
const result = cp.spawnSync(command, unset, {});
|
||||
t.error(result.error);
|
||||
t.equal(result.stdout.toString().trim(), '');
|
||||
t.equal(result.stderr.toString().trim(), '');
|
||||
t.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function assertProcess(t, cmd, { stdout = '' } = {}) {
|
||||
t.plan(2);
|
||||
|
||||
cmd.stdout.on('data', data => {
|
||||
t.equal(data.toString().trim(), stdout);
|
||||
});
|
||||
|
||||
cmd.stderr.on('data', data => {
|
||||
t.fail(`Unexpected data on STDERR: "${data}"`);
|
||||
});
|
||||
|
||||
cmd.on('close', code => {
|
||||
t.equal(code, 0);
|
||||
t.end();
|
||||
});
|
||||
}
|
48
yarn.lock
48
yarn.lock
|
@ -11190,7 +11190,7 @@ deep-equal@^1.0.0, deep-equal@^1.0.1, deep-equal@~1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
|
||||
integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=
|
||||
|
||||
deep-equal@^1.1.1:
|
||||
deep-equal@^1.1.1, deep-equal@~1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
|
||||
integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==
|
||||
|
@ -11853,6 +11853,13 @@ dotenv@^8.0.0:
|
|||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.1.0.tgz#d811e178652bfb8a1e593c6dd704ec7e90d85ea2"
|
||||
integrity sha512-GUE3gqcDCaMltj2++g6bRQ5rBJWtkWTmqmD0fo1RnnMuUqHNCt2oTPeDnS9n6fKYvlhn7AeBkb38lymBtWBQdA==
|
||||
|
||||
dotignore@~0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/dotignore/-/dotignore-0.1.2.tgz#f942f2200d28c3a76fbdd6f0ee9f3257c8a2e905"
|
||||
integrity sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==
|
||||
dependencies:
|
||||
minimatch "^3.0.4"
|
||||
|
||||
downgrade-root@^1.0.0:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/downgrade-root/-/downgrade-root-1.2.2.tgz#531319715b0e81ffcc22eb28478ba27643e12c6c"
|
||||
|
@ -12369,7 +12376,7 @@ error@^7.0.0, error@^7.0.2:
|
|||
string-template "~0.2.1"
|
||||
xtend "~4.0.0"
|
||||
|
||||
es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.13.0, es-abstract@^1.14.2, es-abstract@^1.17.0-next.1, es-abstract@^1.4.3, es-abstract@^1.5.0, es-abstract@^1.5.1, es-abstract@^1.7.0, es-abstract@^1.9.0:
|
||||
es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.13.0, es-abstract@^1.14.2, es-abstract@^1.4.3, es-abstract@^1.5.0, es-abstract@^1.5.1, es-abstract@^1.7.0, es-abstract@^1.9.0:
|
||||
version "1.17.0"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.0.tgz#f42a517d0036a5591dbb2c463591dc8bb50309b1"
|
||||
integrity sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug==
|
||||
|
@ -12386,7 +12393,7 @@ es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.13.0, es-abstract@^1.14
|
|||
string.prototype.trimleft "^2.1.1"
|
||||
string.prototype.trimright "^2.1.1"
|
||||
|
||||
es-abstract@^1.15.0:
|
||||
es-abstract@^1.15.0, es-abstract@^1.17.0-next.1:
|
||||
version "1.17.4"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184"
|
||||
integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==
|
||||
|
@ -14908,7 +14915,7 @@ glob@^6.0.1, glob@^6.0.4:
|
|||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^7.0.5, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
|
||||
glob@^7.0.5, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.6:
|
||||
version "7.1.6"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
||||
|
@ -16714,7 +16721,7 @@ inflight@^1.0.4:
|
|||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2, inherits@~2.0.3:
|
||||
inherits@2, inherits@~2.0.3, inherits@~2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
@ -17542,7 +17549,7 @@ is-redirect@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24"
|
||||
integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=
|
||||
|
||||
is-regex@^1.0.3, is-regex@^1.0.4, is-regex@^1.0.5:
|
||||
is-regex@^1.0.3, is-regex@^1.0.4, is-regex@^1.0.5, is-regex@~1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae"
|
||||
integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==
|
||||
|
@ -21833,7 +21840,7 @@ object-identity-map@^1.0.2:
|
|||
dependencies:
|
||||
object.entries "^1.1.0"
|
||||
|
||||
object-inspect@^1.7.0:
|
||||
object-inspect@^1.7.0, object-inspect@~1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67"
|
||||
integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==
|
||||
|
@ -25830,7 +25837,7 @@ require-from-string@^2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
|
||||
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
|
||||
|
||||
require-in-the-middle@^5.0.0:
|
||||
require-in-the-middle@^5.0.0, require-in-the-middle@^5.0.2:
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-5.0.2.tgz#ce3593007a61583b39ccdcd2c167a2a326c670b2"
|
||||
integrity sha512-l2r6F9i6t5xp4OE9cw/daB/ooQKHZOOW1AYPADhEvk/Tj/THJDS8gePp76Zyuht6Cj57a0KL+eHK5Dyv7wZnKA==
|
||||
|
@ -26042,7 +26049,7 @@ resolve@^1.12.0, resolve@^1.4.0:
|
|||
dependencies:
|
||||
path-parse "^1.0.6"
|
||||
|
||||
resolve@^1.13.1:
|
||||
resolve@^1.13.1, resolve@~1.14.2:
|
||||
version "1.14.2"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2"
|
||||
integrity sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ==
|
||||
|
@ -27895,7 +27902,7 @@ string.prototype.padstart@^3.0.0:
|
|||
es-abstract "^1.4.3"
|
||||
function-bind "^1.0.2"
|
||||
|
||||
string.prototype.trim@^1.2.1:
|
||||
string.prototype.trim@^1.2.1, string.prototype.trim@~1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz#141233dff32c82bfad80684d7e5f0869ee0fb782"
|
||||
integrity sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw==
|
||||
|
@ -28465,6 +28472,27 @@ tapable@^1.1.3:
|
|||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
|
||||
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
|
||||
|
||||
tape@^4.13.0:
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/tape/-/tape-4.13.0.tgz#e2f581ff5f12a7cbd787e9f83c76c2851782fce2"
|
||||
integrity sha512-J/hvA+GJnuWJ0Sj8Z0dmu3JgMNU+MmusvkCT7+SN4/2TklW18FNCp/UuHIEhPZwHfy4sXfKYgC7kypKg4umbOw==
|
||||
dependencies:
|
||||
deep-equal "~1.1.1"
|
||||
defined "~1.0.0"
|
||||
dotignore "~0.1.2"
|
||||
for-each "~0.3.3"
|
||||
function-bind "~1.1.1"
|
||||
glob "~7.1.6"
|
||||
has "~1.0.3"
|
||||
inherits "~2.0.4"
|
||||
is-regex "~1.0.5"
|
||||
minimist "~1.2.0"
|
||||
object-inspect "~1.7.0"
|
||||
resolve "~1.14.2"
|
||||
resumer "~0.0.0"
|
||||
string.prototype.trim "~1.2.1"
|
||||
through "~2.3.8"
|
||||
|
||||
tape@^4.5.1:
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/tape/-/tape-4.10.2.tgz#129fcf62f86df92687036a52cce7b8ddcaffd7a6"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue