Como faço para escapar de uma string para um comando shell no nó?
-
21-09-2019 - |
Pergunta
Dentro nodejs, a única maneira de executar comandos externos é via sys.exec (CMD). Eu gostaria de chamar um comando externo e fornecer dados via Stdin. No Nodejs, ainda não parece ser uma maneira de abrir um comando e depois empurrar dados para ele (apenas para executar e receber suas saídas padrão+erros), então parece que a única maneira de fazer isso agora é através de um único comando de string, como:
var dangerStr = "bad stuff here";
sys.exec("echo '" + dangerStr + "' | somecommand");
A maioria das respostas para perguntas como essa se concentrou em Regex, que não funciona para mim no NodeJS (que usa o mecanismo JavaScript V8 do Google) ou recursos nativos de outros idiomas como o Python.
Eu gostaria de escapar do Dangerstr, para que seja seguro compor uma sequência executiva como a acima. Se ajudar, o DERGERSTR conterá dados JSON.
Solução
Existe uma maneira de escrever para um comando externo: process.createChildProcess
(documentação) retorna um objeto com um write
método. createChildProcess
Porém, não é tão conveniente, porque não bobina o stdout e o stderr, então você precisará de manipuladores de eventos para ler a saída em pedaços.
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.");
Outras dicas
Isso é o que eu uso:
var escapeShell = function(cmd) {
return '"'+cmd.replace(/(["\s'$`\\])/g,'\\$1')+'"';
};
Se você precisar de uma solução simples, pode usar isso:
function escapeShellArg (arg) {
return `'${arg.replace(/'/g, `'\\''`)}'`;
}
Portanto, sua string será simplesmente escapada com citações únicas, como Chris Johnsen mencionou.
echo 'John'\''s phone';
Funciona bash
por causa de citação forte, parece que também funciona em fish
, mas não funciona em zsh
e sh
.
Se você tem bash
Seu pode executar seu script em sh
ou zsh
com 'bash -c \'' + escape('all-the-rest-escaped') + '\''
.
Mas na verdade ... Node.js escapará de todos os personagens necessários para você:
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);
});
Este bloco de código será executado:
echo '`echo 1`;"echo $SSH_TTY;'\''\\0{0..5}'
e será lançado:
stdout: `echo 1`;"echo $SSH_TTY;\'\\0{0..5}
ou algum erro.
Dar uma olhada em http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
A propósito, a solução simples para executar vários comandos é:
require('child_process')
.spawn('sh', ['-c', [
'cd all/your/commands',
'ls here',
'echo "and even" > more'
].join('; ')]);
Tenha um bom dia!
Você deve Nunca Confie em escapar de informações desconhecidas para um parâmetro de shell - Quase sempre haverá algum caso de ponta que você não pensou que permite ao usuário executar o código arbitrário no seu servidor.
O Node possui suporte para chamar um comando e passar cada argumento separadamente, sem escapar necessário. Esta é a maneira mais segura de fazer isso:
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}`);
});
A documentação é aqui
Se você também precisar lidar com caráter especial (quebras de linha etc.), pode fazê-lo desta maneira:
str = JSON.stringify(str)
.replace(/^"|"$/g,'') //remove JSON-string double quotes
.replace(/'/g, '\'"\'"\'') //escape single quotes the ugly bash way
Isso pressupõe que você use o Bash citando forte via cotas únicas) e o receptor pode entender a fuga do tipo C da JSON.