Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
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 rickyes committed Sep 25, 2020
commit a2e42ebe43f0f14ce498bccc8718ebc572693165
7 changes: 7 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,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:
- `--enable-fips`
- `--experimental-modules`
Expand Down
52 changes: 39 additions & 13 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2610,23 +2610,49 @@ void Init(std::vector<std::string>* argv,

#if !defined(NODE_WITHOUT_NODE_OPTIONS)
std::string node_options;
if (SafeGetenv("NODE_OPTIONS", &node_options)) {

if (credentials::SafeGetenv("NODE_OPTIONS", &node_options)) {
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));

// Split NODE_OPTIONS at each ' ' character.
std::string::size_type index = std::string::npos;
do {
std::string::size_type prev_index = index;
index = node_options.find(' ', index + 1);
if (index - prev_index == 1) continue;

const std::string option = node_options.substr(
prev_index + 1, index - prev_index - 1);
if (!option.empty())
env_argv.emplace_back(std::move(option));
} while (index != std::string::npos);
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;
}


ProcessArgv(&env_argv, nullptr, true);
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')
5 changes: 5 additions & 0 deletions test/parallel/test-cli-node-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ tmpdir.refresh();
process.chdir(tmpdir.path);

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('--no-deprecation', 'B\n');
expect('--no-warnings', 'B\n');
Expand Down