@@ -3626,6 +3626,9 @@ static void PrintHelp() {
36263626#endif
36273627#endif
36283628 " NODE_NO_WARNINGS set to 1 to silence process warnings\n "
3629+ #if !defined(NODE_WITHOUT_NODE_OPTIONS)
3630+ " NODE_OPTIONS set CLI options in the environment\n "
3631+ #endif
36293632#ifdef _WIN32
36303633 " NODE_PATH ';'-separated list of directories\n "
36313634#else
@@ -3642,6 +3645,51 @@ static void PrintHelp() {
36423645}
36433646
36443647
3648+ static void CheckIfAllowedInEnv (const char * exe, bool is_env,
3649+ const char * arg) {
3650+ if (!is_env)
3651+ return ;
3652+
3653+ // Find the arg prefix when its --some_arg=val
3654+ const char * eq = strchr (arg, ' =' );
3655+ size_t arglen = eq ? eq - arg : strlen (arg);
3656+
3657+ static const char * whitelist[] = {
3658+ // Node options
3659+ " -r" , " --require" ,
3660+ " --no-deprecation" ,
3661+ " --no-warnings" ,
3662+ " --trace-warnings" ,
3663+ " --redirect-warnings" ,
3664+ " --trace-deprecation" ,
3665+ " --trace-sync-io" ,
3666+ " --trace-events-enabled" ,
3667+ " --track-heap-objects" ,
3668+ " --throw-deprecation" ,
3669+ " --zero-fill-buffers" ,
3670+ " --v8-pool-size" ,
3671+ " --use-openssl-ca" ,
3672+ " --use-bundled-ca" ,
3673+ " --enable-fips" ,
3674+ " --force-fips" ,
3675+ " --openssl-config" ,
3676+ " --icu-data-dir" ,
3677+
3678+ // V8 options
3679+ " --max_old_space_size" ,
3680+ };
3681+
3682+ for (unsigned i = 0 ; i < arraysize (whitelist); i++) {
3683+ const char * allowed = whitelist[i];
3684+ if (strlen (allowed) == arglen && strncmp (allowed, arg, arglen) == 0 )
3685+ return ;
3686+ }
3687+
3688+ fprintf (stderr, " %s: %s is not allowed in NODE_OPTIONS\n " , exe, arg);
3689+ exit (9 );
3690+ }
3691+
3692+
36453693// Parse command line arguments.
36463694//
36473695// argv is modified in place. exec_argv and v8_argv are out arguments that
@@ -3658,7 +3706,8 @@ static void ParseArgs(int* argc,
36583706 int * exec_argc,
36593707 const char *** exec_argv,
36603708 int * v8_argc,
3661- const char *** v8_argv) {
3709+ const char *** v8_argv,
3710+ bool is_env) {
36623711 const unsigned int nargs = static_cast <unsigned int >(*argc);
36633712 const char ** new_exec_argv = new const char *[nargs];
36643713 const char ** new_v8_argv = new const char *[nargs];
@@ -3687,6 +3736,8 @@ static void ParseArgs(int* argc,
36873736 const char * const arg = argv[index];
36883737 unsigned int args_consumed = 1 ;
36893738
3739+ CheckIfAllowedInEnv (argv[0 ], is_env, arg);
3740+
36903741 if (debug_options.ParseOption (arg)) {
36913742 // Done, consumed by DebugOptions::ParseOption().
36923743 } else if (strcmp (arg, " --version" ) == 0 || strcmp (arg, " -v" ) == 0 ) {
@@ -3839,6 +3890,13 @@ static void ParseArgs(int* argc,
38393890
38403891 // Copy remaining arguments.
38413892 const unsigned int args_left = nargs - index;
3893+
3894+ if (is_env && args_left) {
3895+ fprintf (stderr, " %s: %s is not supported in NODE_OPTIONS\n " ,
3896+ argv[0 ], argv[index]);
3897+ exit (9 );
3898+ }
3899+
38423900 memcpy (new_argv + new_argc, argv + index, args_left * sizeof (*argv));
38433901 new_argc += args_left;
38443902
@@ -4161,6 +4219,54 @@ inline void PlatformInit() {
41614219}
41624220
41634221
4222+ void ProcessArgv (int * argc,
4223+ const char ** argv,
4224+ int * exec_argc,
4225+ const char *** exec_argv,
4226+ bool is_env = false ) {
4227+ // Parse a few arguments which are specific to Node.
4228+ int v8_argc;
4229+ const char ** v8_argv;
4230+ ParseArgs (argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv, is_env);
4231+
4232+ // TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler
4233+ // manually? That would give us a little more control over its runtime
4234+ // behavior but it could also interfere with the user's intentions in ways
4235+ // we fail to anticipate. Dillema.
4236+ for (int i = 1 ; i < v8_argc; ++i) {
4237+ if (strncmp (v8_argv[i], " --prof" , sizeof (" --prof" ) - 1 ) == 0 ) {
4238+ v8_is_profiling = true ;
4239+ break ;
4240+ }
4241+ }
4242+
4243+ #ifdef __POSIX__
4244+ // Block SIGPROF signals when sleeping in epoll_wait/kevent/etc. Avoids the
4245+ // performance penalty of frequent EINTR wakeups when the profiler is running.
4246+ // Only do this for v8.log profiling, as it breaks v8::CpuProfiler users.
4247+ if (v8_is_profiling) {
4248+ uv_loop_configure (uv_default_loop (), UV_LOOP_BLOCK_SIGNAL, SIGPROF);
4249+ }
4250+ #endif
4251+
4252+ // The const_cast doesn't violate conceptual const-ness. V8 doesn't modify
4253+ // the argv array or the elements it points to.
4254+ if (v8_argc > 1 )
4255+ V8::SetFlagsFromCommandLine (&v8_argc, const_cast <char **>(v8_argv), true );
4256+
4257+ // Anything that's still in v8_argv is not a V8 or a node option.
4258+ for (int i = 1 ; i < v8_argc; i++) {
4259+ fprintf (stderr, " %s: bad option: %s\n " , argv[0 ], v8_argv[i]);
4260+ }
4261+ delete[] v8_argv;
4262+ v8_argv = nullptr ;
4263+
4264+ if (v8_argc > 1 ) {
4265+ exit (9 );
4266+ }
4267+ }
4268+
4269+
41644270void Init (int * argc,
41654271 const char ** argv,
41664272 int * exec_argc,
@@ -4214,31 +4320,36 @@ void Init(int* argc,
42144320 SafeGetenv (" OPENSSL_CONF" , &openssl_config);
42154321#endif
42164322
4217- // Parse a few arguments which are specific to Node.
4218- int v8_argc;
4219- const char ** v8_argv;
4220- ParseArgs (argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv);
4221-
4222- // TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler
4223- // manually? That would give us a little more control over its runtime
4224- // behavior but it could also interfere with the user's intentions in ways
4225- // we fail to anticipate. Dillema.
4226- for (int i = 1 ; i < v8_argc; ++i) {
4227- if (strncmp (v8_argv[i], " --prof" , sizeof (" --prof" ) - 1 ) == 0 ) {
4228- v8_is_profiling = true ;
4229- break ;
4323+ #if !defined(NODE_WITHOUT_NODE_OPTIONS)
4324+ std::string node_options;
4325+ if (SafeGetenv (" NODE_OPTIONS" , &node_options)) {
4326+ // Smallest tokens are 2-chars (a not space and a space), plus 2 extra
4327+ // pointers, for the prepended executable name, and appended NULL pointer.
4328+ size_t max_len = 2 + (node_options.length () + 1 ) / 2 ;
4329+ const char ** argv_from_env = new const char *[max_len];
4330+ int argc_from_env = 0 ;
4331+ // [0] is expected to be the program name, fill it in from the real argv.
4332+ argv_from_env[argc_from_env++] = argv[0 ];
4333+
4334+ char * cstr = strdup (node_options.c_str ());
4335+ char * initptr = cstr;
4336+ char * token;
4337+ while ((token = strtok (initptr, " " ))) { // NOLINT(runtime/threadsafe_fn)
4338+ initptr = nullptr ;
4339+ argv_from_env[argc_from_env++] = token;
42304340 }
4231- }
4232-
4233- #ifdef __POSIX__
4234- // Block SIGPROF signals when sleeping in epoll_wait/kevent/etc. Avoids the
4235- // performance penalty of frequent EINTR wakeups when the profiler is running.
4236- // Only do this for v8.log profiling, as it breaks v8::CpuProfiler users.
4237- if (v8_is_profiling) {
4238- uv_loop_configure (uv_default_loop (), UV_LOOP_BLOCK_SIGNAL, SIGPROF);
4341+ argv_from_env[argc_from_env] = nullptr ;
4342+ int exec_argc_;
4343+ const char ** exec_argv_ = nullptr ;
4344+ ProcessArgv (&argc_from_env, argv_from_env, &exec_argc_, &exec_argv_, true );
4345+ delete[] exec_argv_;
4346+ delete[] argv_from_env;
4347+ free (cstr);
42394348 }
42404349#endif
42414350
4351+ ProcessArgv (argc, argv, exec_argc, exec_argv);
4352+
42424353#if defined(NODE_HAVE_I18N_SUPPORT)
42434354 // If the parameter isn't given, use the env variable.
42444355 if (icu_data_dir.empty ())
@@ -4250,21 +4361,6 @@ void Init(int* argc,
42504361 " (check NODE_ICU_DATA or --icu-data-dir parameters)" );
42514362 }
42524363#endif
4253- // The const_cast doesn't violate conceptual const-ness. V8 doesn't modify
4254- // the argv array or the elements it points to.
4255- if (v8_argc > 1 )
4256- V8::SetFlagsFromCommandLine (&v8_argc, const_cast <char **>(v8_argv), true );
4257-
4258- // Anything that's still in v8_argv is not a V8 or a node option.
4259- for (int i = 1 ; i < v8_argc; i++) {
4260- fprintf (stderr, " %s: bad option: %s\n " , argv[0 ], v8_argv[i]);
4261- }
4262- delete[] v8_argv;
4263- v8_argv = nullptr ;
4264-
4265- if (v8_argc > 1 ) {
4266- exit (9 );
4267- }
42684364
42694365 // Unconditionally force typed arrays to allocate outside the v8 heap. This
42704366 // is to prevent memory pointers from being moved around that are returned by
0 commit comments