Skip to content

Commit 13d23c9

Browse files
committed
src: use CP_UTF8 for wide file names on win32
`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
1 parent 0b621d2 commit 13d23c9

File tree

4 files changed

+51
-35
lines changed

4 files changed

+51
-35
lines changed

src/node_file.cc

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3173,29 +3173,6 @@ static void GetFormatOfExtensionlessFile(
31733173
#define BufferValueToPath(str) \
31743174
std::filesystem::path(ConvertToWideString(str.ToString(), CP_UTF8))
31753175

3176-
std::string ConvertWideToUTF8(const std::wstring& wstr) {
3177-
if (wstr.empty()) return std::string();
3178-
3179-
int size_needed = WideCharToMultiByte(CP_UTF8,
3180-
0,
3181-
&wstr[0],
3182-
static_cast<int>(wstr.size()),
3183-
nullptr,
3184-
0,
3185-
nullptr,
3186-
nullptr);
3187-
std::string strTo(size_needed, 0);
3188-
WideCharToMultiByte(CP_UTF8,
3189-
0,
3190-
&wstr[0],
3191-
static_cast<int>(wstr.size()),
3192-
&strTo[0],
3193-
size_needed,
3194-
nullptr,
3195-
nullptr);
3196-
return strTo;
3197-
}
3198-
31993176
#define PathToString(path) ConvertWideToUTF8(path.wstring());
32003177

32013178
#else // _WIN32

src/node_modules.cc

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -294,24 +294,39 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
294294
break;
295295
}
296296

297-
// Stop the search when the process doesn't have permissions
298-
// to walk upwards
299-
if (is_permissions_enabled &&
300-
!env->permission()->is_granted(
301-
env,
302-
permission::PermissionScope::kFileSystemRead,
303-
current_path.generic_string())) [[unlikely]] {
297+
// Check if the path ends with `/node_modules`
298+
if (current_path.filename() == "node_modules") {
304299
return nullptr;
305300
}
306301

307-
// Check if the path ends with `/node_modules`
308-
if (current_path.generic_string().ends_with("/node_modules")) {
309-
return nullptr;
302+
// Stop the search when the process doesn't have permissions
303+
// to walk upwards
304+
if (is_permissions_enabled) {
305+
std::string generic_path;
306+
#ifdef _WIN32
307+
generic_path = ConvertWideToUTF8(current_path.generic_wstring());
308+
#else // _WIN32
309+
generic_path = current_path.generic_string();
310+
#endif // _WIN32
311+
//
312+
if (!env->permission()->is_granted(
313+
env,
314+
permission::PermissionScope::kFileSystemRead,
315+
generic_path)) [[unlikely]] {
316+
return nullptr;
317+
}
310318
}
311319

312320
auto package_json_path = current_path / "package.json";
321+
322+
std::string package_json_path_str;
323+
#ifdef _WIN32
324+
package_json_path_str = ConvertWideToUTF8(package_json_path.wstring());
325+
#else // _WIN32
326+
package_json_path_str = package_json_path.string();
327+
#endif // _WIN32
313328
auto package_json =
314-
GetPackageJSON(realm, package_json_path.string(), nullptr);
329+
GetPackageJSON(realm, package_json_path_str, nullptr);
315330
if (package_json != nullptr) {
316331
return package_json;
317332
}
@@ -341,7 +356,7 @@ void BindingData::GetNearestParentPackageJSONType(
341356
std::filesystem::path path;
342357

343358
#ifdef _WIN32
344-
std::wstring wide_path = ConvertToWideString(path_value_str, GetACP());
359+
std::wstring wide_path = ConvertToWideString(path_value_str, CP_UTF8);
345360
path = std::filesystem::path(wide_path);
346361
#else
347362
path = std::filesystem::path(path_value_str);

src/util-inl.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,29 @@ inline std::wstring ConvertToWideString(const std::string& str,
731731
size_needed);
732732
return wstrTo;
733733
}
734+
735+
std::string ConvertWideToUTF8(const std::wstring& wstr) {
736+
if (wstr.empty()) return std::string();
737+
738+
int size_needed = WideCharToMultiByte(CP_UTF8,
739+
0,
740+
&wstr[0],
741+
static_cast<int>(wstr.size()),
742+
nullptr,
743+
0,
744+
nullptr,
745+
nullptr);
746+
std::string strTo(size_needed, 0);
747+
WideCharToMultiByte(CP_UTF8,
748+
0,
749+
&wstr[0],
750+
static_cast<int>(wstr.size()),
751+
&strTo[0],
752+
size_needed,
753+
nullptr,
754+
nullptr);
755+
return strTo;
756+
}
734757
#endif // _WIN32
735758

736759
inline v8::MaybeLocal<v8::Object> NewDictionaryInstance(

src/util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,7 @@ class JSONOutputStream final : public v8::OutputStream {
10391039
// case insensitive.
10401040
inline bool IsWindowsBatchFile(const char* filename);
10411041
inline std::wstring ConvertToWideString(const std::string& str, UINT code_page);
1042+
inline std::string ConvertWideToUTF8(const std::wstring& wstr);
10421043
#endif // _WIN32
10431044

10441045
// A helper to create a new instance of the dictionary template.

0 commit comments

Comments
 (0)