Как мне экранировать строку для команды оболочки в узле?
-
21-09-2019 - |
Вопрос
В nodejs, единственный способ выполнить внешние команды — через sys.exec(cmd).Я хотел бы вызвать внешнюю команду и передать ей данные через стандартный ввод.В nodejs, похоже, еще нет способа открыть команду, а затем передать в нее данные (только для выполнения и получения ее стандартных результатов + ошибок), поэтому, похоже, единственный способ сделать это прямо сейчас - с помощью одной строковой команды, например:
var dangerStr = "bad stuff here";
sys.exec("echo '" + dangerStr + "' | somecommand");
Большинство ответов на подобные вопросы касались либо регулярных выражений, которые у меня не работают в nodejs (который использует движок Google V8 Javascript), либо собственных функций других языков, таких как Python.
Я хотел бы избежать опасностиStr, чтобы можно было безопасно составить строку exec, подобную приведенной выше.Если это поможет, опасностьStr будет содержать данные JSON.
Решение
Существует способ записи во внешнюю команду: process.createChildProcess
(документация) возвращает объект с write
метод. createChildProcess
Однако это не так удобно, поскольку он не буферизует stdout и stderr, поэтому вам потребуются обработчики событий для чтения вывода по частям.
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.");
Другие советы
Это то, что я использую:
var escapeShell = function(cmd) {
return '"'+cmd.replace(/(["\s'$`\\])/g,'\\$1')+'"';
};
Если вам нужно простое решение, вы можете использовать это:
function escapeShellArg (arg) {
return `'${arg.replace(/'/g, `'\\''`)}'`;
}
Таким образом, ваша строка будет просто экранирована одинарными кавычками, как упомянул Крис Джонсен.
echo 'John'\''s phone';
Это работает в bash
из-за сильное цитирование, такое ощущение, что это работает и в fish
, но не работает в zsh
и sh
.
Если у вас есть bash
вы можете запустить свой скрипт в sh
или zsh
с 'bash -c \'' + escape('all-the-rest-escaped') + '\''
.
Но на самом деле...node.js экранирует все необходимые символы:
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);
});
этот блок кода выполнит:
echo '`echo 1`;"echo $SSH_TTY;'\''\\0{0..5}'
и выведет:
stdout: `echo 1`;"echo $SSH_TTY;\'\\0{0..5}
или какая-то ошибка.
Взгляни на http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
Кстати, простое решение для запуска нескольких команд:
require('child_process')
.spawn('sh', ['-c', [
'cd all/your/commands',
'ls here',
'echo "and even" > more'
].join('; ')]);
Хорошего дня!
Ты должен никогда полагаться на экранирование неизвестного ввода, передаваемого в параметр оболочки - почти всегда будет какой-то крайний случай, о котором вы не подумали, который позволяет пользователю выполнять произвольный код на вашем сервере.
Node поддерживает вызов команды и передачу каждого аргумента отдельно, без необходимости экранирования.Это самый безопасный способ сделать это:
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}`);
});
Документация здесь
Если вам также нужно иметь дело со специальными символами (переносами строк и т. д.), вы можете сделать это следующим образом:
str = JSON.stringify(str)
.replace(/^"|"$/g,'') //remove JSON-string double quotes
.replace(/'/g, '\'"\'"\'') //escape single quotes the ugly bash way
Предполагается, что вы используете Bash сильное цитирование через одинарные кавычки), и получатель сможет понять C-подобное экранирование JSON.