47 }
catch (
const std::filesystem::filesystem_error&) {
61 const std::filesystem::path& path,
62 bool allow_symlinks =
false,
63 bool strict_filename =
true
67 std::filesystem::path canonical;
68 if (std::filesystem::exists(path)) {
69 canonical = std::filesystem::canonical(path);
71 canonical = std::filesystem::weakly_canonical(path);
75 if (!allow_symlinks && std::filesystem::exists(path)) {
76 if (std::filesystem::is_symlink(path)) {
79 "Symbolic links are not allowed for security reasons"
85 auto [base_end, path_end] = std::mismatch(
87 canonical.begin(), canonical.end()
93 "Path must be within allowed directory: " +
allowed_base_.string()
98 if (strict_filename && path.has_filename()) {
99 auto filename = path.filename().string();
104 "Filename contains invalid or potentially dangerous characters"
111 }
catch (
const std::filesystem::filesystem_error& e) {
114 std::string(
"Path validation failed: ") + e.what()
139 if (name.empty() || name ==
"." || name ==
"..") {
144 for (
char c : name) {
145 if (!std::isalnum(
static_cast<unsigned char>(c)) &&
146 c !=
'-' && c !=
'_' && c !=
'.') {
161 const std::string& name,
162 char replacement =
'_'
169 return std::string(1, replacement) + name;
172 return std::string(1, replacement) +
'.';
176 result.reserve(name.size());
178 for (
char c : name) {
179 if (std::isalnum(
static_cast<unsigned char>(c)) ||
180 c ==
'-' || c ==
'_' || c ==
'.') {
197 const std::filesystem::path& base,
198 const std::filesystem::path& relative
202 if (relative.is_absolute()) {
205 "Cannot join with absolute path"};
209 auto joined = base / relative;
213 auto validation = validator.
validate(joined);
215 if (validation.is_err()) {
223 }
catch (
const std::filesystem::filesystem_error& e) {
226 std::string(
"Path join failed: ") + e.what()};
Validates file paths to prevent security vulnerabilities.
std::filesystem::path allowed_base_
static bool is_safe_filename(const std::string &name)
Check if a filename is safe (contains only allowed characters)
static std::string sanitize_filename(const std::string &name, char replacement='_')
Sanitize a filename by removing/replacing invalid characters.
common::VoidResult validate(const std::filesystem::path &path, bool allow_symlinks=false, bool strict_filename=true) const
Validate a file path.
const std::filesystem::path & allowed_base() const
Get the allowed base directory.
static result< std::filesystem::path > safe_join(const std::filesystem::path &base, const std::filesystem::path &relative)
Create a safe path by joining base and relative path.
path_validator(std::filesystem::path allowed_base)
Construct with allowed base directory.
Error codes specific to the logger system.
@ path_traversal_detected
common::VoidResult make_logger_void_result(logger_error_code code, const std::string &message="")
std::string get_logger_error_message(const common::VoidResult &result)
Get error message from a VoidResult.
logger_error_code get_logger_error_code(const common::VoidResult &result)