mirror of
https://github.com/electron/electron.git
synced 2026-01-09 15:38:08 -05:00
fix: ESM-from-CJS import when CJK is in path (#48862)
Upstream fix: https://github.com/nodejs/node/pull/60575 Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Fedor Indutny <indutny@signal.org> Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
This commit is contained in:
@@ -41,4 +41,5 @@ lib_check_sharedarraybuffer_existence_in_fast-utf8-stream.patch
|
||||
chore_handle_support_for_import_defer_as_ns_and_import_defer.patch
|
||||
api_delete_deprecated_fields_on_v8_isolate.patch
|
||||
api_promote_deprecation_of_v8_context_and_v8_object_api_methods.patch
|
||||
src_use_cp_utf8_for_wide_file_names_on_win32.patch
|
||||
reland_temporal_unflag_temporal.patch
|
||||
|
||||
312
patches/node/src_use_cp_utf8_for_wide_file_names_on_win32.patch
Normal file
312
patches/node/src_use_cp_utf8_for_wide_file_names_on_win32.patch
Normal file
@@ -0,0 +1,312 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Fedor Indutny <238531+indutny@users.noreply.github.com>
|
||||
Date: Fri, 7 Nov 2025 19:41:44 -0800
|
||||
Subject: src: use CP_UTF8 for wide file names on win32
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
`src/node_modules.cc` needs to be consistent with `src/node_file.cc` in
|
||||
how it translates the utf8 strings to `std::wstring` otherwise we might
|
||||
end up in situation where we can read the source code of imported
|
||||
package from disk, but fail to recognize that it is an ESM (or CJS) and
|
||||
cause runtime errors. This type of error is possible on Windows when the
|
||||
path contains unicode characters and "Language for non-Unicode programs"
|
||||
is set to "Chinese (Traditional, Taiwan)".
|
||||
|
||||
See: #58768
|
||||
PR-URL: https://github.com/nodejs/node/pull/60575
|
||||
Reviewed-By: Anna Henningsen <anna@addaleax.net>
|
||||
Reviewed-By: Darshan Sen <raisinten@gmail.com>
|
||||
Reviewed-By: Stefan Stojanovic <stefan.stojanovic@janeasystems.com>
|
||||
Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com>
|
||||
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
|
||||
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
|
||||
|
||||
diff --git a/src/node_file.cc b/src/node_file.cc
|
||||
index 969e7d08086f8442bed476feaf15599b8c79db7c..e7459654401c275dfb86207831016ed71060bcc9 100644
|
||||
--- a/src/node_file.cc
|
||||
+++ b/src/node_file.cc
|
||||
@@ -3175,42 +3175,6 @@ static void GetFormatOfExtensionlessFile(
|
||||
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
|
||||
}
|
||||
|
||||
-#ifdef _WIN32
|
||||
-#define BufferValueToPath(str) \
|
||||
- std::filesystem::path(ConvertToWideString(str.ToString(), CP_UTF8))
|
||||
-
|
||||
-std::string ConvertWideToUTF8(const std::wstring& wstr) {
|
||||
- if (wstr.empty()) return std::string();
|
||||
-
|
||||
- int size_needed = WideCharToMultiByte(CP_UTF8,
|
||||
- 0,
|
||||
- &wstr[0],
|
||||
- static_cast<int>(wstr.size()),
|
||||
- nullptr,
|
||||
- 0,
|
||||
- nullptr,
|
||||
- nullptr);
|
||||
- std::string strTo(size_needed, 0);
|
||||
- WideCharToMultiByte(CP_UTF8,
|
||||
- 0,
|
||||
- &wstr[0],
|
||||
- static_cast<int>(wstr.size()),
|
||||
- &strTo[0],
|
||||
- size_needed,
|
||||
- nullptr,
|
||||
- nullptr);
|
||||
- return strTo;
|
||||
-}
|
||||
-
|
||||
-#define PathToString(path) ConvertWideToUTF8(path.wstring());
|
||||
-
|
||||
-#else // _WIN32
|
||||
-
|
||||
-#define BufferValueToPath(str) std::filesystem::path(str.ToStringView());
|
||||
-#define PathToString(path) path.native();
|
||||
-
|
||||
-#endif // _WIN32
|
||||
-
|
||||
static void CpSyncCheckPaths(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
Isolate* isolate = env->isolate();
|
||||
@@ -3223,7 +3187,7 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo<Value>& args) {
|
||||
THROW_IF_INSUFFICIENT_PERMISSIONS(
|
||||
env, permission::PermissionScope::kFileSystemRead, src.ToStringView());
|
||||
|
||||
- auto src_path = BufferValueToPath(src);
|
||||
+ auto src_path = src.ToPath();
|
||||
|
||||
BufferValue dest(isolate, args[1]);
|
||||
CHECK_NOT_NULL(*dest);
|
||||
@@ -3231,7 +3195,7 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo<Value>& args) {
|
||||
THROW_IF_INSUFFICIENT_PERMISSIONS(
|
||||
env, permission::PermissionScope::kFileSystemWrite, dest.ToStringView());
|
||||
|
||||
- auto dest_path = BufferValueToPath(dest);
|
||||
+ auto dest_path = dest.ToPath();
|
||||
bool dereference = args[2]->IsTrue();
|
||||
bool recursive = args[3]->IsTrue();
|
||||
|
||||
@@ -3260,8 +3224,8 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo<Value>& args) {
|
||||
(src_status.type() == std::filesystem::file_type::directory) ||
|
||||
(dereference && src_status.type() == std::filesystem::file_type::symlink);
|
||||
|
||||
- auto src_path_str = PathToString(src_path);
|
||||
- auto dest_path_str = PathToString(dest_path);
|
||||
+ auto src_path_str = ConvertPathToUTF8(src_path);
|
||||
+ auto dest_path_str = ConvertPathToUTF8(dest_path);
|
||||
|
||||
if (!error_code) {
|
||||
// Check if src and dest are identical.
|
||||
@@ -3356,7 +3320,7 @@ static bool CopyUtimes(const std::filesystem::path& src,
|
||||
uv_fs_t req;
|
||||
auto cleanup = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); });
|
||||
|
||||
- auto src_path_str = PathToString(src);
|
||||
+ auto src_path_str = ConvertPathToUTF8(src);
|
||||
int result = uv_fs_stat(nullptr, &req, src_path_str.c_str(), nullptr);
|
||||
if (is_uv_error(result)) {
|
||||
env->ThrowUVException(result, "stat", nullptr, src_path_str.c_str());
|
||||
@@ -3367,7 +3331,7 @@ static bool CopyUtimes(const std::filesystem::path& src,
|
||||
const double source_atime = s->st_atim.tv_sec + s->st_atim.tv_nsec / 1e9;
|
||||
const double source_mtime = s->st_mtim.tv_sec + s->st_mtim.tv_nsec / 1e9;
|
||||
|
||||
- auto dest_file_path_str = PathToString(dest);
|
||||
+ auto dest_file_path_str = ConvertPathToUTF8(dest);
|
||||
int utime_result = uv_fs_utime(nullptr,
|
||||
&req,
|
||||
dest_file_path_str.c_str(),
|
||||
@@ -3502,7 +3466,7 @@ static void CpSyncCopyDir(const FunctionCallbackInfo<Value>& args) {
|
||||
std::error_code error;
|
||||
for (auto dir_entry : std::filesystem::directory_iterator(src)) {
|
||||
auto dest_file_path = dest / dir_entry.path().filename();
|
||||
- auto dest_str = PathToString(dest);
|
||||
+ auto dest_str = ConvertPathToUTF8(dest);
|
||||
|
||||
if (dir_entry.is_symlink()) {
|
||||
if (verbatim_symlinks) {
|
||||
@@ -3565,7 +3529,7 @@ static void CpSyncCopyDir(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
} else if (std::filesystem::is_regular_file(dest_file_path)) {
|
||||
if (!dereference || (!force && error_on_exist)) {
|
||||
- auto dest_file_path_str = PathToString(dest_file_path);
|
||||
+ auto dest_file_path_str = ConvertPathToUTF8(dest_file_path);
|
||||
env->ThrowStdErrException(
|
||||
std::make_error_code(std::errc::file_exists),
|
||||
"cp",
|
||||
diff --git a/src/node_modules.cc b/src/node_modules.cc
|
||||
index d8477191efafba3c41c06d765f4b03bd00b8573c..5444e556b89ba5b71739753eaafa706cd9727013 100644
|
||||
--- a/src/node_modules.cc
|
||||
+++ b/src/node_modules.cc
|
||||
@@ -365,12 +365,13 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
|
||||
|
||||
// Stop the search when the process doesn't have permissions
|
||||
// to walk upwards
|
||||
- if (is_permissions_enabled &&
|
||||
- !env->permission()->is_granted(
|
||||
- env,
|
||||
- permission::PermissionScope::kFileSystemRead,
|
||||
- current_path.generic_string())) [[unlikely]] {
|
||||
- return nullptr;
|
||||
+ if (is_permissions_enabled) {
|
||||
+ if (!env->permission()->is_granted(
|
||||
+ env,
|
||||
+ permission::PermissionScope::kFileSystemRead,
|
||||
+ ConvertGenericPathToUTF8(current_path))) [[unlikely]] {
|
||||
+ return nullptr;
|
||||
+ }
|
||||
}
|
||||
|
||||
// If current path is outside the resources path, bail.
|
||||
@@ -380,13 +381,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") {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto package_json_path = current_path / "package.json";
|
||||
+
|
||||
auto package_json =
|
||||
- GetPackageJSON(realm, package_json_path.string(), nullptr);
|
||||
+ GetPackageJSON(realm, ConvertPathToUTF8(package_json_path), nullptr);
|
||||
if (package_json != nullptr) {
|
||||
return package_json;
|
||||
}
|
||||
@@ -408,20 +410,12 @@ void BindingData::GetNearestParentPackageJSONType(
|
||||
|
||||
ToNamespacedPath(realm->env(), &path_value);
|
||||
|
||||
- std::string path_value_str = path_value.ToString();
|
||||
+ auto path = path_value.ToPath();
|
||||
+
|
||||
if (slashCheck) {
|
||||
- path_value_str.push_back(kPathSeparator);
|
||||
+ path /= "";
|
||||
}
|
||||
|
||||
- std::filesystem::path path;
|
||||
-
|
||||
-#ifdef _WIN32
|
||||
- std::wstring wide_path = ConvertToWideString(path_value_str, GetACP());
|
||||
- path = std::filesystem::path(wide_path);
|
||||
-#else
|
||||
- path = std::filesystem::path(path_value_str);
|
||||
-#endif
|
||||
-
|
||||
auto package_json = TraverseParent(realm, path);
|
||||
|
||||
if (package_json == nullptr) {
|
||||
diff --git a/src/util-inl.h b/src/util-inl.h
|
||||
index da9268dcf2ff432ddeec7c0f61a147b73f3130e2..82b2760f535345d126bc3dcf3890573d36dbb497 100644
|
||||
--- a/src/util-inl.h
|
||||
+++ b/src/util-inl.h
|
||||
@@ -698,12 +698,11 @@ inline bool IsWindowsBatchFile(const char* filename) {
|
||||
return !extension.empty() && (extension == "cmd" || extension == "bat");
|
||||
}
|
||||
|
||||
-inline std::wstring ConvertToWideString(const std::string& str,
|
||||
- UINT code_page) {
|
||||
+inline std::wstring ConvertUTF8ToWideString(const std::string& str) {
|
||||
int size_needed = MultiByteToWideChar(
|
||||
- code_page, 0, &str[0], static_cast<int>(str.size()), nullptr, 0);
|
||||
+ CP_UTF8, 0, &str[0], static_cast<int>(str.size()), nullptr, 0);
|
||||
std::wstring wstrTo(size_needed, 0);
|
||||
- MultiByteToWideChar(code_page,
|
||||
+ MultiByteToWideChar(CP_UTF8,
|
||||
0,
|
||||
&str[0],
|
||||
static_cast<int>(str.size()),
|
||||
@@ -711,6 +710,59 @@ inline std::wstring ConvertToWideString(const std::string& str,
|
||||
size_needed);
|
||||
return wstrTo;
|
||||
}
|
||||
+
|
||||
+std::string ConvertWideStringToUTF8(const std::wstring& wstr) {
|
||||
+ if (wstr.empty()) return std::string();
|
||||
+
|
||||
+ int size_needed = WideCharToMultiByte(CP_UTF8,
|
||||
+ 0,
|
||||
+ &wstr[0],
|
||||
+ static_cast<int>(wstr.size()),
|
||||
+ nullptr,
|
||||
+ 0,
|
||||
+ nullptr,
|
||||
+ nullptr);
|
||||
+ std::string strTo(size_needed, 0);
|
||||
+ WideCharToMultiByte(CP_UTF8,
|
||||
+ 0,
|
||||
+ &wstr[0],
|
||||
+ static_cast<int>(wstr.size()),
|
||||
+ &strTo[0],
|
||||
+ size_needed,
|
||||
+ nullptr,
|
||||
+ nullptr);
|
||||
+ return strTo;
|
||||
+}
|
||||
+
|
||||
+template <typename T, size_t kStackStorageSize>
|
||||
+std::filesystem::path MaybeStackBuffer<T, kStackStorageSize>::ToPath() const {
|
||||
+ std::wstring wide_path = ConvertUTF8ToWideString(ToString());
|
||||
+ return std::filesystem::path(wide_path);
|
||||
+}
|
||||
+
|
||||
+std::string ConvertPathToUTF8(const std::filesystem::path& path) {
|
||||
+ return ConvertWideStringToUTF8(path.wstring());
|
||||
+}
|
||||
+
|
||||
+std::string ConvertGenericPathToUTF8(const std::filesystem::path& path) {
|
||||
+ return ConvertWideStringToUTF8(path.generic_wstring());
|
||||
+}
|
||||
+
|
||||
+#else // _WIN32
|
||||
+
|
||||
+template <typename T, size_t kStackStorageSize>
|
||||
+std::filesystem::path MaybeStackBuffer<T, kStackStorageSize>::ToPath() const {
|
||||
+ return std::filesystem::path(ToStringView());
|
||||
+}
|
||||
+
|
||||
+std::string ConvertPathToUTF8(const std::filesystem::path& path) {
|
||||
+ return path.native();
|
||||
+}
|
||||
+
|
||||
+std::string ConvertGenericPathToUTF8(const std::filesystem::path& path) {
|
||||
+ return path.generic_string();
|
||||
+}
|
||||
+
|
||||
#endif // _WIN32
|
||||
|
||||
inline v8::MaybeLocal<v8::Object> NewDictionaryInstance(
|
||||
diff --git a/src/util.h b/src/util.h
|
||||
index 6da57f95165bbdedb65dab6eaae8c39b815ee4e5..e65d6dbf359f61d051efe4e129c9035ed90cc8f6 100644
|
||||
--- a/src/util.h
|
||||
+++ b/src/util.h
|
||||
@@ -507,6 +507,8 @@ class MaybeStackBuffer {
|
||||
inline std::basic_string_view<T> ToStringView() const {
|
||||
return {out(), length()};
|
||||
}
|
||||
+ // This can only be used if the buffer contains path data in UTF8
|
||||
+ inline std::filesystem::path ToPath() const;
|
||||
|
||||
private:
|
||||
size_t length_;
|
||||
@@ -1026,9 +1028,15 @@ class JSONOutputStream final : public v8::OutputStream {
|
||||
// Returns true if OS==Windows and filename ends in .bat or .cmd,
|
||||
// case insensitive.
|
||||
inline bool IsWindowsBatchFile(const char* filename);
|
||||
-inline std::wstring ConvertToWideString(const std::string& str, UINT code_page);
|
||||
+inline std::wstring ConvertUTF8ToWideString(const std::string& str);
|
||||
+inline std::string ConvertWideStringToUTF8(const std::wstring& wstr);
|
||||
+
|
||||
#endif // _WIN32
|
||||
|
||||
+inline std::filesystem::path ConvertUTF8ToPath(const std::string& str);
|
||||
+inline std::string ConvertPathToUTF8(const std::filesystem::path& path);
|
||||
+inline std::string ConvertGenericPathToUTF8(const std::filesystem::path& path);
|
||||
+
|
||||
// A helper to create a new instance of the dictionary template.
|
||||
// Unlike v8::DictionaryTemplate::NewInstance, this method will
|
||||
// check that all properties have been set (are not empty MaybeLocals)
|
||||
Reference in New Issue
Block a user