In nodejs, the only way to execute external commands is via sys.exec(cmd). I'd like to call an external command and give it data via stdin. In nodejs there does yet not appear to be a way to open a command and then push data to it (only to exec and receive its standard+error outputs), so it appears the only way I've got to do this right now is via a single string command such as:
var dangerStr = "bad stuff here";
sys.exec("echo '" + dangerStr + "' | somecommand");
Most answers to questions like this have focused on either regex which doesn't work for me in nodejs (which uses Google's V8 Javascript engine) or native features from other languages like Python.
I'd like to escape dangerStr so that it's safe to compose an exec string like the one above. If it helps, dangerStr will contain JSON data.
There is a way to write to an external command: process.createChildProcess
(documentation) returns an object with a write
method. createChildProcess
isn't as convenient though, because it doesn't buffer stdout and stderr, so you will need event handlers to read the output in chunks.
var stdout = "", stderr = "";
var child = process.createChildProcess("someCommand");
child.addListener("output", function (data) {
if (data !== null) {
stdout += data;
}
});
child.addListener("error", function (data) {
if (data !== null) {
stderr += data;
}
});
child.addListener("exit", function (code) {
if (code === 0) {
sys.puts(stdout);
}
else {
// error
}
});
child.write("This goes to someCommand's stdin.");
This is what I use:
var escapeShell = function(cmd) {
return '"'+cmd.replace(/(["\s'$`\\])/g,'\\$1')+'"';
};
If you need simple solution you can use this:
function escapeShellArg (arg) {
return `'${arg.replace(/'/g, `'\\''`)}'`;
}
So your string will be simply escaped with single quotes as Chris Johnsen mentioned.
echo 'John'\''s phone';
It works in bash
because of strong quoting, feels like it also works in fish
, but does not work in zsh
and sh
.
If you have bash
your can run your script in sh
or zsh
with 'bash -c \'' + escape('all-the-rest-escaped') + '\''
.
But actually... node.js will escape all needed characters for you:
var child = require('child_process')
.spawn('echo', ['`echo 1`;"echo $SSH_TTY;\'\\0{0..5}']);
child.stdout.on('data', function (data) {
console.log('stdout: ' + data);
});
child.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
this block of code will execute:
echo '`echo 1`;"echo $SSH_TTY;'\''\\0{0..5}'
and will output:
stdout: `echo 1`;"echo $SSH_TTY;\'\\0{0..5}
or some error.
Take a look at http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
By the way simple solution to run a bunch of commands is:
require('child_process')
.spawn('sh', ['-c', [
'cd all/your/commands',
'ls here',
'echo "and even" > more'
].join('; ')]);
Have a nice day!
You should never rely on escaping unknown input going to a shell parameter - there will almost always be some edge-case that you haven't thought of that allows the user to execute arbitrary code on your server.
Node has support for calling a command and passing each argument separately, with no escaping required. This is the safest way to do it:
const { spawn } = require('child_process');
// Note that the arguments are in an array, not using string interpolation
const ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
});
ls.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
The documentation is here
If you also need to deal with special character (line-breaks etc.) you can do it this way:
str = JSON.stringify(str)
.replace(/^"|"$/g,'') //remove JSON-string double quotes
.replace(/'/g, '\'"\'"\'') //escape single quotes the ugly bash way
This assumes you use Bash's strong-quoting via single-quotes) and the receiver can understand JSON's C-like escaping.