Skip to content

Commit

Permalink
src: allows escaping NODE_OPTIONS with backslashes
Browse files Browse the repository at this point in the history
The characters specified within NODE_OPTIONS can now be escaped, which
is handy especially in conjunction with `--require` (where the file path
might happen to contain spaces that shouldn't cause the option to be
split into two).

Fixes: nodejs#12971
  • Loading branch information
Maël Nison authored and addaleax committed Apr 17, 2019
1 parent ba74e42 commit 4025612
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 4 deletions.
4 changes: 4 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,10 @@ if they had been specified on the command line before the actual command line
(so they can be overridden). Node.js will exit with an error if an option
that is not allowed in the environment is used, such as `-p` or a script file.

In case an option happens to contain a space or a backslash (for example within
the path passed to `--require`), they must be escaped using an additional
backslash.

Node.js options that are allowed are:
- `--diagnostic-report-directory`
- `--diagnostic-report-filename`
Expand Down
37 changes: 33 additions & 4 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -671,11 +671,40 @@ int InitializeNodeWithArgs(std::vector<std::string>* argv,

#if !defined(NODE_WITHOUT_NODE_OPTIONS)
std::string node_options;

if (credentials::SafeGetenv("NODE_OPTIONS", &node_options)) {
// [0] is expected to be the program name, fill it in from the real argv
// and use 'x' as a placeholder while parsing.
std::vector<std::string> env_argv = SplitString("x " + node_options, ' ');
env_argv[0] = argv->at(0);
std::vector<std::string> env_argv;
// [0] is expected to be the program name, fill it in from the real argv.
env_argv.push_back(argv->at(0));

bool will_start_new_arg = true;
for (std::string::size_type index = 0;
index < node_options.size();
++index) {
char c = node_options.at(index);

// Backslashes escape the following character
if (c == '\\') {
if (index + 1 == node_options.size()) {
fprintf(stderr,
"%s: invalid escaping for NODE_OPTIONS\n",
argv->at(0).c_str());
exit(9);
} else {
c = node_options.at(++index);
}
} else if (c == ' ') {
will_start_new_arg = true;
continue;
}

if (will_start_new_arg) {
env_argv.push_back(std::string(1, c));
will_start_new_arg = false;
} else {
env_argv.back() += c;
}
}

const int exit_code = ProcessGlobalArgs(&env_argv, nullptr, errors, true);
if (exit_code != 0) return exit_code;
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/print A.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('A');
4 changes: 4 additions & 0 deletions test/parallel/test-cli-node-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ const tmpdir = require('../common/tmpdir');
tmpdir.refresh();

const printA = require.resolve('../fixtures/printA.js');
const printSpaceA = require.resolve('../fixtures/print A.js');

expect(`-r ${printA}`, 'A\nB\n');
expect(`-r ${printA.replace(/[a-z]/gi, '\\$&')}`, 'A\nB\n');
expect(`-r ${printSpaceA.replace(/[\\ ]/g, '\\$&')}`, 'A\nB\n');
expect(`-r ${printA} -r ${printA}`, 'A\nB\n');
expect(` -r ${printA} -r ${printA}`, 'A\nB\n');
expect(` --require ${printA} --require ${printA}`, 'A\nB\n');
Expand Down

0 comments on commit 4025612

Please sign in to comment.