mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-14 00:52:04 +00:00
Security fix: path traversal in 3MF import (#12860)
This commit is contained in:
@@ -100,6 +100,45 @@ struct ZipUnicodePathExtraField
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Validate that a relative file path does not escape the root directory via path traversal.
|
||||||
|
static bool is_path_within_root(const std::string& file_path, const boost::filesystem::path& root)
|
||||||
|
{
|
||||||
|
if (file_path.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boost::filesystem::path p(file_path);
|
||||||
|
if (p.is_absolute())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Reject any path component that is ".."
|
||||||
|
for (const auto& component : p) {
|
||||||
|
if (component == "..")
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the full path and verify it starts with the canonical root (also catches symlink escapes)
|
||||||
|
try {
|
||||||
|
boost::filesystem::path full_path = root / p;
|
||||||
|
boost::filesystem::path canonical_root = boost::filesystem::weakly_canonical(root);
|
||||||
|
boost::filesystem::path canonical_full = boost::filesystem::weakly_canonical(full_path);
|
||||||
|
|
||||||
|
auto root_str = canonical_root.string();
|
||||||
|
auto full_str = canonical_full.string();
|
||||||
|
if (full_str.length() < root_str.length())
|
||||||
|
return false;
|
||||||
|
if (full_str.compare(0, root_str.length(), root_str) != 0)
|
||||||
|
return false;
|
||||||
|
// Ensure it's a proper prefix (not just a substring of a longer directory name)
|
||||||
|
if (full_str.length() > root_str.length() &&
|
||||||
|
full_str[root_str.length()] != boost::filesystem::path::preferred_separator)
|
||||||
|
return false;
|
||||||
|
} catch (const boost::filesystem::filesystem_error&) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// VERSION NUMBERS
|
// VERSION NUMBERS
|
||||||
// 0 : .3mf, files saved by older slic3r or other applications. No version definition in them.
|
// 0 : .3mf, files saved by older slic3r or other applications. No version definition in them.
|
||||||
// 1 : Introduction of 3mf versioning. No other change in data saved into 3mf files.
|
// 1 : Introduction of 3mf versioning. No other change in data saved into 3mf files.
|
||||||
@@ -1774,8 +1813,11 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("extract %1%th file %2%, total=%3%")%(i+1)%name%num_entries;
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("extract %1%th file %2%, total=%3%")%(i+1)%name%num_entries;
|
||||||
|
|
||||||
if (name.find("/../") != std::string::npos) {
|
if (name.find("/../") != std::string::npos ||
|
||||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", find file path including /../, not valid, skip it\n");
|
name.find("../") == 0 ||
|
||||||
|
(name.length() >= 3 && name.compare(name.length() - 3, 3, "/..") == 0) ||
|
||||||
|
name == "..") {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", find file path including path traversal, not valid, skip it\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2649,6 +2691,11 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!is_path_within_root(dest_file, dir)) {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", path traversal detected in auxiliary file: %1%, skipping") % dest_file;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (dest_file.find('/') != std::string::npos) {
|
if (dest_file.find('/') != std::string::npos) {
|
||||||
boost::filesystem::path src_path = boost::filesystem::path(dest_file);
|
boost::filesystem::path src_path = boost::filesystem::path(dest_file);
|
||||||
boost::filesystem::path parent_path = src_path.parent_path();
|
boost::filesystem::path parent_path = src_path.parent_path();
|
||||||
@@ -2672,6 +2719,13 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
{
|
{
|
||||||
if (stat.m_uncomp_size > 0) {
|
if (stat.m_uncomp_size > 0) {
|
||||||
std::string src_file = decode_path(stat.m_filename);
|
std::string src_file = decode_path(stat.m_filename);
|
||||||
|
|
||||||
|
boost::filesystem::path backup_root(m_backup_path);
|
||||||
|
if (!is_path_within_root(src_file, backup_root)) {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", path traversal detected in file: %1%, skipping") % src_file;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// BBS: use backup path
|
// BBS: use backup path
|
||||||
//aux directory from model
|
//aux directory from model
|
||||||
boost::filesystem::path dest_path = boost::filesystem::path(m_backup_path + "/" + src_file);
|
boost::filesystem::path dest_path = boost::filesystem::path(m_backup_path + "/" + src_file);
|
||||||
|
|||||||
Reference in New Issue
Block a user