#include "path_sanitizer.h" #include #include #include #include #include namespace fetchml::common { bool canonicalize_and_validate(const char* path, char* out_canonical, size_t out_size) { if (!path || !out_canonical || out_size == 0) { return false; } // Use realpath to canonicalize (resolves symlinks, removes .., etc.) char* resolved = realpath(path, nullptr); if (!resolved) { return false; } // Check size size_t len = strlen(resolved); if (len >= out_size) { free(resolved); return false; } // Copy to output memcpy(out_canonical, resolved, len + 1); free(resolved); // Additional validation: ensure no embedded nulls or control chars for (size_t i = 0; i < len; i++) { if (out_canonical[i] == '\0' || (unsigned char)out_canonical[i] < 32) { return false; } } return true; } int open_dir_nofollow(const char* path) { if (!path) return -1; // Open with O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC // O_NOFOLLOW ensures we don't follow symlinks // O_DIRECTORY ensures it's actually a directory return open(path, O_DIRECTORY | O_NOFOLLOW | O_RDONLY | O_CLOEXEC); } int openat_nofollow(int dir_fd, const char* filename, int flags, int mode) { if (dir_fd < 0 || !filename) return -1; // Use O_NOFOLLOW to prevent symlink attacks // Use openat to open relative to directory fd return openat(dir_fd, filename, flags | O_NOFOLLOW | O_CLOEXEC, mode); } } // namespace fetchml::common