-
Notifications
You must be signed in to change notification settings - Fork 29.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
src: allow CLI args in env with NODE_OPTIONS #12028
Conversation
src/node.cc
Outdated
char* cstr = strdup(nodeopt.c_str()); | ||
// [0] is expected to be the program name, fill it in from the real argv. | ||
argv_from_env[argc_from_env++] = argv[0]; | ||
// XXX(sam) can I use strtok or strtok_r? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bnoordhuis ----^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would you not be able to use strtok_r
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not use std::string::find
and substr
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
strtok_r isn't ANSI C, not sure it exists on Windows or even all the unixen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sam-github My version of the manpage says it’s part of the recent POSIX standards, so I’d take that as a good sign. If you want to be sure whether it’s okay you could just kick of a CI run that uses it.
(You can also wait for Ben’s answer, of course, but I got the impression that he’s a bit more busy than usual right now…)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the little one wasn't due for a while. :-) Anyhow, Windows is not so POSIX, but I'll just do it and if it works in CI I guess its good for us. Of course, strtok is everywhere, but its not clear to me whether we care about it not being thread safe. Node doesn't here, but it might matter when its used as a library.
src/node.cc
Outdated
const char** argv, | ||
int* exec_argc, | ||
const char*** exec_argv, | ||
bool is_nodeopt = false) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you align the arguments vertically?
src/node.cc
Outdated
char* cstr = strdup(nodeopt.c_str()); | ||
// [0] is expected to be the program name, fill it in from the real argv. | ||
argv_from_env[argc_from_env++] = argv[0]; | ||
// XXX(sam) can I use strtok or strtok_r? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would you not be able to use strtok_r
?
src/node.cc
Outdated
if (SafeGetenv("NODEOPT", &nodeopt)) { | ||
const char** argv_from_env = new const char*[(nodeopt.length()+1) / 2]; | ||
int argc_from_env = 0; | ||
char* cstr = strdup(nodeopt.c_str()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the copy? nodeopt
will have its own memory anyway
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suspicion, the c_str() docs aren't amazingly clear about the lifetime of the underlying mem. I'll try without (though I've a mem bug somewhere anyway that I have to find).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sam-github c_str()
is an alias for data()
nowadays, it lives as long as the string instance itself
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can't non-const methods on the string reallocate the memory invalidating the last pointer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They can, but you’re not doing that? Indexed accesses seem to be excluded from invalidating the pointer (according to http://en.cppreference.com/w/cpp/string/basic_string/c_str), which makes sense
src/node.cc
Outdated
@@ -3661,16 +3665,19 @@ static void ParseArgs(int* argc, | |||
if (debug_options.ParseOption(arg)) { | |||
// Done, consumed by DebugOptions::ParseOption(). | |||
} else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) { | |||
DisallowInNodeopts(argv[0], is_nodeopt, arg); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably worth listing all the options you've excluded in the first comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean in git commit body, list the options? I can do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I suspect a large amount of the discussion will be around which options we should enable under what conditions etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the list and reasons to the description of this item for visibility, and will add them back in later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when you say you will add them back in later. Do you mean add the reasons for the exclusion as comments in the code ? I think a comment in the code as well is good in terms of longer term history when people look and wonder why its not allowed. I do see you have the comments for some of them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added comments in source, will add to the commit message when the list stabilizes
Couple of discussion points on this:
|
|
For (1) .. good point :-) The only other one that I would definitely rule out, however, is the |
Right, forgot |
+1 ... yeah, |
On stripping of NODEOPT env var during spawn: I think it takes away from the usefullness of NODEOPT (specifically, it means we can't use However, I'd rather have at least a one-level NODEOPT with this confusing caveat than none at all. @jasnell, can you make a case for why you want this non-inheritable env var behaviour? And what specifically you propose? For example: require('child_process').exec('node -p process.env.NODE_PATH', function(err, out) {
console.log(err || out);
});
require('child_process').exec('node -p process.env.NODEOPT', function(err, out) {
console.log(err || out);
}); That
instead of
I would find confusing, is that what you propose? And what do you propose that the following code would print?
I would expect it to print the value of NODEOPT, do you propose it print something else? |
@sam-github ... the concern is largely about backwards compatibility and the possibility of For instance, imagine a child process that uses (Essentially, passing My proposal is that |
c0836e5
to
f7e2e40
Compare
But don't we already have that issue with existing environment variables, e.g. |
NODE_TTY_UNSAFE_ASYNC is also inherited, and modifies API contract in ways that would break child processes, there seems to be some precedence for these "use at own risk" env vars. If there is widespread concern, I'd rather disable V8 options that output to stdio then break the inheritance, it leaves the behaviour much more predictable. |
|
|
I would be much more comfortable if this was a whitelist instead of a blacklist. It would prevent accidentally harmful flags from being exposed. |
|
@jasnell , what about if we have NODEOPT and NODEOPT_INHERIT and let people choose the behavior they want. NODEOPT_INHERIT would be passed to child processes while NODEOPT would not. In that way you can use the environment to set options and chose if they are inherited or not. I think having the ability to pass on options to child processes is important and this would be one way to let the end user make a specific choice. |
@jasnell Before I implement this, lets be really clear:
You propose @bmeck, Do you mean for node options, or V8 options? I can switch it around to opt-in for node, I think there is a way I can do that, but there are 417 V8 options as of this moment on master, and they all look like the kinds of things someone might want to set via env to tune performance or diagnose mis-performance, and there don't appear to be anything we'd want to blacklist. |
ProcessArgv(&argc_from_env, argv_from_env, &exec_argc_, &exec_argv_, true); | ||
delete[] exec_argv_; | ||
delete[] argv_from_env; | ||
free(cstr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I'd rather that C++ std lib classes are used to avoid all the manual allocations. And you shouldn't have to worry about if the C++ std lib is available on a certain platform. And you don't need to rely on the non-reentrant legacy c strtok
.
std::vector<std::string> env_args;
std::string::size_type pos = 0, pos_next = 0;
while (pos_next < nodeopt.size())
{
pos_next = nodeopt.find(" ", pos);
if (pos_next == std::string::npos)
{
pos_next = nodeopt.size();
}
if (pos_next > pos)
{
env_args.push_back(nodeopt.substr(pos, pos_next - pos));
}
pos = pos_next + 1;
}
std::vector<const char *> argv_from_env;
argv_from_env.reserve(args.size() + 2);
argv_from_env.push_back(argv[0]);
for (size_t i = 0; i < args.size(); i++)
{
argv_from_env.push_back(env_args[i].c_str());
}
argv_from_env.push_back(nullptr);
and now you can call ProcessArgv
:
ProcessArgv(&argc_from_env, &argv_from_env[0], &exec_argc_, &exec_argv_, true );
@sam-github ... no, if In other words...
@mhdawson ... as long as there is an explicit opt-in for the options to be used in child processes I'm fine with however it happens. A second environment variable would not be my first choice tho but I could live with it. |
Note: that if we went with the |
src/node.cc
Outdated
break; | ||
char* cstr = strdup(nodeopt.c_str()); | ||
char* initptr = cstr; | ||
while (char* token = strtok(initptr, " ")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this handle multiple spaces? like "-a -b"
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes
@sam-github I am definitely thinking about more than just |
Another note: the documentation on this should need to be clear that: (a) command line arguments passed directly on the command line take precedence over those passed in To illustrate the kind of impact this has consider the following example:
To be certain, this is not a criticism, just something that should be noted. (This is also true when using the Note that the current CLI/env documentation (https://nodejs.org/dist/latest-v7.x/docs/api/cli.html) currently does not explain that arguments would take precedence over env vars. |
I agree with @addaleax that environment variables should be passed to child processes by default. |
Another test case that will need be looked at:
|
Not all CLI options are supported, those that are problematic from a security or implementation point of view are disallowed, as are ones that are inappropriate (for example, -e, -p, --i), or that only make sense when changed with code changes (such as options that change the javascript syntax or add new APIs). PR-URL: nodejs#12028 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com> Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
Not all CLI options are supported, those that are problematic from a security or implementation point of view are disallowed, as are ones that are inappropriate (for example, -e, -p, --i), or that only make sense when changed with code changes (such as options that change the javascript syntax or add new APIs). Backport-PR-URL: #12677 PR-URL: #12028 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com> Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
Not all CLI options are supported, those that are problematic from a security or implementation point of view are disallowed, as are ones that are inappropriate (for example, -e, -p, --i), or that only make sense when changed with code changes (such as options that change the javascript syntax or add new APIs). Backport-PR-URL: #12677 PR-URL: #12028 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com> Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
Notable Changes: * assert: - assert.fail() can now take one or two arguments (Rich Trott) #12293 * crypto: - add sign/verify support for RSASSA-PSS (Tobias Nießen) #11705 * deps: - upgrade openssl sources to 1.0.2m (Shigeki Ohtsu) #16691 - upgrade libuv to 1.15.0 (cjihrig) #15745 - upgrade libuv to 1.14.1 (cjihrig) #14866 - upgrade libuv to 1.13.1 (cjihrig) #14117 - upgrade libuv to 1.12.0 (cjihrig) #13306 * fs: - Add support for fs.write/fs.writeSync(fd, buffer, cb) and fs.write/fs.writeSync(fd, buffer, offset, cb) as documented (Andreas Lind) #7856 * inspector: - enable --inspect-brk (Refael Ackermann) #12615 * process: - add --redirect-warnings command line argument (James M Snell) #10116 * src: - allow CLI args in env with NODE_OPTIONS (Sam Roberts) #12028) - --abort-on-uncaught-exception in NODE_OPTIONS (Sam Roberts) #13932 - allow --tls-cipher-list in NODE_OPTIONS (Sam Roberts) #13172 - use SafeGetenv() for NODE_REDIRECT_WARNINGS (Sam Roberts) #12677 * test: - remove common.fail() (Rich Trott) #12293 PR-URL: #16263
Notable Changes: * assert: - assert.fail() can now take one or two arguments (Rich Trott) #12293 * crypto: - add sign/verify support for RSASSA-PSS (Tobias Nießen) #11705 * deps: - upgrade openssl sources to 1.0.2m (Shigeki Ohtsu) #16691 - upgrade libuv to 1.15.0 (cjihrig) #15745 - upgrade libuv to 1.14.1 (cjihrig) #14866 - upgrade libuv to 1.13.1 (cjihrig) #14117 - upgrade libuv to 1.12.0 (cjihrig) #13306 * fs: - Add support for fs.write/fs.writeSync(fd, buffer, cb) and fs.write/fs.writeSync(fd, buffer, offset, cb) as documented (Andreas Lind) #7856 * inspector: - enable --inspect-brk (Refael Ackermann) #12615 * process: - add --redirect-warnings command line argument (James M Snell) #10116 * src: - allow CLI args in env with NODE_OPTIONS (Sam Roberts) #12028) - --abort-on-uncaught-exception in NODE_OPTIONS (Sam Roberts) #13932 - allow --tls-cipher-list in NODE_OPTIONS (Sam Roberts) #13172 - use SafeGetenv() for NODE_REDIRECT_WARNINGS (Sam Roberts) #12677 * test: - remove common.fail() (Rich Trott) #12293 PR-URL: #16263
Not all CLI options are supported, those that are problematic from a
security or implementation point of view are disallowed, as are ones
that are inappropriate.
Disallowed because they don't make any sense to inject, they change node behaviour to fundamentally to be useful:
--version/-v
--help/-h
--eval/-e/--print/-pe/-p
--check/-c
--interactive/-i
--v8-options
(mapped to v8's--help
)--
: NODEOPT only supports options, so doesn't need a seperator between options and scriptscript.js
: the main script and its CLI options can only be specified on the actual command line--preserve-symlinks
Disallowed because of security concerns:
--tls-cipher-list
: This is the kind of thing I personally prefer to see configured by env var than by CLI, but @jasnell has concerns, and I've never seen the CLI option used, so I am prepared to wait until someone presents a use-case before allowing this in the future (if ever).Disallowed because of implementation difficulties:
--expose-internals
: Isn't actually implemented by the options parser, will need some code rearranged to become possible to specify in the envV8 options:
--help
(mentioned above)Tests are still missing, and I'm in the process of testing
-r
, the most important option, but it seems basically workable.Fix: #11997
Replace: #11888
Reference: #881
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
src