Repository: electron/electron
Pull request: fix: hard crash on invalid command line switches

#### Description of Change

Closes https://github.com/electron/electron/issues/45991.

Addresses an issue where poorly formed switched could cause a hard crash in Chromium since M134 by ensuring the switches are lowercased. Also improve the docs a little.

#### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [x] PR description included and stakeholders cc'd
- [x] `npm test` passes
- [x] tests are [changed or added](https://github.com/electron/electron/blob/main/docs/development/testing.md)
- [x] [PR release notes](https://github.com/electron/clerk/blob/main/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/main/README.md#examples).

#### Release Notes

Notes: Fixed an issue where badly formatted switches could cause crashes in `app.commandLine` functions.


Changed files:
filename: docs/api/command-line.md
patch: @@ -20,45 +20,87 @@ document.
 
 #### `commandLine.appendSwitch(switch[, value])`
 
-* `switch` string - A command-line switch, without the leading `--`
-* `value` string (optional) - A value for the given switch
+* `switch` string - A command-line switch, without the leading `--`.
+* `value` string (optional) - A value for the given switch.
 
 Append a switch (with optional `value`) to Chromium's command line.
 
 **Note:** This will not affect `process.argv`. The intended usage of this function is to
 control Chromium's behavior.
 
+```js
+const { app } = require('electron')
+
+app.commandLine.appendSwitch('remote-debugging-port', '8315')
+```
+
 #### `commandLine.appendArgument(value)`
 
-* `value` string - The argument to append to the command line
+* `value` string - The argument to append to the command line.
 
 Append an argument to Chromium's command line. The argument will be quoted
 correctly. Switches will precede arguments regardless of appending order.
 
 If you're appending an argument like `--switch=value`, consider using `appendSwitch('switch', 'value')` instead.
 
+```js
+const { app } = require('electron')
+
+app.commandLine.appendArgument('--enable-experimental-web-platform-features')
+```
+
 **Note:** This will not affect `process.argv`. The intended usage of this function is to
 control Chromium's behavior.
 
 #### `commandLine.hasSwitch(switch)`
 
-* `switch` string - A command-line switch
+* `switch` string - A command-line switch.
 
 Returns `boolean` - Whether the command-line switch is present.
 
+```js
+const { app } = require('electron')
+
+app.commandLine.appendSwitch('remote-debugging-port', '8315')
+const hasPort = app.commandLine.hasSwitch('remote-debugging-port')
+console.log(hasPort) // true
+```
+
 #### `commandLine.getSwitchValue(switch)`
 
-* `switch` string - A command-line switch
+* `switch` string - A command-line switch.
 
 Returns `string` - The command-line switch value.
 
+This function is meant to obtain Chromium command line switches. It is not
+meant to be used for application-specific command line arguments. For the
+latter, please use `process.argv`.
+
+```js
+const { app } = require('electron')
+
+app.commandLine.appendSwitch('remote-debugging-port', '8315')
+const portValue = app.commandLine.getSwitchValue('remote-debugging-port')
+console.log(portValue) // '8315'
+```
+
 **Note:** When the switch is not present or has no value, it returns empty string.
 
 #### `commandLine.removeSwitch(switch)`
 
-* `switch` string - A command-line switch
+* `switch` string - A command-line switch.
 
 Removes the specified switch from Chromium's command line.
 
+```js
+const { app } = require('electron')
+
+app.commandLine.appendSwitch('remote-debugging-port', '8315')
+console.log(app.commandLine.hasSwitch('remote-debugging-port')) // true
+
+app.commandLine.removeSwitch('remote-debugging-port')
+console.log(app.commandLine.hasSwitch('remote-debugging-port')) // false
+```
+
 **Note:** This will not affect `process.argv`. The intended usage of this function is to
 control Chromium's behavior.

filename: docs/breaking-changes.md
patch: @@ -19,6 +19,14 @@ This document uses the following convention to categorize breaking changes:
 `BrowserWindow.IsVisibleOnAllWorkspaces()` will now return false on Linux if the
 window is not currently visible.
 
+### Behavior Changes: `app.commandLine`
+
+`app.commandLine` will convert upper-cases switches and arguments to lowercase.
+
+`app.commandLine` was only meant to handle chromium switches (which aren't case-sensitive) and switches passed via `app.commandLine` will not be passed down to any of the child processes.
+
+If you were using `app.commandLine` to control the behavior of the  main process, you should do this via `process.argv`.
+
 ## Planned Breaking API Changes (36.0)
 
 ### Utility Process unhandled rejection behavior change

filename: shell/common/api/electron_api_command_line.cc
patch: @@ -4,50 +4,59 @@
 
 #include "base/command_line.h"
 #include "base/files/file_path.h"
+#include "base/strings/string_util.h"
 #include "services/network/public/cpp/network_switches.h"
 #include "shell/common/gin_converters/base_converter.h"
 #include "shell/common/gin_converters/file_path_converter.h"
 #include "shell/common/gin_helper/dictionary.h"
 #include "shell/common/node_includes.h"
+#include "third_party/abseil-cpp/absl/strings/ascii.h"
 
 namespace {
+bool HasSwitch(const std::string& switch_string) {
+  auto switch_str = base::ToLowerASCII(switch_string);
 
-bool HasSwitch(const std::string& name) {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(name);
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  return command_line->HasSwitch(switch_str);
 }
 
-base::CommandLine::StringType GetSwitchValue(const std::string& name) {
-  return base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(name);
+base::CommandLine::StringType GetSwitchValue(gin_helper::ErrorThrower thrower,
+                                             const std::string& switch_string) {
+  auto switch_str = base::ToLowerASCII(switch_string);
+
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  return command_line->GetSwitchValueNative(switch_str);
 }
 
 void AppendSwitch(const std::string& switch_string,
                   gin_helper::Arguments* args) {
+  auto switch_str = base::ToLowerASCII(switch_string);
   auto* command_line = base::CommandLine::ForCurrentProcess();
-
   if (base::EndsWith(switch_string, "-path",
                      base::CompareCase::INSENSITIVE_ASCII) ||
       switch_string == network::switches::kLogNetLog) {
     base::FilePath path;
     args->GetNext(&path);
-    command_line->AppendSwitchPath(switch_string, path);
+    command_line->AppendSwitchPath(switch_str, path);
     return;
   }
 
   base::CommandLine::StringType value;
   if (args->GetNext(&value))
-    command_line->AppendSwitchNative(switch_string, value);
+    command_line->AppendSwitchNative(switch_str, value);
   else
-    command_line->AppendSwitch(switch_string);
+    command_line->AppendSwitch(switch_str);
 }
 
 void RemoveSwitch(const std::string& switch_string) {
+  auto switch_str = base::ToLowerASCII(switch_string);
+
   auto* command_line = base::CommandLine::ForCurrentProcess();
-  command_line->RemoveSwitch(switch_string);
+  command_line->RemoveSwitch(switch_str);
 }
 
 void AppendArg(const std::string& arg) {
   auto* command_line = base::CommandLine::ForCurrentProcess();
-
   command_line->AppendArg(arg);
 }