fix_ensure_traverseparent_bails_on_resource_path_exit.patch 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
  2. From: Shelley Vohr <[email protected]>
  3. Date: Tue, 18 Mar 2025 10:41:27 +0100
  4. Subject: fix: ensure TraverseParent bails on resource path exit
  5. Electron's security model requires that we do not traverse outside of the
  6. resource path. This commit ensures that the TraverseParent function
  7. bails out if the parent path is outside of the resource path.
  8. diff --git a/src/node_modules.cc b/src/node_modules.cc
  9. index 210a01d24e11764dc9fc37a77b354f11383693f8..4e9f70a1c41b44d2a1863b778d4f1e37279178d9 100644
  10. --- a/src/node_modules.cc
  11. +++ b/src/node_modules.cc
  12. @@ -290,8 +290,41 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
  13. Realm* realm, const std::filesystem::path& check_path) {
  14. std::filesystem::path current_path = check_path;
  15. auto env = realm->env();
  16. + Isolate* isolate = env->isolate();
  17. const bool is_permissions_enabled = env->permission()->enabled();
  18. + // Get the resources path with trailing slash.
  19. + std::string resources_path;
  20. + {
  21. + HandleScope handle_scope(isolate);
  22. + Local<Value> resources_path_value;
  23. + Local<Object> process_object = env->process_object();
  24. +
  25. + Local<String> resources_path_key =
  26. + String::NewFromUtf8Literal(isolate, "resourcesPath");
  27. + if (process_object->Get(env->context(), resources_path_key)
  28. + .ToLocal(&resources_path_value) &&
  29. + resources_path_value->IsString()) {
  30. + resources_path = *String::Utf8Value(isolate, resources_path_value);
  31. + if (!resources_path.empty() && !resources_path.ends_with(kPathSeparator)) {
  32. + resources_path += kPathSeparator;
  33. + }
  34. + }
  35. + }
  36. +
  37. + auto starts_with = [](const std::string& str, const std::string& prefix) -> bool {
  38. + if (prefix.size() > str.size()) return false;
  39. + return std::equal(
  40. + prefix.begin(), prefix.end(), str.begin(),
  41. + [](char a, char b) {
  42. + return std::tolower(static_cast<unsigned char>(a)) ==
  43. + std::tolower(static_cast<unsigned char>(b));
  44. + });
  45. + };
  46. +
  47. + bool did_original_path_start_with_resources_path = starts_with(check_path.
  48. + generic_string(), resources_path);
  49. +
  50. do {
  51. current_path = current_path.parent_path();
  52. @@ -310,6 +343,12 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
  53. return nullptr;
  54. }
  55. + // If current path is outside the resources path, bail.
  56. + if (did_original_path_start_with_resources_path &&
  57. + !starts_with(current_path.generic_string(), resources_path)) {
  58. + return nullptr;
  59. + }
  60. +
  61. // Check if the path ends with `/node_modules`
  62. if (current_path.generic_string().ends_with("/node_modules")) {
  63. return nullptr;