How do I escape a string for a shell command in node?

前端 未结 6 1798
粉色の甜心
粉色の甜心 2020-12-01 05:04

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 app

相关标签:
6条回答
  • 2020-12-01 05:31

    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

    0 讨论(0)
  • 2020-12-01 05:34

    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!

    0 讨论(0)
  • 2020-12-01 05:38

    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.

    0 讨论(0)
  • 2020-12-01 05:44

    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.

    0 讨论(0)
  • 2020-12-01 05:53

    This is what I use:

    var escapeShell = function(cmd) {
      return '"'+cmd.replace(/(["\s'$`\\])/g,'\\$1')+'"';
    };
    
    0 讨论(0)
  • 2020-12-01 05:54

    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.");
    
    0 讨论(0)
提交回复
热议问题