Repository: electron/electron
Pull request: fix: crash on windows when UTF-8 is in path

#### Description of Change

Backport of #48898 

In 6399527761b43abb5a3ea40dd64eb05efad3b9de we changed the path strings that `node_modules.cc` operates on from single-byte to wide strings. Unfortunately this means that `generic_path()` that the "fix: ensure TraverseParent bails on resource path exit" patch was calling was no longer a safe method to call on Windows if the underlying string has unicode characters in it.

Here we fix it by using `ConvertGenericPathToUTF8` from the Node.js internal utilities.

cc @codebytere @VerteDinde @dsanders11 

#### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [x] PR description included and stakeholders cc'd
- [ ] `npm test` passes
- [ ] tests are [changed or added](https://github.com/electron/electron/blob/main/docs/development/testing.md)
- [ ] relevant API documentation, tutorials, and examples are updated and follow the [documentation style guide](https://github.com/electron/electron/blob/main/docs/development/style-guide.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: fix crash on windows when UTF-8 is in path


Changed files:
filename: patches/node/.patches
patch: @@ -39,7 +39,6 @@ build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch
 fix_adjust_wpt_and_webidl_tests_for_enabled_float16array.patch
 chore_add_createexternalizabletwobytestring_to_globals.patch
 refactor_attach_cppgc_heap_on_v8_isolate_creation.patch
-fix_ensure_traverseparent_bails_on_resource_path_exit.patch
 cli_move_--trace-atomics-wait_to_eol.patch
 fix_cppgc_initializing_twice.patch
 fix_task_starvation_in_inspector_context_test.patch
@@ -53,3 +52,4 @@ src_switch_from_get_setprototype_to_get_setprototypev2.patch
 fix_replace_deprecated_setprototype.patch
 fix_redefined_macos_sdk_header_symbols.patch
 src_use_cp_utf8_for_wide_file_names_on_win32.patch
+fix_ensure_traverseparent_bails_on_resource_path_exit.patch

filename: patches/node/api_remove_deprecated_getisolate.patch
patch: @@ -592,7 +592,7 @@ index 3c5f38ba4f492749c9d7d82179d2a6563787602b..6e83da3ee975dea431e21209bba9227e
    data_.Reset();
    return ret;
 diff --git a/src/node_modules.cc b/src/node_modules.cc
-index ed22da844a61b14b8580cd3d6bb3a233b8559b38..14f2a35f87e8c2fa17898147d7247cc00c066f35 100644
+index 62050791174563f88b8629d576eed8959b3c2e20..fa04b4a8cdd02a2cad1eaf2e5a848b951d1b1150 100644
 --- a/src/node_modules.cc
 +++ b/src/node_modules.cc
 @@ -64,7 +64,7 @@ void BindingData::Deserialize(v8::Local<v8::Context> context,
@@ -604,7 +604,7 @@ index ed22da844a61b14b8580cd3d6bb3a233b8559b38..14f2a35f87e8c2fa17898147d7247cc0
    Realm* realm = Realm::GetCurrent(context);
    BindingData* binding = realm->AddBindingData<BindingData>(holder);
    CHECK_NOT_NULL(binding);
-@@ -656,7 +656,7 @@ void BindingData::CreatePerContextProperties(Local<Object> target,
+@@ -617,7 +617,7 @@ void BindingData::CreatePerContextProperties(Local<Object> target,
    Realm* realm = Realm::GetCurrent(context);
    realm->AddBindingData<BindingData>(target);
  

filename: patches/node/fix_ensure_traverseparent_bails_on_resource_path_exit.patch
patch: @@ -8,10 +8,10 @@ resource path. This commit ensures that the TraverseParent function
 bails out if the parent path is outside of the resource path.
 
 diff --git a/src/node_modules.cc b/src/node_modules.cc
-index 60b03b1563b230f78d8a9bdd8480da4d2ae90a27..35bfada261258407982d9e24cf7b3e820235c941 100644
+index 947513d5b91e8c13478b25d80a1157edbcd64b64..4e46df6afe6ca57f6df9a64d3fc572450baf7d5c 100644
 --- a/src/node_modules.cc
 +++ b/src/node_modules.cc
-@@ -279,8 +279,41 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
+@@ -315,8 +315,41 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
      Realm* realm, const std::filesystem::path& check_path) {
    std::filesystem::path current_path = check_path;
    auto env = realm->env();
@@ -47,22 +47,22 @@ index 60b03b1563b230f78d8a9bdd8480da4d2ae90a27..35bfada261258407982d9e24cf7b3e82
 +        });
 +  };
 +
-+  bool did_original_path_start_with_resources_path = starts_with(check_path.
-+      generic_string(), resources_path);
++  bool did_original_path_start_with_resources_path = starts_with(
++      ConvertGenericPathToUTF8(check_path), resources_path);
 +
    do {
      current_path = current_path.parent_path();
  
-@@ -299,6 +332,12 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
-       return nullptr;
+@@ -336,6 +369,12 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
+       }
      }
  
 +    // If current path is outside the resources path, bail.
 +    if (did_original_path_start_with_resources_path &&
-+        !starts_with(current_path.generic_string(), resources_path)) {
++        !starts_with(ConvertGenericPathToUTF8(current_path), resources_path)) {
 +      return nullptr;
 +    }
 +
      // Check if the path ends with `/node_modules`
-     if (current_path.generic_string().ends_with("/node_modules")) {
+     if (current_path.filename() == "node_modules") {
        return nullptr;

filename: patches/node/fix_expose_readfilesync_override_for_modules.patch
patch: @@ -20,7 +20,7 @@ index 82225b0a53dd828750991a4e15a060b736b6ea2b..4b0d31356a2496a7fc67876a22da2453
    V(performance_entry_callback, v8::Function)                                  \
    V(prepare_stack_trace_callback, v8::Function)                                \
 diff --git a/src/node_modules.cc b/src/node_modules.cc
-index 35bfada261258407982d9e24cf7b3e820235c941..ed22da844a61b14b8580cd3d6bb3a233b8559b38 100644
+index 60b03b1563b230f78d8a9bdd8480da4d2ae90a27..62050791174563f88b8629d576eed8959b3c2e20 100644
 --- a/src/node_modules.cc
 +++ b/src/node_modules.cc
 @@ -21,6 +21,7 @@ namespace modules {
@@ -90,7 +90,7 @@ index 35bfada261258407982d9e24cf7b3e820235c941..ed22da844a61b14b8580cd3d6bb3a233
  void BindingData::ReadPackageJSON(const FunctionCallbackInfo<Value>& args) {
    CHECK_GE(args.Length(), 1);  // path, [is_esm, base, specifier]
    CHECK(args[0]->IsString());  // path
-@@ -597,6 +633,8 @@ void InitImportMetaPathHelpers(const FunctionCallbackInfo<Value>& args) {
+@@ -558,6 +594,8 @@ void InitImportMetaPathHelpers(const FunctionCallbackInfo<Value>& args) {
  void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
                                               Local<ObjectTemplate> target) {
    Isolate* isolate = isolate_data->isolate();
@@ -99,7 +99,7 @@ index 35bfada261258407982d9e24cf7b3e820235c941..ed22da844a61b14b8580cd3d6bb3a233
    SetMethod(isolate, target, "readPackageJSON", ReadPackageJSON);
    SetMethod(isolate,
              target,
-@@ -635,6 +673,8 @@ void BindingData::CreatePerContextProperties(Local<Object> target,
+@@ -596,6 +634,8 @@ void BindingData::CreatePerContextProperties(Local<Object> target,
  
  void BindingData::RegisterExternalReferences(
      ExternalReferenceRegistry* registry) {

filename: patches/node/src_use_cp_utf8_for_wide_file_names_on_win32.patch
patch: @@ -136,10 +136,10 @@ index 75be21c9e8b413f522240a906da06d26c44d5b71..e94c2b5f2cf7cac413cd5cb782fa1cca
                      std::make_error_code(std::errc::file_exists),
                      "cp",
 diff --git a/src/node_modules.cc b/src/node_modules.cc
-index 14f2a35f87e8c2fa17898147d7247cc00c066f35..871282c6f8780ee0bca1e7230c0c2d83fd0c98c0 100644
+index fa04b4a8cdd02a2cad1eaf2e5a848b951d1b1150..947513d5b91e8c13478b25d80a1157edbcd64b64 100644
 --- a/src/node_modules.cc
 +++ b/src/node_modules.cc
-@@ -360,12 +360,13 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
+@@ -327,22 +327,24 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
  
      // Stop the search when the process doesn't have permissions
      // to walk upwards
@@ -158,10 +158,6 @@ index 14f2a35f87e8c2fa17898147d7247cc00c066f35..871282c6f8780ee0bca1e7230c0c2d83
 +      }
      }
  
-     // If current path is outside the resources path, bail.
-@@ -375,13 +376,14 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
-     }
- 
      // Check if the path ends with `/node_modules`
 -    if (current_path.generic_string().ends_with("/node_modules")) {
 +    if (current_path.filename() == "node_modules") {
@@ -176,7 +172,7 @@ index 14f2a35f87e8c2fa17898147d7247cc00c066f35..871282c6f8780ee0bca1e7230c0c2d83
      if (package_json != nullptr) {
        return package_json;
      }
-@@ -403,20 +405,12 @@ void BindingData::GetNearestParentPackageJSONType(
+@@ -364,20 +366,12 @@ void BindingData::GetNearestParentPackageJSONType(
  
    ToNamespacedPath(realm->env(), &path_value);