57 const std::filesystem::path& path,
58 const std::filesystem::path& allowed_base =
""
62 std::string path_str = path.string();
65 if (path_str.find(
"..") != std::string::npos) {
68 "Path contains '..' (path traversal attempt)"
73 if (!allowed_base.empty()) {
75 std::filesystem::path canonical_path;
76 std::filesystem::path canonical_base;
79 if (std::filesystem::exists(allowed_base)) {
80 canonical_base = std::filesystem::canonical(allowed_base);
82 canonical_base = std::filesystem::absolute(allowed_base);
86 std::filesystem::path parent = path.parent_path();
87 if (!parent.empty() && std::filesystem::exists(parent)) {
88 canonical_path = std::filesystem::canonical(parent) / path.filename();
90 canonical_path = std::filesystem::absolute(path);
94 auto [base_end, path_end] = std::mismatch(
95 canonical_base.begin(), canonical_base.end(),
96 canonical_path.begin(), canonical_path.end()
99 if (base_end != canonical_base.end()) {
102 "Path is outside allowed directory: " + path_str
108 }
catch (
const std::filesystem::filesystem_error& e) {
111 std::string(
"Path validation failed: ") + e.what()
280 if (!std::filesystem::exists(path, ec)) {
281 auto parent = path.parent_path();
282 if (parent.empty()) {
285 return std::filesystem::exists(parent, ec) &&
290 auto status = std::filesystem::status(path, ec);
296 auto perms = status.permissions();
297 return (perms & std::filesystem::perms::owner_write) != std::filesystem::perms::none;
312 const std::string& prefix =
"temp",
313 const std::string& extension =
".tmp"
315 auto now = std::chrono::system_clock::now();
316 auto time_t = std::chrono::system_clock::to_time_t(now);
320 localtime_s(&tm_buf, &time_t);
322 localtime_r(&time_t, &tm_buf);
327 std::snprintf(buffer,
sizeof(buffer),
328 "%s_%04d%02d%02d%02d%02d%02d_%d%s",
330 tm_buf.tm_year + 1900,
336 static_cast<int>(std::chrono::steady_clock::now().time_since_epoch().count() % 10000),
339 return std::string(buffer);