mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-14 04:24:31 +00:00
[file_sys] Quick sandbox escape fix (#4223)
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4223 Reviewed-by: Lizzie <lizzie@eden-emu.dev> Reviewed-by: MaranBr <maranbr@eden-emu.dev>
This commit is contained in:
@@ -476,6 +476,29 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
|
||||
path.erase(std::unique(start, path.end(),
|
||||
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
|
||||
path.end());
|
||||
|
||||
const bool absolute = !path.empty() && path[0] == type2;
|
||||
std::vector<std::string_view> parts;
|
||||
|
||||
for (const auto part : SplitPathComponents(path))
|
||||
{
|
||||
if (part.empty() || part == ".")
|
||||
continue;
|
||||
if (part == ".." && !parts.empty() && parts.back() != "..")
|
||||
parts.pop_back();
|
||||
else if (part != "..") parts.push_back(part);
|
||||
}
|
||||
|
||||
std::string resolved = absolute ? std::string(1, type2) : std::string{};
|
||||
for (std::size_t i = 0; i < parts.size(); ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
resolved += type2;
|
||||
resolved.append(parts[i].data(), parts[i].size());
|
||||
}
|
||||
|
||||
path = std::move(resolved);
|
||||
|
||||
return std::string(RemoveTrailingSlash(path));
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,16 @@ namespace {
|
||||
|
||||
constexpr size_t MaxOpenFiles = 8192;
|
||||
|
||||
bool IsWithinRoot(std::string_view root, std::string_view full_path) {
|
||||
if (root.empty())
|
||||
return true;
|
||||
|
||||
if (full_path.size() < root.size() || full_path.substr(0, root.size()) != root)
|
||||
return false;
|
||||
|
||||
return full_path.size() == root.size() || full_path[root.size()] == '/' || full_path[root.size()] == '\\';
|
||||
}
|
||||
|
||||
constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(OpenMode mode) {
|
||||
switch (mode) {
|
||||
case OpenMode::Read:
|
||||
@@ -403,7 +413,8 @@ RealVfsDirectory::~RealVfsDirectory() = default;
|
||||
|
||||
VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) const {
|
||||
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
|
||||
if (!FS::Exists(full_path) || FS::IsDir(full_path)) {
|
||||
if (!FS::Exists(full_path) || FS::IsDir(full_path)
|
||||
|| !IsWithinRoot(FS::SanitizePath(path), full_path)) {
|
||||
return nullptr;
|
||||
}
|
||||
return base.OpenFile(full_path, perms);
|
||||
@@ -411,7 +422,8 @@ VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) co
|
||||
|
||||
VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const {
|
||||
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
|
||||
if (!FS::Exists(full_path) || !FS::IsDir(full_path)) {
|
||||
if (!FS::Exists(full_path) || !FS::IsDir(full_path)
|
||||
|| !IsWithinRoot(FS::SanitizePath(path), full_path)) {
|
||||
return nullptr;
|
||||
}
|
||||
return base.OpenDirectory(full_path, perms);
|
||||
@@ -427,7 +439,7 @@ VirtualDir RealVfsDirectory::GetSubdirectory(std::string_view name) const {
|
||||
|
||||
VirtualFile RealVfsDirectory::CreateFileRelative(std::string_view relative_path) {
|
||||
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
|
||||
if (!FS::CreateParentDirs(full_path)) {
|
||||
if (!FS::CreateParentDirs(full_path) || !IsWithinRoot(FS::SanitizePath(path), full_path)) {
|
||||
return nullptr;
|
||||
}
|
||||
return base.CreateFile(full_path, perms);
|
||||
|
||||
Reference in New Issue
Block a user