mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
# Backport This will backport the following commits from `main` to `7.17`: - [Update node spawn process hardening (#186786)](https://github.com/elastic/kibana/pull/186786) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Sid","email":"siddharthmantri1@gmail.com"},"sourceCommit":{"committedDate":"2024-06-26T17:06:37Z","message":"Update node spawn process hardening (#186786)\n\n### Summary\r\nUpdate node `child_process.spawn` hardening with updated measures.\r\n\r\n---------\r\n\r\nCo-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>","sha":"bbafad47cc919230bf7595484f4af6bec8dacb6b","branchLabelMapping":{"^v8.15.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Team:Security","Feature:Hardening","v8.15.0","v7.17.23","v8.14.2"],"title":"Update node spawn process hardening ","number":186786,"url":"https://github.com/elastic/kibana/pull/186786","mergeCommit":{"message":"Update node spawn process hardening (#186786)\n\n### Summary\r\nUpdate node `child_process.spawn` hardening with updated measures.\r\n\r\n---------\r\n\r\nCo-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>","sha":"bbafad47cc919230bf7595484f4af6bec8dacb6b"}},"sourceBranch":"main","suggestedTargetBranches":["7.17","8.14"],"targetPullRequestStates":[{"branch":"main","label":"v8.15.0","branchLabelMappingKey":"^v8.15.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/186786","number":186786,"mergeCommit":{"message":"Update node spawn process hardening (#186786)\n\n### Summary\r\nUpdate node `child_process.spawn` hardening with updated measures.\r\n\r\n---------\r\n\r\nCo-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>","sha":"bbafad47cc919230bf7595484f4af6bec8dacb6b"}},{"branch":"7.17","label":"v7.17.23","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.14","label":"v8.14.2","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Sid <siddharthmantri1@gmail.com>
This commit is contained in:
parent
2829b50928
commit
d931262c0a
3 changed files with 67 additions and 11 deletions
|
@ -32,31 +32,34 @@ new ritm.Hook(['child_process'], function (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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
9
test/harden/_node_script.js
Normal file
9
test/harden/_node_script.js
Normal file
|
@ -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!');
|
|
@ -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));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue