@@ -3759,6 +3759,9 @@ static void PrintHelp() {
3759
3759
#endif
3760
3760
#endif
3761
3761
" NODE_NO_WARNINGS set to 1 to silence process warnings\n "
3762
+ #if !defined(NODE_WITHOUT_NODE_OPTIONS)
3763
+ " NODE_OPTIONS set CLI options in the environment\n "
3764
+ #endif
3762
3765
#ifdef _WIN32
3763
3766
" NODE_PATH ';'-separated list of directories\n "
3764
3767
#else
@@ -3773,6 +3776,50 @@ static void PrintHelp() {
3773
3776
}
3774
3777
3775
3778
3779
+ static void CheckIfAllowedInEnv (const char * exe, bool is_env,
3780
+ const char * arg) {
3781
+ if (!is_env)
3782
+ return ;
3783
+
3784
+ // Find the arg prefix when its --some_arg=val
3785
+ const char * eq = strchr (arg, ' =' );
3786
+ size_t arglen = eq ? eq - arg : strlen (arg);
3787
+
3788
+ static const char * whitelist[] = {
3789
+ // Node options
3790
+ " -r" , " --require" ,
3791
+ " --no-deprecation" ,
3792
+ " --no-warnings" ,
3793
+ " --trace-warnings" ,
3794
+ " --redirect-warnings" ,
3795
+ " --trace-deprecation" ,
3796
+ " --trace-sync-io" ,
3797
+ " --track-heap-objects" ,
3798
+ " --throw-deprecation" ,
3799
+ " --zero-fill-buffers" ,
3800
+ " --v8-pool-size" ,
3801
+ " --use-openssl-ca" ,
3802
+ " --use-bundled-ca" ,
3803
+ " --enable-fips" ,
3804
+ " --force-fips" ,
3805
+ " --openssl-config" ,
3806
+ " --icu-data-dir" ,
3807
+
3808
+ // V8 options
3809
+ " --max_old_space_size" ,
3810
+ };
3811
+
3812
+ for (unsigned i = 0 ; i < arraysize (whitelist); i++) {
3813
+ const char * allowed = whitelist[i];
3814
+ if (strlen (allowed) == arglen && strncmp (allowed, arg, arglen) == 0 )
3815
+ return ;
3816
+ }
3817
+
3818
+ fprintf (stderr, " %s: %s is not allowed in NODE_OPTIONS\n " , exe, arg);
3819
+ exit (9 );
3820
+ }
3821
+
3822
+
3776
3823
// Parse command line arguments.
3777
3824
//
3778
3825
// argv is modified in place. exec_argv and v8_argv are out arguments that
@@ -3789,7 +3836,8 @@ static void ParseArgs(int* argc,
3789
3836
int * exec_argc,
3790
3837
const char *** exec_argv,
3791
3838
int * v8_argc,
3792
- const char *** v8_argv) {
3839
+ const char *** v8_argv,
3840
+ bool is_env) {
3793
3841
const unsigned int nargs = static_cast <unsigned int >(*argc);
3794
3842
const char ** new_exec_argv = new const char *[nargs];
3795
3843
const char ** new_v8_argv = new const char *[nargs];
@@ -3814,6 +3862,8 @@ static void ParseArgs(int* argc,
3814
3862
const char * const arg = argv[index ];
3815
3863
unsigned int args_consumed = 1 ;
3816
3864
3865
+ CheckIfAllowedInEnv (argv[0 ], is_env, arg);
3866
+
3817
3867
if (ParseDebugOpt (arg)) {
3818
3868
// Done, consumed by ParseDebugOpt().
3819
3869
} else if (strcmp (arg, " --version" ) == 0 || strcmp (arg, " -v" ) == 0 ) {
@@ -3934,6 +3984,13 @@ static void ParseArgs(int* argc,
3934
3984
3935
3985
// Copy remaining arguments.
3936
3986
const unsigned int args_left = nargs - index ;
3987
+
3988
+ if (is_env && args_left) {
3989
+ fprintf (stderr, " %s: %s is not supported in NODE_OPTIONS\n " ,
3990
+ argv[0 ], argv[index ]);
3991
+ exit (9 );
3992
+ }
3993
+
3937
3994
memcpy (new_argv + new_argc, argv + index , args_left * sizeof (*argv));
3938
3995
new_argc += args_left;
3939
3996
@@ -4367,6 +4424,54 @@ inline void PlatformInit() {
4367
4424
}
4368
4425
4369
4426
4427
+ void ProcessArgv (int * argc,
4428
+ const char ** argv,
4429
+ int * exec_argc,
4430
+ const char *** exec_argv,
4431
+ bool is_env = false ) {
4432
+ // Parse a few arguments which are specific to Node.
4433
+ int v8_argc;
4434
+ const char ** v8_argv;
4435
+ ParseArgs (argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv, is_env);
4436
+
4437
+ // TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler
4438
+ // manually? That would give us a little more control over its runtime
4439
+ // behavior but it could also interfere with the user's intentions in ways
4440
+ // we fail to anticipate. Dillema.
4441
+ for (int i = 1 ; i < v8_argc; ++i) {
4442
+ if (strncmp (v8_argv[i], " --prof" , sizeof (" --prof" ) - 1 ) == 0 ) {
4443
+ v8_is_profiling = true ;
4444
+ break ;
4445
+ }
4446
+ }
4447
+
4448
+ #ifdef __POSIX__
4449
+ // Block SIGPROF signals when sleeping in epoll_wait/kevent/etc. Avoids the
4450
+ // performance penalty of frequent EINTR wakeups when the profiler is running.
4451
+ // Only do this for v8.log profiling, as it breaks v8::CpuProfiler users.
4452
+ if (v8_is_profiling) {
4453
+ uv_loop_configure (uv_default_loop (), UV_LOOP_BLOCK_SIGNAL, SIGPROF);
4454
+ }
4455
+ #endif
4456
+
4457
+ // The const_cast doesn't violate conceptual const-ness. V8 doesn't modify
4458
+ // the argv array or the elements it points to.
4459
+ if (v8_argc > 1 )
4460
+ V8::SetFlagsFromCommandLine (&v8_argc, const_cast <char **>(v8_argv), true );
4461
+
4462
+ // Anything that's still in v8_argv is not a V8 or a node option.
4463
+ for (int i = 1 ; i < v8_argc; i++) {
4464
+ fprintf (stderr, " %s: bad option: %s\n " , argv[0 ], v8_argv[i]);
4465
+ }
4466
+ delete[] v8_argv;
4467
+ v8_argv = nullptr ;
4468
+
4469
+ if (v8_argc > 1 ) {
4470
+ exit (9 );
4471
+ }
4472
+ }
4473
+
4474
+
4370
4475
void Init (int * argc,
4371
4476
const char ** argv,
4372
4477
int * exec_argc,
@@ -4397,31 +4502,36 @@ void Init(int* argc,
4397
4502
if (config_warning_file.empty ())
4398
4503
SafeGetenv (" NODE_REDIRECT_WARNINGS" , &config_warning_file);
4399
4504
4400
- // Parse a few arguments which are specific to Node.
4401
- int v8_argc;
4402
- const char ** v8_argv;
4403
- ParseArgs (argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv);
4404
-
4405
- // TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler
4406
- // manually? That would give us a little more control over its runtime
4407
- // behavior but it could also interfere with the user's intentions in ways
4408
- // we fail to anticipate. Dillema.
4409
- for (int i = 1 ; i < v8_argc; ++i) {
4410
- if (strncmp (v8_argv[i], " --prof" , sizeof (" --prof" ) - 1 ) == 0 ) {
4411
- v8_is_profiling = true ;
4412
- break ;
4505
+ #if !defined(NODE_WITHOUT_NODE_OPTIONS)
4506
+ std::string node_options;
4507
+ if (SafeGetenv (" NODE_OPTIONS" , &node_options)) {
4508
+ // Smallest tokens are 2-chars (a not space and a space), plus 2 extra
4509
+ // pointers, for the prepended executable name, and appended NULL pointer.
4510
+ size_t max_len = 2 + (node_options.length () + 1 ) / 2 ;
4511
+ const char ** argv_from_env = new const char *[max_len];
4512
+ int argc_from_env = 0 ;
4513
+ // [0] is expected to be the program name, fill it in from the real argv.
4514
+ argv_from_env[argc_from_env++] = argv[0 ];
4515
+
4516
+ char * cstr = strdup (node_options.c_str ());
4517
+ char * initptr = cstr;
4518
+ char * token;
4519
+ while ((token = strtok (initptr, " " ))) { // NOLINT(runtime/threadsafe_fn)
4520
+ initptr = nullptr ;
4521
+ argv_from_env[argc_from_env++] = token;
4413
4522
}
4414
- }
4415
-
4416
- #ifdef __POSIX__
4417
- // Block SIGPROF signals when sleeping in epoll_wait/kevent/etc. Avoids the
4418
- // performance penalty of frequent EINTR wakeups when the profiler is running.
4419
- // Only do this for v8.log profiling, as it breaks v8::CpuProfiler users.
4420
- if (v8_is_profiling) {
4421
- uv_loop_configure (uv_default_loop (), UV_LOOP_BLOCK_SIGNAL, SIGPROF);
4523
+ argv_from_env[argc_from_env] = nullptr ;
4524
+ int exec_argc_;
4525
+ const char ** exec_argv_ = nullptr ;
4526
+ ProcessArgv (&argc_from_env, argv_from_env, &exec_argc_, &exec_argv_, true );
4527
+ delete[] exec_argv_;
4528
+ delete[] argv_from_env;
4529
+ free (cstr);
4422
4530
}
4423
4531
#endif
4424
4532
4533
+ ProcessArgv (argc, argv, exec_argc, exec_argv);
4534
+
4425
4535
#if defined(NODE_HAVE_I18N_SUPPORT)
4426
4536
// If the parameter isn't given, use the env variable.
4427
4537
if (icu_data_dir.empty ())
@@ -4433,21 +4543,6 @@ void Init(int* argc,
4433
4543
" (check NODE_ICU_DATA or --icu-data-dir parameters)\n " );
4434
4544
}
4435
4545
#endif
4436
- // The const_cast doesn't violate conceptual const-ness. V8 doesn't modify
4437
- // the argv array or the elements it points to.
4438
- if (v8_argc > 1 )
4439
- V8::SetFlagsFromCommandLine (&v8_argc, const_cast <char **>(v8_argv), true );
4440
-
4441
- // Anything that's still in v8_argv is not a V8 or a node option.
4442
- for (int i = 1 ; i < v8_argc; i++) {
4443
- fprintf (stderr, " %s: bad option: %s\n " , argv[0 ], v8_argv[i]);
4444
- }
4445
- delete[] v8_argv;
4446
- v8_argv = nullptr ;
4447
-
4448
- if (v8_argc > 1 ) {
4449
- exit (9 );
4450
- }
4451
4546
4452
4547
// Unconditionally force typed arrays to allocate outside the v8 heap. This
4453
4548
// is to prevent memory pointers from being moved around that are returned by
0 commit comments