English 中文(简体)
How do I escape a string for a shell command in node?
原标题:

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.

问题回答

This is what I use:

var escapeShell = function(cmd) {
  return  " +cmd.replace(/([" $`\])/g, \$1 )+ " ;
};

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 need simple (yet correct) 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!

I second the opinion of Will, whenever possible you should avoid escaping by hand and prefer spawn.

However, in the case that escaping is unavoidable, for example if you need to use exec or you are executing a command through ssh. Then you can use base64 to pass safe characters to bash and rely on bash to escape the unknown.

const dangerStr =  bad stuff here 
// base64 has safe characters [A-Za-z=0-9+/]
const dangerBase64 = btoa(dangerStr)

sys.exec(`echo "$(echo ${dangerBase64} | base64 -d)" | somecommand`)

The explanation is the following:

dangerBase64 is unknown but it does not contain unsafe characters in bash. Hence echo ${dangerBase64} will output what we want.

Finally the double quote around $(echo ${dangerBase64} | base64 -d) escape the actual value passed by the user inside bash, which is safe and has the same value that the user wanted.

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.

If you are building you own software, you can encode the command to base64 or hex format then decode the arguments from the program.

For my Nodejs applications I use.

var base64_encode = exports.base64_encode = function(non_base64_string){
    return Buffer.from(non_base64_string).toString( base64 );
}


var base64_decode = exports.base64_decode = function(base64_string){
    return Buffer.from(base64_string,  base64 ).toString( ascii )
}

So when I run a base64 encoded command like this

webman grep --search "aW5jbHVkZV9vbmNlICRfU0VSVkVSWyJET0NVTUVOVF9ST09UIl0uIi9zZXR0aW5ncy5waHAiOw==" --replacement "JGRvY3VtZW50X3Jvb3QgPSBfX0RJUl9fO3doaWxlKHRydWUpe2lmIChmaWxlX2V4aXN0cygkZG9jdW1lbnRfcm9vdC4iL3NldHRpbmdzLmpzb24iKSl7YnJlYWs7fWVsc2V7JGRvY3VtZW50X3Jvb3Q9ZGlybmFtZSgkZG9jdW1lbnRfcm9vdCk7fX08bmV3bGluZT5pbmNsdWRlX29uY2UgJGRvY3VtZW50X3Jvb3QuIi9zZXR0aW5ncy5waHAiOw=="

I can get the arguments search and replacement arguments without stress using base64_decode

A quick example using await, with proper automatic escaping (no handmade regexes)...

import util from  util ;
import {execFile as execFileWithCallback} from  child_process ;
const execFile = util.promisify(execFileWithCallback)

async function go() {
    const dangerStr1 = `"!£ "`
    const dangerStr2 = `>> more
 ; | ^Craziness`
    const result = await execFile("echo", [dangerStr1, dangerStr2])
    console.log(result.stdout)
}
go()

/*

Will pump out...

"!£ " >> more
 ; | ^Craziness

*/

Note that you can t do pipes with this technique. You d have to take result.stdout and slap it into another execFile() call.





相关问题
selected text in iframe

How to get a selected text inside a iframe. I my page i m having a iframe which is editable true. So how can i get the selected text in that iframe.

How to fire event handlers on the link using javascript

I would like to click a link in my page using javascript. I would like to Fire event handlers on the link without navigating. How can this be done? This has to work both in firefox and Internet ...

How to Add script codes before the </body> tag ASP.NET

Heres the problem, In Masterpage, the google analytics code were pasted before the end of body tag. In ASPX page, I need to generate a script (google addItem tracker) using codebehind ClientScript ...

Clipboard access using Javascript - sans Flash?

Is there a reliable way to access the client machine s clipboard using Javascript? I continue to run into permissions issues when attempting to do this. How does Google Docs do this? Do they use ...

javascript debugging question

I have a large javascript which I didn t write but I need to use it and I m slowely going trough it trying to figure out what does it do and how, I m using alert to print out what it does but now I ...

Parsing date like twitter

I ve made a little forum and I want parse the date on newest posts like twitter, you know "posted 40 minutes ago ","posted 1 hour ago"... What s the best way ? Thanx.

热门标签