diff options
author | Sam Roberts <vieuxtech@gmail.com> | 2017-02-20 06:18:43 -0800 |
---|---|---|
committer | Myles Borins <myles.borins@gmail.com> | 2017-10-25 04:09:42 -0400 |
commit | dd6ea892172cf78eb65024851662ba787f3afc72 (patch) | |
tree | 18576573b00a98cee3f1ebf9da542dbced798b0e /src/node.cc | |
parent | 8f4214836e6201dcc19aab1741572c72844b36a8 (diff) | |
download | node-new-dd6ea892172cf78eb65024851662ba787f3afc72.tar.gz |
src: allow CLI args in env with NODE_OPTIONS
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: https://github.com/nodejs/node/pull/12677
PR-URL: https://github.com/nodejs/node/pull/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>
Diffstat (limited to 'src/node.cc')
-rw-r--r-- | src/node.cc | 169 |
1 files changed, 132 insertions, 37 deletions
diff --git a/src/node.cc b/src/node.cc index 3214d58f48..c4f2a96c95 100644 --- a/src/node.cc +++ b/src/node.cc @@ -3759,6 +3759,9 @@ static void PrintHelp() { #endif #endif "NODE_NO_WARNINGS set to 1 to silence process warnings\n" +#if !defined(NODE_WITHOUT_NODE_OPTIONS) + "NODE_OPTIONS set CLI options in the environment\n" +#endif #ifdef _WIN32 "NODE_PATH ';'-separated list of directories\n" #else @@ -3773,6 +3776,50 @@ static void PrintHelp() { } +static void CheckIfAllowedInEnv(const char* exe, bool is_env, + const char* arg) { + if (!is_env) + return; + + // Find the arg prefix when its --some_arg=val + const char* eq = strchr(arg, '='); + size_t arglen = eq ? eq - arg : strlen(arg); + + static const char* whitelist[] = { + // Node options + "-r", "--require", + "--no-deprecation", + "--no-warnings", + "--trace-warnings", + "--redirect-warnings", + "--trace-deprecation", + "--trace-sync-io", + "--track-heap-objects", + "--throw-deprecation", + "--zero-fill-buffers", + "--v8-pool-size", + "--use-openssl-ca", + "--use-bundled-ca", + "--enable-fips", + "--force-fips", + "--openssl-config", + "--icu-data-dir", + + // V8 options + "--max_old_space_size", + }; + + for (unsigned i = 0; i < arraysize(whitelist); i++) { + const char* allowed = whitelist[i]; + if (strlen(allowed) == arglen && strncmp(allowed, arg, arglen) == 0) + return; + } + + fprintf(stderr, "%s: %s is not allowed in NODE_OPTIONS\n", exe, arg); + exit(9); +} + + // Parse command line arguments. // // argv is modified in place. exec_argv and v8_argv are out arguments that @@ -3789,7 +3836,8 @@ static void ParseArgs(int* argc, int* exec_argc, const char*** exec_argv, int* v8_argc, - const char*** v8_argv) { + const char*** v8_argv, + bool is_env) { const unsigned int nargs = static_cast<unsigned int>(*argc); const char** new_exec_argv = new const char*[nargs]; const char** new_v8_argv = new const char*[nargs]; @@ -3814,6 +3862,8 @@ static void ParseArgs(int* argc, const char* const arg = argv[index]; unsigned int args_consumed = 1; + CheckIfAllowedInEnv(argv[0], is_env, arg); + if (ParseDebugOpt(arg)) { // Done, consumed by ParseDebugOpt(). } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) { @@ -3934,6 +3984,13 @@ static void ParseArgs(int* argc, // Copy remaining arguments. const unsigned int args_left = nargs - index; + + if (is_env && args_left) { + fprintf(stderr, "%s: %s is not supported in NODE_OPTIONS\n", + argv[0], argv[index]); + exit(9); + } + memcpy(new_argv + new_argc, argv + index, args_left * sizeof(*argv)); new_argc += args_left; @@ -4367,6 +4424,54 @@ inline void PlatformInit() { } +void ProcessArgv(int* argc, + const char** argv, + int* exec_argc, + const char*** exec_argv, + bool is_env = false) { + // Parse a few arguments which are specific to Node. + int v8_argc; + const char** v8_argv; + ParseArgs(argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv, is_env); + + // TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler + // manually? That would give us a little more control over its runtime + // behavior but it could also interfere with the user's intentions in ways + // we fail to anticipate. Dillema. + for (int i = 1; i < v8_argc; ++i) { + if (strncmp(v8_argv[i], "--prof", sizeof("--prof") - 1) == 0) { + v8_is_profiling = true; + break; + } + } + +#ifdef __POSIX__ + // Block SIGPROF signals when sleeping in epoll_wait/kevent/etc. Avoids the + // performance penalty of frequent EINTR wakeups when the profiler is running. + // Only do this for v8.log profiling, as it breaks v8::CpuProfiler users. + if (v8_is_profiling) { + uv_loop_configure(uv_default_loop(), UV_LOOP_BLOCK_SIGNAL, SIGPROF); + } +#endif + + // The const_cast doesn't violate conceptual const-ness. V8 doesn't modify + // the argv array or the elements it points to. + if (v8_argc > 1) + V8::SetFlagsFromCommandLine(&v8_argc, const_cast<char**>(v8_argv), true); + + // Anything that's still in v8_argv is not a V8 or a node option. + for (int i = 1; i < v8_argc; i++) { + fprintf(stderr, "%s: bad option: %s\n", argv[0], v8_argv[i]); + } + delete[] v8_argv; + v8_argv = nullptr; + + if (v8_argc > 1) { + exit(9); + } +} + + void Init(int* argc, const char** argv, int* exec_argc, @@ -4397,31 +4502,36 @@ void Init(int* argc, if (config_warning_file.empty()) SafeGetenv("NODE_REDIRECT_WARNINGS", &config_warning_file); - // Parse a few arguments which are specific to Node. - int v8_argc; - const char** v8_argv; - ParseArgs(argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv); - - // TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler - // manually? That would give us a little more control over its runtime - // behavior but it could also interfere with the user's intentions in ways - // we fail to anticipate. Dillema. - for (int i = 1; i < v8_argc; ++i) { - if (strncmp(v8_argv[i], "--prof", sizeof("--prof") - 1) == 0) { - v8_is_profiling = true; - break; +#if !defined(NODE_WITHOUT_NODE_OPTIONS) + std::string node_options; + if (SafeGetenv("NODE_OPTIONS", &node_options)) { + // Smallest tokens are 2-chars (a not space and a space), plus 2 extra + // pointers, for the prepended executable name, and appended NULL pointer. + size_t max_len = 2 + (node_options.length() + 1) / 2; + const char** argv_from_env = new const char*[max_len]; + int argc_from_env = 0; + // [0] is expected to be the program name, fill it in from the real argv. + argv_from_env[argc_from_env++] = argv[0]; + + char* cstr = strdup(node_options.c_str()); + char* initptr = cstr; + char* token; + while ((token = strtok(initptr, " "))) { // NOLINT(runtime/threadsafe_fn) + initptr = nullptr; + argv_from_env[argc_from_env++] = token; } - } - -#ifdef __POSIX__ - // Block SIGPROF signals when sleeping in epoll_wait/kevent/etc. Avoids the - // performance penalty of frequent EINTR wakeups when the profiler is running. - // Only do this for v8.log profiling, as it breaks v8::CpuProfiler users. - if (v8_is_profiling) { - uv_loop_configure(uv_default_loop(), UV_LOOP_BLOCK_SIGNAL, SIGPROF); + argv_from_env[argc_from_env] = nullptr; + int exec_argc_; + const char** exec_argv_ = nullptr; + ProcessArgv(&argc_from_env, argv_from_env, &exec_argc_, &exec_argv_, true); + delete[] exec_argv_; + delete[] argv_from_env; + free(cstr); } #endif + ProcessArgv(argc, argv, exec_argc, exec_argv); + #if defined(NODE_HAVE_I18N_SUPPORT) // If the parameter isn't given, use the env variable. if (icu_data_dir.empty()) @@ -4433,21 +4543,6 @@ void Init(int* argc, "(check NODE_ICU_DATA or --icu-data-dir parameters)\n"); } #endif - // The const_cast doesn't violate conceptual const-ness. V8 doesn't modify - // the argv array or the elements it points to. - if (v8_argc > 1) - V8::SetFlagsFromCommandLine(&v8_argc, const_cast<char**>(v8_argv), true); - - // Anything that's still in v8_argv is not a V8 or a node option. - for (int i = 1; i < v8_argc; i++) { - fprintf(stderr, "%s: bad option: %s\n", argv[0], v8_argv[i]); - } - delete[] v8_argv; - v8_argv = nullptr; - - if (v8_argc > 1) { - exit(9); - } // Unconditionally force typed arrays to allocate outside the v8 heap. This // is to prevent memory pointers from being moved around that are returned by |