GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — src: allow CLI args in env with NODE_OPTIONS by sam-github · Pull Request #12028 · nodejs/node Conversation
|
| 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.
The reason will be displayed to describe this comment to others. Learn more.
@bnoordhuis ----^
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
why not use std::string::find and substr?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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…)
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| const char** argv, | ||
| int* exec_argc, | ||
| const char*** exec_argv, | ||
| bool is_nodeopt = false) { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Can you align the arguments vertically?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| 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.
The reason will be displayed to describe this comment to others. Learn more.
Why would you not be able to use strtok_r?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| 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.
The reason will be displayed to describe this comment to others. Learn more.
Why the copy? nodeopt will have its own memory anyway
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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).
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| 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.
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.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Couple of discussion points on this:
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
For (1) .. good point :-) The only other one that I would definitely rule out, however, is the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Right, forgot |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
+1 ... yeah, |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
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? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@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 |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
c0836e5 to
f7e2e40
Compare
|
But don't we already have that issue with existing environment variables, e.g. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
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. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I would be much more comfortable if this was a whitelist instead of a blacklist. It would prevent accidentally harmful flags from being exposed. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@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. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@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. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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 );
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
@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. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Note: that if we went with the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
does this handle multiple spaces? like "-a -b".
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
yes
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@jasnell @mhdawson If the main risk you have in mind is security, it won’t matter whether you pass one or two environment variables. (But as I’ve said before, using environment variables that are not inherited by child processes seems pointless by definition.) |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@sam-github I am definitely thinking about more than just |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
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. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I agree with @addaleax that environment variables should be passed to child processes by default. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Another test case that will need be looked at: |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
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 <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Gibson Fahnestock <[email protected]> Reviewed-By: Bradley Farias <[email protected]>
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 <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Gibson Fahnestock <[email protected]> Reviewed-By: Bradley Farias <[email protected]>
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 <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Gibson Fahnestock <[email protected]> Reviewed-By: Bradley Farias <[email protected]>
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 <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Gibson Fahnestock <[email protected]> Reviewed-By: Bradley Farias <[email protected]>
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
Proposal: --config=file command line argument
There was an error while loading. Please reload this page.