@@ -3548,6 +3548,9 @@ static void PrintHelp() {
35483548#endif
35493549#endif
35503550 " NODE_NO_WARNINGS set to 1 to silence process warnings\n "
3551+ #if !defined(NODE_WITHOUT_NODE_OPTIONS)
3552+ " NODE_OPTIONS set CLI options in the environment\n "
3553+ #endif
35513554#ifdef _WIN32
35523555 " NODE_PATH ';'-separated list of directories\n "
35533556#else
@@ -3562,6 +3565,51 @@ static void PrintHelp() {
35623565}
35633566
35643567
3568+ static void CheckIfAllowedInEnv (const char * exe, bool is_env,
3569+ const char * arg) {
3570+ if (!is_env)
3571+ return ;
3572+
3573+ // Find the arg prefix when its --some_arg=val
3574+ const char * eq = strchr (arg, ' =' );
3575+ size_t arglen = eq ? eq - arg : strlen (arg);
3576+
3577+ static const char * whitelist[] = {
3578+ // Node options
3579+ " -r" , " --require" ,
3580+ " --no-deprecation" ,
3581+ " --no-warnings" ,
3582+ " --trace-warnings" ,
3583+ " --redirect-warnings" ,
3584+ " --trace-deprecation" ,
3585+ " --trace-sync-io" ,
3586+ " --trace-events-enabled" ,
3587+ " --track-heap-objects" ,
3588+ " --throw-deprecation" ,
3589+ " --zero-fill-buffers" ,
3590+ " --v8-pool-size" ,
3591+ " --use-openssl-ca" ,
3592+ " --use-bundled-ca" ,
3593+ " --enable-fips" ,
3594+ " --force-fips" ,
3595+ " --openssl-config" ,
3596+ " --icu-data-dir" ,
3597+
3598+ // V8 options
3599+ " --max_old_space_size" ,
3600+ };
3601+
3602+ for (unsigned i = 0 ; i < arraysize (whitelist); i++) {
3603+ const char * allowed = whitelist[i];
3604+ if (strlen (allowed) == arglen && strncmp (allowed, arg, arglen) == 0 )
3605+ return ;
3606+ }
3607+
3608+ fprintf (stderr, " %s: %s is not allowed in NODE_OPTIONS\n " , exe, arg);
3609+ exit (9 );
3610+ }
3611+
3612+
35653613// Parse command line arguments.
35663614//
35673615// argv is modified in place. exec_argv and v8_argv are out arguments that
@@ -3578,7 +3626,8 @@ static void ParseArgs(int* argc,
35783626 int * exec_argc,
35793627 const char *** exec_argv,
35803628 int * v8_argc,
3581- const char *** v8_argv) {
3629+ const char *** v8_argv,
3630+ bool is_env) {
35823631 const unsigned int nargs = static_cast <unsigned int >(*argc);
35833632 const char ** new_exec_argv = new const char *[nargs];
35843633 const char ** new_v8_argv = new const char *[nargs];
@@ -3605,6 +3654,8 @@ static void ParseArgs(int* argc,
36053654 const char * const arg = argv[index];
36063655 unsigned int args_consumed = 1 ;
36073656
3657+ CheckIfAllowedInEnv (argv[0 ], is_env, arg);
3658+
36083659 if (debug_options.ParseOption (arg)) {
36093660 // Done, consumed by DebugOptions::ParseOption().
36103661 } else if (strcmp (arg, " --version" ) == 0 || strcmp (arg, " -v" ) == 0 ) {
@@ -3733,6 +3784,13 @@ static void ParseArgs(int* argc,
37333784
37343785 // Copy remaining arguments.
37353786 const unsigned int args_left = nargs - index;
3787+
3788+ if (is_env && args_left) {
3789+ fprintf (stderr, " %s: %s is not supported in NODE_OPTIONS\n " ,
3790+ argv[0 ], argv[index]);
3791+ exit (9 );
3792+ }
3793+
37363794 memcpy (new_argv + new_argc, argv + index, args_left * sizeof (*argv));
37373795 new_argc += args_left;
37383796
@@ -4166,6 +4224,54 @@ inline void PlatformInit() {
41664224}
41674225
41684226
4227+ void ProcessArgv (int * argc,
4228+ const char ** argv,
4229+ int * exec_argc,
4230+ const char *** exec_argv,
4231+ bool is_env = false ) {
4232+ // Parse a few arguments which are specific to Node.
4233+ int v8_argc;
4234+ const char ** v8_argv;
4235+ ParseArgs (argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv, is_env);
4236+
4237+ // TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler
4238+ // manually? That would give us a little more control over its runtime
4239+ // behavior but it could also interfere with the user's intentions in ways
4240+ // we fail to anticipate. Dillema.
4241+ for (int i = 1 ; i < v8_argc; ++i) {
4242+ if (strncmp (v8_argv[i], " --prof" , sizeof (" --prof" ) - 1 ) == 0 ) {
4243+ v8_is_profiling = true ;
4244+ break ;
4245+ }
4246+ }
4247+
4248+ #ifdef __POSIX__
4249+ // Block SIGPROF signals when sleeping in epoll_wait/kevent/etc. Avoids the
4250+ // performance penalty of frequent EINTR wakeups when the profiler is running.
4251+ // Only do this for v8.log profiling, as it breaks v8::CpuProfiler users.
4252+ if (v8_is_profiling) {
4253+ uv_loop_configure (uv_default_loop (), UV_LOOP_BLOCK_SIGNAL, SIGPROF);
4254+ }
4255+ #endif
4256+
4257+ // The const_cast doesn't violate conceptual const-ness. V8 doesn't modify
4258+ // the argv array or the elements it points to.
4259+ if (v8_argc > 1 )
4260+ V8::SetFlagsFromCommandLine (&v8_argc, const_cast <char **>(v8_argv), true );
4261+
4262+ // Anything that's still in v8_argv is not a V8 or a node option.
4263+ for (int i = 1 ; i < v8_argc; i++) {
4264+ fprintf (stderr, " %s: bad option: %s\n " , argv[0 ], v8_argv[i]);
4265+ }
4266+ delete[] v8_argv;
4267+ v8_argv = nullptr ;
4268+
4269+ if (v8_argc > 1 ) {
4270+ exit (9 );
4271+ }
4272+ }
4273+
4274+
41694275void Init (int * argc,
41704276 const char ** argv,
41714277 int * exec_argc,
@@ -4208,31 +4314,36 @@ void Init(int* argc,
42084314 if (openssl_config.empty ())
42094315 SafeGetenv (" OPENSSL_CONF" , &openssl_config);
42104316
4211- // Parse a few arguments which are specific to Node.
4212- int v8_argc;
4213- const char ** v8_argv;
4214- ParseArgs (argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv);
4215-
4216- // TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler
4217- // manually? That would give us a little more control over its runtime
4218- // behavior but it could also interfere with the user's intentions in ways
4219- // we fail to anticipate. Dillema.
4220- for (int i = 1 ; i < v8_argc; ++i) {
4221- if (strncmp (v8_argv[i], " --prof" , sizeof (" --prof" ) - 1 ) == 0 ) {
4222- v8_is_profiling = true ;
4223- break ;
4317+ #if !defined(NODE_WITHOUT_NODE_OPTIONS)
4318+ std::string node_options;
4319+ if (SafeGetenv (" NODE_OPTIONS" , &node_options)) {
4320+ // Smallest tokens are 2-chars (a not space and a space), plus 2 extra
4321+ // pointers, for the prepended executable name, and appended NULL pointer.
4322+ size_t max_len = 2 + (node_options.length () + 1 ) / 2 ;
4323+ const char ** argv_from_env = new const char *[max_len];
4324+ int argc_from_env = 0 ;
4325+ // [0] is expected to be the program name, fill it in from the real argv.
4326+ argv_from_env[argc_from_env++] = argv[0 ];
4327+
4328+ char * cstr = strdup (node_options.c_str ());
4329+ char * initptr = cstr;
4330+ char * token;
4331+ while ((token = strtok (initptr, " " ))) { // NOLINT(runtime/threadsafe_fn)
4332+ initptr = nullptr ;
4333+ argv_from_env[argc_from_env++] = token;
42244334 }
4225- }
4226-
4227- #ifdef __POSIX__
4228- // Block SIGPROF signals when sleeping in epoll_wait/kevent/etc. Avoids the
4229- // performance penalty of frequent EINTR wakeups when the profiler is running.
4230- // Only do this for v8.log profiling, as it breaks v8::CpuProfiler users.
4231- if (v8_is_profiling) {
4232- uv_loop_configure (uv_default_loop (), UV_LOOP_BLOCK_SIGNAL, SIGPROF);
4335+ argv_from_env[argc_from_env] = nullptr ;
4336+ int exec_argc_;
4337+ const char ** exec_argv_ = nullptr ;
4338+ ProcessArgv (&argc_from_env, argv_from_env, &exec_argc_, &exec_argv_, true );
4339+ delete[] exec_argv_;
4340+ delete[] argv_from_env;
4341+ free (cstr);
42334342 }
42344343#endif
42354344
4345+ ProcessArgv (argc, argv, exec_argc, exec_argv);
4346+
42364347#if defined(NODE_HAVE_I18N_SUPPORT)
42374348 // If the parameter isn't given, use the env variable.
42384349 if (icu_data_dir.empty ())
@@ -4244,21 +4355,6 @@ void Init(int* argc,
42444355 " (check NODE_ICU_DATA or --icu-data-dir parameters)" );
42454356 }
42464357#endif
4247- // The const_cast doesn't violate conceptual const-ness. V8 doesn't modify
4248- // the argv array or the elements it points to.
4249- if (v8_argc > 1 )
4250- V8::SetFlagsFromCommandLine (&v8_argc, const_cast <char **>(v8_argv), true );
4251-
4252- // Anything that's still in v8_argv is not a V8 or a node option.
4253- for (int i = 1 ; i < v8_argc; i++) {
4254- fprintf (stderr, " %s: bad option: %s\n " , argv[0 ], v8_argv[i]);
4255- }
4256- delete[] v8_argv;
4257- v8_argv = nullptr ;
4258-
4259- if (v8_argc > 1 ) {
4260- exit (9 );
4261- }
42624358
42634359 // Unconditionally force typed arrays to allocate outside the v8 heap. This
42644360 // is to prevent memory pointers from being moved around that are returned by
0 commit comments