Skip to content
Permalink
Browse files

src: allows escaping NODE_OPTIONS with backslashes

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: #12971

PR-URL: #24065
Reviewed-By: Anna Henningsen <anna@addaleax.net>
  • Loading branch information...
Maël Nison authored and addaleax committed Nov 3, 2018
1 parent ba74e42 commit 3ef1512f9e416cc7656b9cd16934dffdee1b11d3
Showing with 55 additions and 4 deletions.
  1. +7 −0 doc/api/cli.md
  2. +42 −4 src/node.cc
  3. +1 −0 test/fixtures/print A.js
  4. +5 −0 test/parallel/test-cli-node-options.js
@@ -774,6 +774,13 @@ 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 value happens to contain a space (for example a path listed in
`--require`), it must be escaped using double quotes. For example:

```bash
--require "./my path/file.js"
```

Node.js options that are allowed are:
- `--diagnostic-report-directory`
- `--diagnostic-report-filename`
@@ -671,11 +671,49 @@ 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 is_in_string = false;
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 == '\\' && is_in_string) {
if (index + 1 == node_options.size()) {
errors->push_back("invalid value for NODE_OPTIONS "
"(invalid escape)\n");
return 9;
} else {
c = node_options.at(++index);
}
} else if (c == ' ' && !is_in_string) {
will_start_new_arg = true;
continue;
} else if (c == '"') {
is_in_string = !is_in_string;
continue;
}

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

if (is_in_string) {
errors->push_back("invalid value for NODE_OPTIONS "
"(unterminated string)\n");
return 9;
}

const int exit_code = ProcessGlobalArgs(&env_argv, nullptr, errors, true);
if (exit_code != 0) return exit_code;
@@ -0,0 +1 @@
console.log('A')
@@ -12,7 +12,12 @@ 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}`, 'A\nB\n');
expect(`-r ${JSON.stringify(printA)}`, 'A\nB\n');
expect(`-r ${JSON.stringify(printSpaceA)}`, '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');

0 comments on commit 3ef1512

Please sign in to comment.
You can’t perform that action at this time.