1 #include "node_options.h" // NOLINT(build/include_inline)
2 #include "node_options-inl.h"
3
4 #include "env-inl.h"
5 #include "node_binding.h"
6 #include "node_internals.h"
7
8 #include <errno.h>
9 #include <sstream>
10 #include <cstdlib> // strtoul, errno
11
12 using v8::Boolean;
13 using v8::Context;
14 using v8::FunctionCallbackInfo;
15 using v8::Integer;
16 using v8::Isolate;
17 using v8::Local;
18 using v8::Map;
19 using v8::Number;
20 using v8::Object;
21 using v8::Undefined;
22 using v8::Value;
23
24 namespace node {
25
26 namespace per_process {
27 Mutex cli_options_mutex;
28 std::shared_ptr<PerProcessOptions> cli_options{new PerProcessOptions()};
29 } // namespace per_process
30
CheckOptions(std::vector<std::string> * errors)31 void DebugOptions::CheckOptions(std::vector<std::string>* errors) {
32 #if !NODE_USE_V8_PLATFORM && !HAVE_INSPECTOR
33 if (inspector_enabled) {
34 errors->push_back("Inspector is not available when Node is compiled "
35 "--without-v8-platform and --without-inspector.");
36 }
37 #endif
38
39 if (deprecated_debug) {
40 errors->push_back("[DEP0062]: `node --debug` and `node --debug-brk` "
41 "are invalid. Please use `node --inspect` and "
42 "`node --inspect-brk` instead.");
43 }
44
45 std::vector<std::string> destinations =
46 SplitString(inspect_publish_uid_string, ',');
47 inspect_publish_uid.console = false;
48 inspect_publish_uid.http = false;
49 for (const std::string& destination : destinations) {
50 if (destination == "stderr") {
51 inspect_publish_uid.console = true;
52 } else if (destination == "http") {
53 inspect_publish_uid.http = true;
54 } else {
55 errors->push_back("--inspect-publish-uid destination can be "
56 "stderr or http");
57 }
58 }
59 }
60
CheckOptions(std::vector<std::string> * errors)61 void PerProcessOptions::CheckOptions(std::vector<std::string>* errors) {
62 #if HAVE_OPENSSL
63 if (use_openssl_ca && use_bundled_ca) {
64 errors->push_back("either --use-openssl-ca or --use-bundled-ca can be "
65 "used, not both");
66 }
67 #endif
68 if (use_largepages != "off" &&
69 use_largepages != "on" &&
70 use_largepages != "silent") {
71 errors->push_back("invalid value for --use-largepages");
72 }
73 per_isolate->CheckOptions(errors);
74 }
75
CheckOptions(std::vector<std::string> * errors)76 void PerIsolateOptions::CheckOptions(std::vector<std::string>* errors) {
77 per_env->CheckOptions(errors);
78 }
79
CheckOptions(std::vector<std::string> * errors)80 void EnvironmentOptions::CheckOptions(std::vector<std::string>* errors) {
81 if (has_policy_integrity_string && experimental_policy.empty()) {
82 errors->push_back("--policy-integrity requires "
83 "--experimental-policy be enabled");
84 }
85 if (has_policy_integrity_string && experimental_policy_integrity.empty()) {
86 errors->push_back("--policy-integrity cannot be empty");
87 }
88
89 if (!module_type.empty()) {
90 if (module_type != "commonjs" && module_type != "module") {
91 errors->push_back("--input-type must be \"module\" or \"commonjs\"");
92 }
93 }
94
95 if (!experimental_specifier_resolution.empty()) {
96 if (experimental_specifier_resolution != "node" &&
97 experimental_specifier_resolution != "explicit") {
98 errors->push_back(
99 "invalid value for --experimental-specifier-resolution");
100 }
101 }
102
103 if (syntax_check_only && has_eval_string) {
104 errors->push_back("either --check or --eval can be used, not both");
105 }
106
107 if (!unhandled_rejections.empty() &&
108 unhandled_rejections != "warn-with-error-code" &&
109 unhandled_rejections != "throw" &&
110 unhandled_rejections != "strict" &&
111 unhandled_rejections != "warn" &&
112 unhandled_rejections != "none") {
113 errors->push_back("invalid value for --unhandled-rejections");
114 }
115
116 if (tls_min_v1_3 && tls_max_v1_2) {
117 errors->push_back("either --tls-min-v1.3 or --tls-max-v1.2 can be "
118 "used, not both");
119 }
120
121 if (heap_snapshot_near_heap_limit < 0) {
122 errors->push_back("--heap-snapshot-near-heap-limit must not be negative");
123 }
124
125 #if HAVE_INSPECTOR
126 if (!cpu_prof) {
127 if (!cpu_prof_name.empty()) {
128 errors->push_back("--cpu-prof-name must be used with --cpu-prof");
129 }
130 if (!cpu_prof_dir.empty()) {
131 errors->push_back("--cpu-prof-dir must be used with --cpu-prof");
132 }
133 // We can't catch the case where the value passed is the default value,
134 // then the option just becomes a noop which is fine.
135 if (cpu_prof_interval != kDefaultCpuProfInterval) {
136 errors->push_back("--cpu-prof-interval must be used with --cpu-prof");
137 }
138 }
139
140 if (cpu_prof && cpu_prof_dir.empty() && !diagnostic_dir.empty()) {
141 cpu_prof_dir = diagnostic_dir;
142 }
143
144 if (!heap_prof) {
145 if (!heap_prof_name.empty()) {
146 errors->push_back("--heap-prof-name must be used with --heap-prof");
147 }
148 if (!heap_prof_dir.empty()) {
149 errors->push_back("--heap-prof-dir must be used with --heap-prof");
150 }
151 // We can't catch the case where the value passed is the default value,
152 // then the option just becomes a noop which is fine.
153 if (heap_prof_interval != kDefaultHeapProfInterval) {
154 errors->push_back("--heap-prof-interval must be used with --heap-prof");
155 }
156 }
157
158 if (heap_prof && heap_prof_dir.empty() && !diagnostic_dir.empty()) {
159 heap_prof_dir = diagnostic_dir;
160 }
161
162 debug_options_.CheckOptions(errors);
163 #endif // HAVE_INSPECTOR
164 }
165
166 namespace options_parser {
167
168 class DebugOptionsParser : public OptionsParser<DebugOptions> {
169 public:
170 DebugOptionsParser();
171 };
172
173 class EnvironmentOptionsParser : public OptionsParser<EnvironmentOptions> {
174 public:
175 EnvironmentOptionsParser();
EnvironmentOptionsParser(const DebugOptionsParser & dop)176 explicit EnvironmentOptionsParser(const DebugOptionsParser& dop)
177 : EnvironmentOptionsParser() {
178 Insert(dop, &EnvironmentOptions::get_debug_options);
179 }
180 };
181
182 class PerIsolateOptionsParser : public OptionsParser<PerIsolateOptions> {
183 public:
184 PerIsolateOptionsParser() = delete;
185 explicit PerIsolateOptionsParser(const EnvironmentOptionsParser& eop);
186 };
187
188 class PerProcessOptionsParser : public OptionsParser<PerProcessOptions> {
189 public:
190 PerProcessOptionsParser() = delete;
191 explicit PerProcessOptionsParser(const PerIsolateOptionsParser& iop);
192 };
193
194 #if HAVE_INSPECTOR
195 const DebugOptionsParser _dop_instance{};
196 const EnvironmentOptionsParser _eop_instance{_dop_instance};
197
198 // This Parse is not dead code. It is used by embedders (e.g., Electron).
199 template <>
Parse(StringVector * const args,StringVector * const exec_args,StringVector * const v8_args,DebugOptions * const options,OptionEnvvarSettings required_env_settings,StringVector * const errors)200 void Parse(
201 StringVector* const args, StringVector* const exec_args,
202 StringVector* const v8_args,
203 DebugOptions* const options,
204 OptionEnvvarSettings required_env_settings, StringVector* const errors) {
205 _dop_instance.Parse(
206 args, exec_args, v8_args, options, required_env_settings, errors);
207 }
208 #else
209 const EnvironmentOptionsParser _eop_instance{};
210 #endif // HAVE_INSPECTOR
211 const PerIsolateOptionsParser _piop_instance{_eop_instance};
212 const PerProcessOptionsParser _ppop_instance{_piop_instance};
213
214 template <>
Parse(StringVector * const args,StringVector * const exec_args,StringVector * const v8_args,PerIsolateOptions * const options,OptionEnvvarSettings required_env_settings,StringVector * const errors)215 void Parse(
216 StringVector* const args, StringVector* const exec_args,
217 StringVector* const v8_args,
218 PerIsolateOptions* const options,
219 OptionEnvvarSettings required_env_settings, StringVector* const errors) {
220 _piop_instance.Parse(
221 args, exec_args, v8_args, options, required_env_settings, errors);
222 }
223
224 template <>
Parse(StringVector * const args,StringVector * const exec_args,StringVector * const v8_args,PerProcessOptions * const options,OptionEnvvarSettings required_env_settings,StringVector * const errors)225 void Parse(
226 StringVector* const args, StringVector* const exec_args,
227 StringVector* const v8_args,
228 PerProcessOptions* const options,
229 OptionEnvvarSettings required_env_settings, StringVector* const errors) {
230 _ppop_instance.Parse(
231 args, exec_args, v8_args, options, required_env_settings, errors);
232 }
233
234 // XXX: If you add an option here, please also add it to doc/node.1 and
235 // doc/api/cli.md
236 // TODO(addaleax): Make that unnecessary.
237
DebugOptionsParser()238 DebugOptionsParser::DebugOptionsParser() {
239 AddOption("--inspect-port",
240 "set host:port for inspector",
241 &DebugOptions::host_port,
242 kAllowedInEnvironment);
243 AddAlias("--debug-port", "--inspect-port");
244
245 AddOption("--inspect",
246 "activate inspector on host:port (default: 127.0.0.1:9229)",
247 &DebugOptions::inspector_enabled,
248 kAllowedInEnvironment);
249 AddAlias("--inspect=", { "--inspect-port", "--inspect" });
250
251 AddOption("--debug", "", &DebugOptions::deprecated_debug);
252 AddAlias("--debug=", "--debug");
253 AddOption("--debug-brk", "", &DebugOptions::deprecated_debug);
254 AddAlias("--debug-brk=", "--debug-brk");
255
256 AddOption("--inspect-brk",
257 "activate inspector on host:port and break at start of user script",
258 &DebugOptions::break_first_line,
259 kAllowedInEnvironment);
260 Implies("--inspect-brk", "--inspect");
261 AddAlias("--inspect-brk=", { "--inspect-port", "--inspect-brk" });
262
263 AddOption("--inspect-brk-node", "", &DebugOptions::break_node_first_line);
264 Implies("--inspect-brk-node", "--inspect");
265 AddAlias("--inspect-brk-node=", { "--inspect-port", "--inspect-brk-node" });
266
267 AddOption("--inspect-publish-uid",
268 "comma separated list of destinations for inspector uid"
269 "(default: stderr,http)",
270 &DebugOptions::inspect_publish_uid_string,
271 kAllowedInEnvironment);
272 }
273
EnvironmentOptionsParser()274 EnvironmentOptionsParser::EnvironmentOptionsParser() {
275 AddOption("--conditions",
276 "additional user conditions for conditional exports and imports",
277 &EnvironmentOptions::conditions,
278 kAllowedInEnvironment);
279 AddAlias("-C", "--conditions");
280 AddOption("--diagnostic-dir",
281 "set dir for all output files"
282 " (default: current working directory)",
283 &EnvironmentOptions::diagnostic_dir,
284 kAllowedInEnvironment);
285 AddOption("--dns-result-order",
286 "set default value of verbatim in dns.lookup. Options are "
287 "'ipv4first' (IPv4 addresses are placed before IPv6 addresses) "
288 "'verbatim' (addresses are in the order the DNS resolver "
289 "returned)",
290 &EnvironmentOptions::dns_result_order,
291 kAllowedInEnvironment);
292 AddOption("--enable-source-maps",
293 "experimental Source Map V3 support",
294 &EnvironmentOptions::enable_source_maps,
295 kAllowedInEnvironment);
296 AddOption("--experimental-abortcontroller",
297 "experimental AbortController support",
298 &EnvironmentOptions::experimental_abortcontroller,
299 kAllowedInEnvironment);
300 AddOption("--experimental-json-modules",
301 "experimental JSON interop support for the ES Module loader",
302 &EnvironmentOptions::experimental_json_modules,
303 kAllowedInEnvironment);
304 AddOption("--experimental-loader",
305 "use the specified module as a custom loader",
306 &EnvironmentOptions::userland_loader,
307 kAllowedInEnvironment);
308 AddAlias("--loader", "--experimental-loader");
309 AddOption("--experimental-modules",
310 "",
311 &EnvironmentOptions::experimental_modules,
312 kAllowedInEnvironment);
313 AddOption("--experimental-wasm-modules",
314 "experimental ES Module support for webassembly modules",
315 &EnvironmentOptions::experimental_wasm_modules,
316 kAllowedInEnvironment);
317 AddOption("--experimental-import-meta-resolve",
318 "experimental ES Module import.meta.resolve() support",
319 &EnvironmentOptions::experimental_import_meta_resolve,
320 kAllowedInEnvironment);
321 AddOption("--experimental-policy",
322 "use the specified file as a "
323 "security policy",
324 &EnvironmentOptions::experimental_policy,
325 kAllowedInEnvironment);
326 AddOption("[has_policy_integrity_string]",
327 "",
328 &EnvironmentOptions::has_policy_integrity_string);
329 AddOption("--policy-integrity",
330 "ensure the security policy contents match "
331 "the specified integrity",
332 &EnvironmentOptions::experimental_policy_integrity,
333 kAllowedInEnvironment);
334 Implies("--policy-integrity", "[has_policy_integrity_string]");
335 AddOption("--experimental-repl-await",
336 "experimental await keyword support in REPL",
337 &EnvironmentOptions::experimental_repl_await,
338 kAllowedInEnvironment);
339 AddOption("--experimental-vm-modules",
340 "experimental ES Module support in vm module",
341 &EnvironmentOptions::experimental_vm_modules,
342 kAllowedInEnvironment);
343 AddOption("--experimental-worker", "", NoOp{}, kAllowedInEnvironment);
344 AddOption("--experimental-report", "", NoOp{}, kAllowedInEnvironment);
345 AddOption("--experimental-wasi-unstable-preview1",
346 "experimental WASI support",
347 &EnvironmentOptions::experimental_wasi,
348 kAllowedInEnvironment);
349 AddOption("--expose-internals", "", &EnvironmentOptions::expose_internals);
350 AddOption("--frozen-intrinsics",
351 "experimental frozen intrinsics support",
352 &EnvironmentOptions::frozen_intrinsics,
353 kAllowedInEnvironment);
354 AddOption("--heapsnapshot-signal",
355 "Generate heap snapshot on specified signal",
356 &EnvironmentOptions::heap_snapshot_signal,
357 kAllowedInEnvironment);
358 AddOption("--heapsnapshot-near-heap-limit",
359 "Generate heap snapshots whenever V8 is approaching "
360 "the heap limit. No more than the specified number of "
361 "heap snapshots will be generated.",
362 &EnvironmentOptions::heap_snapshot_near_heap_limit,
363 kAllowedInEnvironment);
364 AddOption("--http-parser", "", NoOp{}, kAllowedInEnvironment);
365 AddOption("--insecure-http-parser",
366 "use an insecure HTTP parser that accepts invalid HTTP headers",
367 &EnvironmentOptions::insecure_http_parser,
368 kAllowedInEnvironment);
369 AddOption("--input-type",
370 "set module type for string input",
371 &EnvironmentOptions::module_type,
372 kAllowedInEnvironment);
373 AddOption("--experimental-specifier-resolution",
374 "Select extension resolution algorithm for es modules; "
375 "either 'explicit' (default) or 'node'",
376 &EnvironmentOptions::experimental_specifier_resolution,
377 kAllowedInEnvironment);
378 AddAlias("--es-module-specifier-resolution",
379 "--experimental-specifier-resolution");
380 AddOption("--deprecation",
381 "silence deprecation warnings",
382 &EnvironmentOptions::deprecation,
383 kAllowedInEnvironment,
384 true);
385 AddOption("--force-async-hooks-checks",
386 "disable checks for async_hooks",
387 &EnvironmentOptions::force_async_hooks_checks,
388 kAllowedInEnvironment,
389 true);
390 AddOption("--addons",
391 "disable loading native addons",
392 &EnvironmentOptions::allow_native_addons,
393 kAllowedInEnvironment,
394 true);
395 AddOption("--warnings",
396 "silence all process warnings",
397 &EnvironmentOptions::warnings,
398 kAllowedInEnvironment,
399 true);
400 AddOption("--force-context-aware",
401 "disable loading non-context-aware addons",
402 &EnvironmentOptions::force_context_aware,
403 kAllowedInEnvironment);
404 AddOption("--pending-deprecation",
405 "emit pending deprecation warnings",
406 &EnvironmentOptions::pending_deprecation,
407 kAllowedInEnvironment);
408 AddOption("--preserve-symlinks",
409 "preserve symbolic links when resolving",
410 &EnvironmentOptions::preserve_symlinks,
411 kAllowedInEnvironment);
412 AddOption("--preserve-symlinks-main",
413 "preserve symbolic links when resolving the main module",
414 &EnvironmentOptions::preserve_symlinks_main,
415 kAllowedInEnvironment);
416 AddOption("--prof",
417 "Generate V8 profiler output.",
418 V8Option{});
419 AddOption("--prof-process",
420 "process V8 profiler output generated using --prof",
421 &EnvironmentOptions::prof_process);
422 // Options after --prof-process are passed through to the prof processor.
423 AddAlias("--prof-process", { "--prof-process", "--" });
424 #if HAVE_INSPECTOR
425 AddOption("--cpu-prof",
426 "Start the V8 CPU profiler on start up, and write the CPU profile "
427 "to disk before exit. If --cpu-prof-dir is not specified, write "
428 "the profile to the current working directory.",
429 &EnvironmentOptions::cpu_prof);
430 AddOption("--cpu-prof-name",
431 "specified file name of the V8 CPU profile generated with "
432 "--cpu-prof",
433 &EnvironmentOptions::cpu_prof_name);
434 AddOption("--cpu-prof-interval",
435 "specified sampling interval in microseconds for the V8 CPU "
436 "profile generated with --cpu-prof. (default: 1000)",
437 &EnvironmentOptions::cpu_prof_interval);
438 AddOption("--cpu-prof-dir",
439 "Directory where the V8 profiles generated by --cpu-prof will be "
440 "placed. Does not affect --prof.",
441 &EnvironmentOptions::cpu_prof_dir);
442 AddOption(
443 "--heap-prof",
444 "Start the V8 heap profiler on start up, and write the heap profile "
445 "to disk before exit. If --heap-prof-dir is not specified, write "
446 "the profile to the current working directory.",
447 &EnvironmentOptions::heap_prof);
448 AddOption("--heap-prof-name",
449 "specified file name of the V8 heap profile generated with "
450 "--heap-prof",
451 &EnvironmentOptions::heap_prof_name);
452 AddOption("--heap-prof-dir",
453 "Directory where the V8 heap profiles generated by --heap-prof "
454 "will be placed.",
455 &EnvironmentOptions::heap_prof_dir);
456 AddOption("--heap-prof-interval",
457 "specified sampling interval in bytes for the V8 heap "
458 "profile generated with --heap-prof. (default: 512 * 1024)",
459 &EnvironmentOptions::heap_prof_interval);
460 #endif // HAVE_INSPECTOR
461 AddOption("--max-http-header-size",
462 "set the maximum size of HTTP headers (default: 16384 (16KB))",
463 &EnvironmentOptions::max_http_header_size,
464 kAllowedInEnvironment);
465 AddOption("--redirect-warnings",
466 "write warnings to file instead of stderr",
467 &EnvironmentOptions::redirect_warnings,
468 kAllowedInEnvironment);
469 AddOption("--test-udp-no-try-send", "", // For testing only.
470 &EnvironmentOptions::test_udp_no_try_send);
471 AddOption("--throw-deprecation",
472 "throw an exception on deprecations",
473 &EnvironmentOptions::throw_deprecation,
474 kAllowedInEnvironment);
475 AddOption("--trace-atomics-wait",
476 "trace Atomics.wait() operations",
477 &EnvironmentOptions::trace_atomics_wait,
478 kAllowedInEnvironment);
479 AddOption("--trace-deprecation",
480 "show stack traces on deprecations",
481 &EnvironmentOptions::trace_deprecation,
482 kAllowedInEnvironment);
483 AddOption("--trace-exit",
484 "show stack trace when an environment exits",
485 &EnvironmentOptions::trace_exit,
486 kAllowedInEnvironment);
487 AddOption("--trace-sync-io",
488 "show stack trace when use of sync IO is detected after the "
489 "first tick",
490 &EnvironmentOptions::trace_sync_io,
491 kAllowedInEnvironment);
492 AddOption("--trace-tls",
493 "prints TLS packet trace information to stderr",
494 &EnvironmentOptions::trace_tls,
495 kAllowedInEnvironment);
496 AddOption("--trace-uncaught",
497 "show stack traces for the `throw` behind uncaught exceptions",
498 &EnvironmentOptions::trace_uncaught,
499 kAllowedInEnvironment);
500 AddOption("--trace-warnings",
501 "show stack traces on process warnings",
502 &EnvironmentOptions::trace_warnings,
503 kAllowedInEnvironment);
504 AddOption("--unhandled-rejections",
505 "define unhandled rejections behavior. Options are 'strict' "
506 "(always raise an error), 'throw' (raise an error unless "
507 "'unhandledRejection' hook is set), 'warn' (log a warning), 'none' "
508 "(silence warnings), 'warn-with-error-code' (log a warning and set "
509 "exit code 1 unless 'unhandledRejection' hook is set).",
510 &EnvironmentOptions::unhandled_rejections,
511 kAllowedInEnvironment);
512 AddOption("--verify-base-objects",
513 "", /* undocumented, only for debugging */
514 &EnvironmentOptions::verify_base_objects,
515 kAllowedInEnvironment);
516
517 AddOption("--check",
518 "syntax check script without executing",
519 &EnvironmentOptions::syntax_check_only);
520 AddAlias("-c", "--check");
521 // This option is only so that we can tell --eval with an empty string from
522 // no eval at all. Having it not start with a dash makes it inaccessible
523 // from the parser itself, but available for using Implies().
524 // TODO(addaleax): When moving --help over to something generated from the
525 // programmatic descriptions, this will need some special care.
526 // (See also [ssl_openssl_cert_store] below.)
527 AddOption("[has_eval_string]", "", &EnvironmentOptions::has_eval_string);
528 AddOption("--eval", "evaluate script", &EnvironmentOptions::eval_string);
529 Implies("--eval", "[has_eval_string]");
530 AddOption("--print",
531 "evaluate script and print result",
532 &EnvironmentOptions::print_eval);
533 AddAlias("-e", "--eval");
534 AddAlias("--print <arg>", "-pe");
535 AddAlias("-pe", { "--print", "--eval" });
536 AddAlias("-p", "--print");
537 AddOption("--require",
538 "module to preload (option can be repeated)",
539 &EnvironmentOptions::preload_modules,
540 kAllowedInEnvironment);
541 AddAlias("-r", "--require");
542 AddOption("--interactive",
543 "always enter the REPL even if stdin does not appear "
544 "to be a terminal",
545 &EnvironmentOptions::force_repl);
546 AddAlias("-i", "--interactive");
547
548 AddOption("--napi-modules", "", NoOp{}, kAllowedInEnvironment);
549
550 AddOption("--tls-keylog",
551 "log TLS decryption keys to named file for traffic analysis",
552 &EnvironmentOptions::tls_keylog, kAllowedInEnvironment);
553
554 AddOption("--tls-min-v1.0",
555 "set default TLS minimum to TLSv1.0 (default: TLSv1.2)",
556 &EnvironmentOptions::tls_min_v1_0,
557 kAllowedInEnvironment);
558 AddOption("--tls-min-v1.1",
559 "set default TLS minimum to TLSv1.1 (default: TLSv1.2)",
560 &EnvironmentOptions::tls_min_v1_1,
561 kAllowedInEnvironment);
562 AddOption("--tls-min-v1.2",
563 "set default TLS minimum to TLSv1.2 (default: TLSv1.2)",
564 &EnvironmentOptions::tls_min_v1_2,
565 kAllowedInEnvironment);
566 AddOption("--tls-min-v1.3",
567 "set default TLS minimum to TLSv1.3 (default: TLSv1.2)",
568 &EnvironmentOptions::tls_min_v1_3,
569 kAllowedInEnvironment);
570 AddOption("--tls-max-v1.2",
571 "set default TLS maximum to TLSv1.2 (default: TLSv1.3)",
572 &EnvironmentOptions::tls_max_v1_2,
573 kAllowedInEnvironment);
574 // Current plan is:
575 // - 11.x and below: TLS1.3 is opt-in with --tls-max-v1.3
576 // - 12.x: TLS1.3 is opt-out with --tls-max-v1.2
577 // In either case, support both options they are uniformly available.
578 AddOption("--tls-max-v1.3",
579 "set default TLS maximum to TLSv1.3 (default: TLSv1.3)",
580 &EnvironmentOptions::tls_max_v1_3,
581 kAllowedInEnvironment);
582 }
583
PerIsolateOptionsParser(const EnvironmentOptionsParser & eop)584 PerIsolateOptionsParser::PerIsolateOptionsParser(
585 const EnvironmentOptionsParser& eop) {
586 AddOption("--track-heap-objects",
587 "track heap object allocations for heap snapshots",
588 &PerIsolateOptions::track_heap_objects,
589 kAllowedInEnvironment);
590 AddOption("--node-snapshot",
591 "", // It's a debug-only option.
592 &PerIsolateOptions::node_snapshot,
593 kAllowedInEnvironment);
594
595 // Explicitly add some V8 flags to mark them as allowed in NODE_OPTIONS.
596 AddOption("--abort-on-uncaught-exception",
597 "aborting instead of exiting causes a core file to be generated "
598 "for analysis",
599 V8Option{},
600 kAllowedInEnvironment);
601 AddOption("--interpreted-frames-native-stack",
602 "help system profilers to translate JavaScript interpreted frames",
603 V8Option{}, kAllowedInEnvironment);
604 AddOption("--max-old-space-size", "", V8Option{}, kAllowedInEnvironment);
605 AddOption("--perf-basic-prof", "", V8Option{}, kAllowedInEnvironment);
606 AddOption("--perf-basic-prof-only-functions",
607 "",
608 V8Option{},
609 kAllowedInEnvironment);
610 AddOption("--perf-prof", "", V8Option{}, kAllowedInEnvironment);
611 AddOption("--perf-prof-unwinding-info",
612 "",
613 V8Option{},
614 kAllowedInEnvironment);
615 AddOption("--stack-trace-limit", "", V8Option{}, kAllowedInEnvironment);
616 AddOption("--disallow-code-generation-from-strings",
617 "disallow eval and friends",
618 V8Option{},
619 kAllowedInEnvironment);
620 AddOption("--huge-max-old-generation-size",
621 "increase default maximum heap size on machines with 16GB memory "
622 "or more",
623 V8Option{},
624 kAllowedInEnvironment);
625 AddOption("--jitless",
626 "disable runtime allocation of executable memory",
627 V8Option{},
628 kAllowedInEnvironment);
629 AddOption("--report-uncaught-exception",
630 "generate diagnostic report on uncaught exceptions",
631 &PerIsolateOptions::report_uncaught_exception,
632 kAllowedInEnvironment);
633 AddOption("--report-on-signal",
634 "generate diagnostic report upon receiving signals",
635 &PerIsolateOptions::report_on_signal,
636 kAllowedInEnvironment);
637 AddOption("--report-signal",
638 "causes diagnostic report to be produced on provided signal,"
639 " unsupported in Windows. (default: SIGUSR2)",
640 &PerIsolateOptions::report_signal,
641 kAllowedInEnvironment);
642 Implies("--report-signal", "--report-on-signal");
643
644 AddOption("--experimental-top-level-await",
645 "",
646 &PerIsolateOptions::experimental_top_level_await,
647 kAllowedInEnvironment);
648 AddOption("--harmony-top-level-await", "", V8Option{});
649 Implies("--experimental-top-level-await", "--harmony-top-level-await");
650 Implies("--harmony-top-level-await", "--experimental-top-level-await");
651 ImpliesNot("--no-harmony-top-level-await", "--experimental-top-level-await");
652
653 Insert(eop, &PerIsolateOptions::get_per_env_options);
654 }
655
PerProcessOptionsParser(const PerIsolateOptionsParser & iop)656 PerProcessOptionsParser::PerProcessOptionsParser(
657 const PerIsolateOptionsParser& iop) {
658 AddOption("--title",
659 "the process title to use on startup",
660 &PerProcessOptions::title,
661 kAllowedInEnvironment);
662 AddOption("--trace-event-categories",
663 "comma separated list of trace event categories to record",
664 &PerProcessOptions::trace_event_categories,
665 kAllowedInEnvironment);
666 AddOption("--trace-event-file-pattern",
667 "Template string specifying the filepath for the trace-events "
668 "data, it supports ${rotation} and ${pid}.",
669 &PerProcessOptions::trace_event_file_pattern,
670 kAllowedInEnvironment);
671 AddAlias("--trace-events-enabled", {
672 "--trace-event-categories", "v8,node,node.async_hooks" });
673 AddOption("--v8-pool-size",
674 "set V8's thread pool size",
675 &PerProcessOptions::v8_thread_pool_size,
676 kAllowedInEnvironment);
677 AddOption("--zero-fill-buffers",
678 "automatically zero-fill all newly allocated Buffer and "
679 "SlowBuffer instances",
680 &PerProcessOptions::zero_fill_all_buffers,
681 kAllowedInEnvironment);
682 AddOption("--debug-arraybuffer-allocations",
683 "", /* undocumented, only for debugging */
684 &PerProcessOptions::debug_arraybuffer_allocations,
685 kAllowedInEnvironment);
686 AddOption("--disable-proto",
687 "disable Object.prototype.__proto__",
688 &PerProcessOptions::disable_proto,
689 kAllowedInEnvironment);
690
691 // 12.x renamed this inadvertently, so alias it for consistency within the
692 // release line, while using the original name for consistency with older
693 // release lines.
694 AddOption("--security-revert", "", &PerProcessOptions::security_reverts);
695 AddAlias("--security-reverts", "--security-revert");
696 AddOption("--completion-bash",
697 "print source-able bash completion script",
698 &PerProcessOptions::print_bash_completion);
699 AddOption("--help",
700 "print node command line options",
701 &PerProcessOptions::print_help);
702 AddAlias("-h", "--help");
703 AddOption(
704 "--version", "print Node.js version", &PerProcessOptions::print_version);
705 AddAlias("-v", "--version");
706 AddOption("--v8-options",
707 "print V8 command line options",
708 &PerProcessOptions::print_v8_help);
709 AddOption("--report-compact",
710 "output compact single-line JSON",
711 &PerProcessOptions::report_compact,
712 kAllowedInEnvironment);
713 AddOption("--report-dir",
714 "define custom report pathname."
715 " (default: current working directory)",
716 &PerProcessOptions::report_directory,
717 kAllowedInEnvironment);
718 AddAlias("--report-directory", "--report-dir");
719 AddOption("--report-filename",
720 "define custom report file name."
721 " (default: YYYYMMDD.HHMMSS.PID.SEQUENCE#.txt)",
722 &PerProcessOptions::report_filename,
723 kAllowedInEnvironment);
724 AddOption("--report-on-fatalerror",
725 "generate diagnostic report on fatal (internal) errors",
726 &PerProcessOptions::report_on_fatalerror,
727 kAllowedInEnvironment);
728
729 #ifdef NODE_HAVE_I18N_SUPPORT
730 AddOption("--icu-data-dir",
731 "set ICU data load path to dir (overrides NODE_ICU_DATA)"
732 #ifndef NODE_HAVE_SMALL_ICU
733 " (note: linked-in ICU data is present)"
734 #endif
735 ,
736 &PerProcessOptions::icu_data_dir,
737 kAllowedInEnvironment);
738 #endif
739
740 #if HAVE_OPENSSL
741 AddOption("--openssl-config",
742 "load OpenSSL configuration from the specified file "
743 "(overrides OPENSSL_CONF)",
744 &PerProcessOptions::openssl_config,
745 kAllowedInEnvironment);
746 AddOption("--tls-cipher-list",
747 "use an alternative default TLS cipher list",
748 &PerProcessOptions::tls_cipher_list,
749 kAllowedInEnvironment);
750 AddOption("--use-openssl-ca",
751 "use OpenSSL's default CA store"
752 #if defined(NODE_OPENSSL_CERT_STORE)
753 " (default)"
754 #endif
755 ,
756 &PerProcessOptions::use_openssl_ca,
757 kAllowedInEnvironment);
758 AddOption("--use-bundled-ca",
759 "use bundled CA store"
760 #if !defined(NODE_OPENSSL_CERT_STORE)
761 " (default)"
762 #endif
763 ,
764 &PerProcessOptions::use_bundled_ca,
765 kAllowedInEnvironment);
766 // Similar to [has_eval_string] above, except that the separation between
767 // this and use_openssl_ca only exists for option validation after parsing.
768 // This is not ideal.
769 AddOption("[ssl_openssl_cert_store]",
770 "",
771 &PerProcessOptions::ssl_openssl_cert_store);
772 Implies("--use-openssl-ca", "[ssl_openssl_cert_store]");
773 ImpliesNot("--use-bundled-ca", "[ssl_openssl_cert_store]");
774 AddOption("--enable-fips",
775 "enable FIPS crypto at startup",
776 &PerProcessOptions::enable_fips_crypto,
777 kAllowedInEnvironment);
778 AddOption("--force-fips",
779 "force FIPS crypto (cannot be disabled)",
780 &PerProcessOptions::force_fips_crypto,
781 kAllowedInEnvironment);
782 #endif
783 AddOption("--use-largepages",
784 "Map the Node.js static code to large pages. Options are "
785 "'off' (the default value, meaning do not map), "
786 "'on' (map and ignore failure, reporting it to stderr), "
787 "or 'silent' (map and silently ignore failure)",
788 &PerProcessOptions::use_largepages,
789 kAllowedInEnvironment);
790
791 AddOption("--trace-sigint",
792 "enable printing JavaScript stacktrace on SIGINT",
793 &PerProcessOptions::trace_sigint,
794 kAllowedInEnvironment);
795
796 Insert(iop, &PerProcessOptions::get_per_isolate_options);
797
798 AddOption("--node-memory-debug",
799 "Run with extra debug checks for memory leaks in Node.js itself",
800 NoOp{}, kAllowedInEnvironment);
801 Implies("--node-memory-debug", "--debug-arraybuffer-allocations");
802 Implies("--node-memory-debug", "--verify-base-objects");
803 }
804
RemoveBrackets(const std::string & host)805 inline std::string RemoveBrackets(const std::string& host) {
806 if (!host.empty() && host.front() == '[' && host.back() == ']')
807 return host.substr(1, host.size() - 2);
808 else
809 return host;
810 }
811
ParseAndValidatePort(const std::string & port,std::vector<std::string> * errors)812 inline int ParseAndValidatePort(const std::string& port,
813 std::vector<std::string>* errors) {
814 char* endptr;
815 errno = 0;
816 const unsigned long result = // NOLINT(runtime/int)
817 strtoul(port.c_str(), &endptr, 10);
818 if (errno != 0 || *endptr != '\0'||
819 (result != 0 && result < 1024) || result > 65535) {
820 errors->push_back(" must be 0 or in range 1024 to 65535.");
821 }
822 return static_cast<int>(result);
823 }
824
SplitHostPort(const std::string & arg,std::vector<std::string> * errors)825 HostPort SplitHostPort(const std::string& arg,
826 std::vector<std::string>* errors) {
827 // remove_brackets only works if no port is specified
828 // so if it has an effect only an IPv6 address was specified.
829 std::string host = RemoveBrackets(arg);
830 if (host.length() < arg.length())
831 return HostPort{host, DebugOptions::kDefaultInspectorPort};
832
833 size_t colon = arg.rfind(':');
834 if (colon == std::string::npos) {
835 // Either a port number or a host name. Assume that
836 // if it's not all decimal digits, it's a host name.
837 for (char c : arg) {
838 if (c < '0' || c > '9') {
839 return HostPort{arg, DebugOptions::kDefaultInspectorPort};
840 }
841 }
842 return HostPort { "", ParseAndValidatePort(arg, errors) };
843 }
844 // Host and port found:
845 return HostPort { RemoveBrackets(arg.substr(0, colon)),
846 ParseAndValidatePort(arg.substr(colon + 1), errors) };
847 }
848
GetBashCompletion()849 std::string GetBashCompletion() {
850 Mutex::ScopedLock lock(per_process::cli_options_mutex);
851 const auto& parser = _ppop_instance;
852
853 std::ostringstream out;
854
855 out << "_node_complete() {\n"
856 " local cur_word options\n"
857 " cur_word=\"${COMP_WORDS[COMP_CWORD]}\"\n"
858 " if [[ \"${cur_word}\" == -* ]] ; then\n"
859 " COMPREPLY=( $(compgen -W '";
860
861 for (const auto& item : parser.options_) {
862 if (item.first[0] != '[') {
863 out << item.first << " ";
864 }
865 }
866 for (const auto& item : parser.aliases_) {
867 if (item.first[0] != '[') {
868 out << item.first << " ";
869 }
870 }
871 if (parser.aliases_.size() > 0) {
872 out.seekp(-1, out.cur); // Strip the trailing space
873 }
874
875 out << "' -- \"${cur_word}\") )\n"
876 " return 0\n"
877 " else\n"
878 " COMPREPLY=( $(compgen -f \"${cur_word}\") )\n"
879 " return 0\n"
880 " fi\n"
881 "}\n"
882 "complete -o filenames -o nospace -o bashdefault "
883 "-F _node_complete node node_g";
884 return out.str();
885 }
886
887 // Return a map containing all the options and their metadata as well
888 // as the aliases
GetOptions(const FunctionCallbackInfo<Value> & args)889 void GetOptions(const FunctionCallbackInfo<Value>& args) {
890 Mutex::ScopedLock lock(per_process::cli_options_mutex);
891 Environment* env = Environment::GetCurrent(args);
892 if (!env->has_run_bootstrapping_code()) {
893 // No code because this is an assertion.
894 return env->ThrowError(
895 "Should not query options before bootstrapping is done");
896 }
897 env->set_has_serialized_options(true);
898
899 Isolate* isolate = env->isolate();
900 Local<Context> context = env->context();
901
902 // Temporarily act as if the current Environment's/IsolateData's options were
903 // the default options, i.e. like they are the ones we'd access for global
904 // options parsing, so that all options are available from the main parser.
905 auto original_per_isolate = per_process::cli_options->per_isolate;
906 per_process::cli_options->per_isolate = env->isolate_data()->options();
907 auto original_per_env = per_process::cli_options->per_isolate->per_env;
908 per_process::cli_options->per_isolate->per_env = env->options();
909 auto on_scope_leave = OnScopeLeave([&]() {
910 per_process::cli_options->per_isolate->per_env = original_per_env;
911 per_process::cli_options->per_isolate = original_per_isolate;
912 });
913
914 Local<Map> options = Map::New(isolate);
915 if (options
916 ->SetPrototype(context, env->primordials_safe_map_prototype_object())
917 .IsNothing()) {
918 return;
919 }
920
921 for (const auto& item : _ppop_instance.options_) {
922 Local<Value> value;
923 const auto& option_info = item.second;
924 auto field = option_info.field;
925 PerProcessOptions* opts = per_process::cli_options.get();
926 switch (option_info.type) {
927 case kNoOp:
928 case kV8Option:
929 // Special case for --abort-on-uncaught-exception which is also
930 // respected by Node.js internals
931 if (item.first == "--abort-on-uncaught-exception") {
932 value = Boolean::New(
933 isolate, original_per_env->abort_on_uncaught_exception);
934 } else {
935 value = Undefined(isolate);
936 }
937 break;
938 case kBoolean:
939 value = Boolean::New(isolate,
940 *_ppop_instance.Lookup<bool>(field, opts));
941 break;
942 case kInteger:
943 value = Number::New(isolate,
944 *_ppop_instance.Lookup<int64_t>(field, opts));
945 break;
946 case kUInteger:
947 value = Number::New(isolate,
948 *_ppop_instance.Lookup<uint64_t>(field, opts));
949 break;
950 case kString:
951 if (!ToV8Value(context,
952 *_ppop_instance.Lookup<std::string>(field, opts))
953 .ToLocal(&value)) {
954 return;
955 }
956 break;
957 case kStringList:
958 if (!ToV8Value(context,
959 *_ppop_instance.Lookup<StringVector>(field, opts))
960 .ToLocal(&value)) {
961 return;
962 }
963 break;
964 case kHostPort: {
965 const HostPort& host_port =
966 *_ppop_instance.Lookup<HostPort>(field, opts);
967 Local<Object> obj = Object::New(isolate);
968 Local<Value> host;
969 if (!ToV8Value(context, host_port.host()).ToLocal(&host) ||
970 obj->Set(context, env->host_string(), host).IsNothing() ||
971 obj->Set(context,
972 env->port_string(),
973 Integer::New(isolate, host_port.port()))
974 .IsNothing()) {
975 return;
976 }
977 value = obj;
978 break;
979 }
980 default:
981 UNREACHABLE();
982 }
983 CHECK(!value.IsEmpty());
984
985 Local<Value> name = ToV8Value(context, item.first).ToLocalChecked();
986 Local<Object> info = Object::New(isolate);
987 Local<Value> help_text;
988 if (!ToV8Value(context, option_info.help_text).ToLocal(&help_text) ||
989 !info->Set(context, env->help_text_string(), help_text)
990 .FromMaybe(false) ||
991 !info->Set(context,
992 env->env_var_settings_string(),
993 Integer::New(isolate,
994 static_cast<int>(option_info.env_setting)))
995 .FromMaybe(false) ||
996 !info->Set(context,
997 env->type_string(),
998 Integer::New(isolate, static_cast<int>(option_info.type)))
999 .FromMaybe(false) ||
1000 !info->Set(context,
1001 env->default_is_true_string(),
1002 Boolean::New(isolate, option_info.default_is_true))
1003 .FromMaybe(false) ||
1004 info->Set(context, env->value_string(), value).IsNothing() ||
1005 options->Set(context, name, info).IsEmpty()) {
1006 return;
1007 }
1008 }
1009
1010 Local<Value> aliases;
1011 if (!ToV8Value(context, _ppop_instance.aliases_).ToLocal(&aliases)) return;
1012
1013 if (aliases.As<Object>()
1014 ->SetPrototype(context, env->primordials_safe_map_prototype_object())
1015 .IsNothing()) {
1016 return;
1017 }
1018
1019 Local<Object> ret = Object::New(isolate);
1020 if (ret->Set(context, env->options_string(), options).IsNothing() ||
1021 ret->Set(context, env->aliases_string(), aliases).IsNothing()) {
1022 return;
1023 }
1024
1025 args.GetReturnValue().Set(ret);
1026 }
1027
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)1028 void Initialize(Local<Object> target,
1029 Local<Value> unused,
1030 Local<Context> context,
1031 void* priv) {
1032 Environment* env = Environment::GetCurrent(context);
1033 Isolate* isolate = env->isolate();
1034 env->SetMethodNoSideEffect(target, "getOptions", GetOptions);
1035
1036 Local<Object> env_settings = Object::New(isolate);
1037 NODE_DEFINE_CONSTANT(env_settings, kAllowedInEnvironment);
1038 NODE_DEFINE_CONSTANT(env_settings, kDisallowedInEnvironment);
1039 target
1040 ->Set(
1041 context, FIXED_ONE_BYTE_STRING(isolate, "envSettings"), env_settings)
1042 .Check();
1043
1044 target
1045 ->Set(context,
1046 FIXED_ONE_BYTE_STRING(env->isolate(), "shouldNotRegisterESMLoader"),
1047 Boolean::New(isolate, env->should_not_register_esm_loader()))
1048 .Check();
1049
1050 Local<Object> types = Object::New(isolate);
1051 NODE_DEFINE_CONSTANT(types, kNoOp);
1052 NODE_DEFINE_CONSTANT(types, kV8Option);
1053 NODE_DEFINE_CONSTANT(types, kBoolean);
1054 NODE_DEFINE_CONSTANT(types, kInteger);
1055 NODE_DEFINE_CONSTANT(types, kUInteger);
1056 NODE_DEFINE_CONSTANT(types, kString);
1057 NODE_DEFINE_CONSTANT(types, kHostPort);
1058 NODE_DEFINE_CONSTANT(types, kStringList);
1059 target->Set(context, FIXED_ONE_BYTE_STRING(isolate, "types"), types)
1060 .Check();
1061 }
1062
1063 } // namespace options_parser
1064
HandleEnvOptions(std::shared_ptr<EnvironmentOptions> env_options)1065 void HandleEnvOptions(std::shared_ptr<EnvironmentOptions> env_options) {
1066 HandleEnvOptions(env_options, [](const char* name) {
1067 std::string text;
1068 return credentials::SafeGetenv(name, &text) ? text : "";
1069 });
1070 }
1071
HandleEnvOptions(std::shared_ptr<EnvironmentOptions> env_options,std::function<std::string (const char *)> opt_getter)1072 void HandleEnvOptions(std::shared_ptr<EnvironmentOptions> env_options,
1073 std::function<std::string(const char*)> opt_getter) {
1074 env_options->pending_deprecation =
1075 opt_getter("NODE_PENDING_DEPRECATION") == "1";
1076
1077 env_options->preserve_symlinks = opt_getter("NODE_PRESERVE_SYMLINKS") == "1";
1078
1079 env_options->preserve_symlinks_main =
1080 opt_getter("NODE_PRESERVE_SYMLINKS_MAIN") == "1";
1081
1082 if (env_options->redirect_warnings.empty())
1083 env_options->redirect_warnings = opt_getter("NODE_REDIRECT_WARNINGS");
1084 }
1085
ParseNodeOptionsEnvVar(const std::string & node_options,std::vector<std::string> * errors)1086 std::vector<std::string> ParseNodeOptionsEnvVar(
1087 const std::string& node_options, std::vector<std::string>* errors) {
1088 std::vector<std::string> env_argv;
1089
1090 bool is_in_string = false;
1091 bool will_start_new_arg = true;
1092 for (std::string::size_type index = 0; index < node_options.size(); ++index) {
1093 char c = node_options.at(index);
1094
1095 // Backslashes escape the following character
1096 if (c == '\\' && is_in_string) {
1097 if (index + 1 == node_options.size()) {
1098 errors->push_back("invalid value for NODE_OPTIONS "
1099 "(invalid escape)\n");
1100 return env_argv;
1101 } else {
1102 c = node_options.at(++index);
1103 }
1104 } else if (c == ' ' && !is_in_string) {
1105 will_start_new_arg = true;
1106 continue;
1107 } else if (c == '"') {
1108 is_in_string = !is_in_string;
1109 continue;
1110 }
1111
1112 if (will_start_new_arg) {
1113 env_argv.emplace_back(std::string(1, c));
1114 will_start_new_arg = false;
1115 } else {
1116 env_argv.back() += c;
1117 }
1118 }
1119
1120 if (is_in_string) {
1121 errors->push_back("invalid value for NODE_OPTIONS "
1122 "(unterminated string)\n");
1123 }
1124 return env_argv;
1125 }
1126 } // namespace node
1127
1128 NODE_MODULE_CONTEXT_AWARE_INTERNAL(options, node::options_parser::Initialize)
1129