diff --git a/src/node.cc b/src/node.cc index b368c58734345f..b2d28bbb70e8ba 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1633,7 +1633,14 @@ static ExitCode StartInternal(int argc, char** argv) { int Start(int argc, char** argv) { #ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION - std::tie(argc, argv) = sea::FixupArgsForSEA(argc, argv); + std::vector errors; + std::tie(argc, argv) = sea::FixupArgsForSEA(argc, argv, &errors); + if (!errors.empty()) { + for (const std::string& error : errors) { + FPrintF(stderr, "%s: %s\n", argv[0], error); + } + return static_cast(ExitCode::kInvalidCommandLineArgument); + } #endif return static_cast(StartInternal(argc, argv)); } diff --git a/src/node_sea.cc b/src/node_sea.cc index 1be41e6f14146e..9b84540391291d 100644 --- a/src/node_sea.cc +++ b/src/node_sea.cc @@ -283,7 +283,9 @@ void IsExperimentalSeaWarningNeeded(const FunctionCallbackInfo& args) { sea_resource.flags & SeaFlags::kDisableExperimentalSeaWarning)); } -std::tuple FixupArgsForSEA(int argc, char** argv) { +std::tuple FixupArgsForSEA(int argc, + char** argv, + std::vector* errors) { // Repeats argv[0] at position 1 on argv as a replacement for the missing // entry point file path. if (IsSingleExecutable()) { @@ -303,8 +305,10 @@ std::tuple FixupArgsForSEA(int argc, char** argv) { for (int i = 1; i < argc; ++i) { if (strncmp(argv[i], "--node-options=", 15) == 0) { std::string node_options = argv[i] + 15; - std::vector errors; - cli_extension_args = ParseNodeOptionsEnvVar(node_options, &errors); + cli_extension_args = ParseNodeOptionsEnvVar(node_options, errors); + if (!errors->empty()) { + return {argc, argv}; + } // Remove this argument by shifting the rest for (int j = i; j < argc - 1; ++j) { argv[j] = argv[j + 1]; diff --git a/src/node_sea.h b/src/node_sea.h index dd0b89db841eed..b147caefa5e589 100644 --- a/src/node_sea.h +++ b/src/node_sea.h @@ -70,7 +70,9 @@ struct SeaResource { bool IsSingleExecutable(); std::string_view FindSingleExecutableBlob(); SeaResource FindSingleExecutableResource(); -std::tuple FixupArgsForSEA(int argc, char** argv); +std::tuple FixupArgsForSEA(int argc, + char** argv, + std::vector* errors); node::ExitCode WriteSingleExecutableBlob( const std::string& config_path, const std::vector& args, diff --git a/test/sea/test-single-executable-application-exec-argv-extension-cli.js b/test/sea/test-single-executable-application-exec-argv-extension-cli.js index 3999cf0cbaecf6..82f53906334102 100644 --- a/test/sea/test-single-executable-application-exec-argv-extension-cli.js +++ b/test/sea/test-single-executable-application-exec-argv-extension-cli.js @@ -20,18 +20,36 @@ tmpdir.refresh(); const outputFile = buildSEA(fixtures.path('sea', 'exec-argv-extension-cli')); +const env = { + ...process.env, + NODE_OPTIONS: '--max-old-space-size=2048', // Should be ignored + COMMON_DIRECTORY: join(__dirname, '..', 'common'), + NODE_DEBUG_NATIVE: 'SEA', +}; + // Test that --node-options works with execArgvExtension: "cli" spawnSyncAndAssert( outputFile, ['--node-options=--max-old-space-size=1024', 'user-arg1', 'user-arg2'], { - env: { - ...process.env, - NODE_OPTIONS: '--max-old-space-size=2048', // Should be ignored - COMMON_DIRECTORY: join(__dirname, '..', 'common'), - NODE_DEBUG_NATIVE: 'SEA', - }, + env, }, { stdout: /execArgvExtension cli test passed/, }); + +// Test that malformed --node-options values are rejected. +[ + ['--no-warnings "', /unterminated string/], + ['"--no-warnings\\', /invalid escape/], +].forEach(([nodeOptions, stderr]) => { + spawnSyncAndAssert( + outputFile, + [`--node-options=${nodeOptions}`], + { env }, + { + status: 9, + stdout: '', + stderr, + }); +});