Skip to content

Commit

Permalink
Update node spawn process hardening (elastic#186786)
Browse files Browse the repository at this point in the history
### Summary
Update node `child_process.spawn` hardening with updated measures.

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
SiddharthMantri and kibanamachine committed Jun 26, 2024
1 parent 15a495a commit bbafad4
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 11 deletions.
25 changes: 14 additions & 11 deletions src/setup_node_env/harden/child_process.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,31 +30,34 @@ function patchChildProcess(cp) {
function patchOptions(hasArgs) {
return function apply(target, thisArg, args) {
var pos = 1;
if (pos === args.length) {
var newArgs = Object.setPrototypeOf([].concat(args), null);

if (pos === newArgs.length) {
// fn(arg1)
args[pos] = prototypelessSpawnOpts();
} else if (pos < args.length) {
if (hasArgs && (Array.isArray(args[pos]) || args[pos] == null)) {
newArgs[pos] = prototypelessSpawnOpts();
} else if (pos < newArgs.length) {
if (hasArgs && (Array.isArray(newArgs[pos]) || newArgs[pos] == null)) {
// fn(arg1, args, ...)
pos++;
}

if (typeof args[pos] === 'object' && args[pos] !== null) {
if (typeof newArgs[pos] === 'object' && newArgs[pos] !== null) {
// fn(arg1, {}, ...)
// fn(arg1, args, {}, ...)
args[pos] = prototypelessSpawnOpts(args[pos]);
} else if (args[pos] == null) {
newArgs[pos] = prototypelessSpawnOpts(newArgs[pos]);
} else if (newArgs[pos] == null) {
// fn(arg1, null/undefined, ...)
// fn(arg1, args, null/undefined, ...)
args[pos] = prototypelessSpawnOpts();
} else if (typeof args[pos] === 'function') {
newArgs[pos] = prototypelessSpawnOpts();
} else if (typeof newArgs[pos] === 'function') {
// fn(arg1, callback)
// fn(arg1, args, callback)
args.splice(pos, 0, prototypelessSpawnOpts());
// `newArgs` doesn't have prototype and hence `splice` method anymore.
Array.prototype.splice.call(newArgs, pos, 0, prototypelessSpawnOpts());
}
}

return target.apply(thisArg, args);
return target.apply(thisArg, newArgs);
};
}

Expand Down
9 changes: 9 additions & 0 deletions test/harden/_node_script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

console.log('Hello from _node_script.js!');
44 changes: 44 additions & 0 deletions test/harden/child_process.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,50 @@ for (const name of functions) {
assertProcess(t, cp.spawn(command, [], { env: { custom: 'custom' } }), { stdout: 'custom' });
});

test('spawn(command, options) - prevent object prototype pollution', (t) => {
const pathName = path.join(__dirname, '_node_script.js');
const options = {};
const pollutedObject = {
env: {
NODE_OPTIONS: `--require ${pathName}`,
},
shell: process.argv[0],
};
// eslint-disable-next-line no-proto
options.__proto__['2'] = pollutedObject;

const argsArray = [];

/**
* Declares that 3 assertions should be run.
* We don't use the assertProcess function here as we need an extra assertion
* for the polluted prototype
*/
t.plan(3);

t.deepEqual(
argsArray[2],
pollutedObject,
'Prototype should be polluted with the object at index 2'
);

const stdout = '';

const cmd = cp.spawn(command, argsArray);
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();
});
});

for (const unset of notSet) {
test(`spawn(command, ${unset})`, (t) => {
assertProcess(t, cp.spawn(command, unset));
Expand Down

0 comments on commit bbafad4

Please sign in to comment.