Skip to content

Commit 7b464ea

Browse files
committed
fix(FileSystemStorage): use case-insensitive comparison to test unsafe path
1 parent c9bbb87 commit 7b464ea

File tree

3 files changed

+25
-10
lines changed

3 files changed

+25
-10
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
8+
### Fixed
9+
- use case-insensitive comparison to detect unsafe paths. Prevent issues when
10+
Vortex is used.
11+
812
### Added
913
- debug-level logs to diagnosis issues at runtime.
1014

src/FileSystemStorage.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,30 +159,32 @@ Red::DynArray<Red::Handle<AsyncFile>> FileSystemStorage::get_async_files() {
159159

160160
std::filesystem::path FileSystemStorage::restrict_path(
161161
const std::string& p_path, std::error_code& p_error) const {
162-
std::filesystem::path path = storage_path / p_path;
162+
std::filesystem::path file_path = storage_path / p_path;
163163

164164
// NOTE: See issue regarding usage of `std::weakly_canonical` with MO2:
165165
// https://github.com/ModOrganizer2/modorganizer/issues/2039
166166
if (FileSystem::is_mo2_detected()) {
167167
if (p_path.find('/') != std::string::npos ||
168168
p_path.find('\\') != std::string::npos) {
169+
FileSystem::debug("Unsafe path detected: \"{}\"", file_path.string().c_str());
169170
p_error = std::make_error_code(std::errc::permission_denied);
170171
}
171-
return path;
172+
return file_path;
172173
}
173174

174-
const std::filesystem::path real_path = std::filesystem::weakly_canonical(path, p_error);
175+
const std::filesystem::path absolute_path = std::filesystem::weakly_canonical(file_path, p_error);
175176
if (p_error) {
176177
FileSystem::debug("Failed to get canonical path for \"{}\": {}", p_path.c_str(), p_error.message().c_str());
177-
return real_path;
178+
return absolute_path;
178179
}
179180

180-
if (real_path.string().find(storage_path.string() + "\\") != 0) {
181-
FileSystem::debug("Accessing: \"{}\"", p_path.c_str());
182-
FileSystem::debug("Resolving: \"{}\"", real_path.string().c_str());
181+
const auto path = to_lower(absolute_path.string());
182+
const auto root_path = to_lower(storage_path.string());
183+
if (path.find(root_path + "\\") != 0 && path.find(root_path + '/') != 0) {
184+
FileSystem::debug("Unsafe path detected: \"{}\"", absolute_path.string().c_str());
183185
p_error = std::make_error_code(std::errc::permission_denied);
184186
}
185-
return real_path;
187+
return absolute_path;
186188
}
187189

188190
SharedMutex FileSystemStorage::get_mutex(const std::filesystem::path& p_path) {

src/Utils.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,17 @@ namespace RedFS {
99

1010
using SharedMutex = std::shared_ptr<std::mutex>;
1111

12-
static bool equals_insensitive(const std::string& p_a, const std::string& p_b) {
13-
return std::ranges::equal(p_a, p_b, [](char a, char b) -> bool {
12+
inline std::string to_lower(const std::string& p_str) {
13+
std::string str = p_str;
14+
std::ranges::transform(str, str.begin(),
15+
[](const int c) {
16+
return std::tolower(c);
17+
});
18+
return str;
19+
}
20+
21+
inline bool equals_insensitive(const std::string& p_a, const std::string& p_b) {
22+
return std::ranges::equal(p_a, p_b, [](const int a, const int b) -> bool {
1423
return std::tolower(a) == std::tolower(b);
1524
});
1625
}

0 commit comments

Comments
 (0)