]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3831] Backported
authorFrancis Dupont <fdupont@isc.org>
Mon, 19 May 2025 22:20:07 +0000 (00:20 +0200)
committerFrancis Dupont <fdupont@isc.org>
Mon, 19 May 2025 22:20:07 +0000 (00:20 +0200)
src/lib/util/filesystem.cc
src/lib/util/filesystem.h
src/lib/util/tests/filesystem_unittests.cc

index cf7a506f2a7c1c718c272cc12abcfa1d366b4e1b..ecfe29d3e6a954daf797edceca50e4dfa5589943 100644 (file)
@@ -14,7 +14,6 @@
 #include <cstdlib>
 #include <fstream>
 #include <string>
-#include <filesystem>
 #include <iostream>
 
 #include <dirent.h>
@@ -289,6 +288,31 @@ PathChecker::validatePath(const std::string input_path_str,
     return (valid_path);
 }
 
+std::string
+PathChecker::validateDirectory(const std::string input_path_str,
+                               bool enforce_path /* = true */) const {
+    std::string input_copy = trim(input_path_str);
+    if (!enforce_path) {
+        return(input_copy);
+    }
+
+    // We only allow absolute path equal to default. Catch an invalid path.
+    if (!input_path_str.empty()) {
+        std::string input_copy = input_path_str;
+        while (!input_copy.empty() && input_copy.back() == '/') {
+               input_copy.pop_back();
+        }
+
+        if (input_copy != path_) {
+            isc_throw(BadValue, "invalid path specified: '"
+                      << input_path_str << "', supported path is '"
+                      << path_ << "'");
+        }
+    }
+
+    return (path_);
+}
+
 bool
 PathChecker::pathHasPermissions(mode_t permissions) {
     return(hasPermissions(path_, permissions));
index 45be0c76a0b6439dfdfe3006fe8f69484cf5acd4..40862d4b78f12a30fe29b12a36a24d18d4094760 100644 (file)
@@ -70,7 +70,7 @@ isFile(const std::string& path);
 void
 setUmask();
 
-/// \brief Paths on a filesystem
+/// @brief Paths on a filesystem
 struct Path {
     /// @brief Constructor
     ///
@@ -217,6 +217,26 @@ public:
     std::string validatePath(const std::string input_path_str,
                              bool enforce_path = true) const;
 
+    /// @brief Validates a directory against a supported path.
+    ///
+    /// Used to validate a string that represents a directory that may or
+    /// may not end with a "/" (i.e "/foo/bar", bar is assumed to be the
+    /// a directory, not a file.
+    ///
+    /// If the input is empty or it matches the supported path, it returns
+    /// the supported path.  Otherwise it throws an error.
+    ///
+    /// @param input_path_str file path to validate.
+    /// @param enforce_path enables validation against the supported path. If
+    /// it simply returns the supported path.
+    ///
+    /// @return validated path
+    ///
+    /// @throw BadValue if the input directory does not match the supported
+    /// path.
+    std::string validateDirectory(const std::string input_path_str,
+                                  bool enforce_path = true) const;
+
     /// @brief Tests that the supported path has the given permissions.
     ///
     /// @param permissions mode_t mask of required permissions.
index 7da8d3cfd501ddea7799170bb039c13f237ff63b..22de01e5e3d16be86686bcd76e45a7f3b80c3554 100644 (file)
@@ -13,6 +13,7 @@
 #include <fstream>
 #include <list>
 #include <string>
+#include <cstdlib>
 
 #include <gtest/gtest.h>
 
@@ -434,4 +435,139 @@ TEST_F(FileUtilTest, hasPermissions) {
     EXPECT_FALSE(hasPermissions(path, current_permissions));
 }
 
+// Verifies PathChecker::validateDirectory() when enforce_path is true.
+TEST(PathChecker, validateDirectoryEnforcePath) {
+    std::string def_path(TEST_DATA_BUILDDIR);
+    struct Scenario {
+        int line_;
+        std::string lib_path_;
+        std::string exp_path_;
+        std::string exp_error_;
+    };
+
+    std::list<Scenario> scenarios = {
+    {
+        // Invalid path with slash.
+        __LINE__,
+        "/var/lib/bs/",
+        "",
+        string("invalid path specified: '/var/lib/bs/', supported path is '" + def_path + "'")
+    },
+    {
+        // Invalid path without slash.
+        __LINE__,
+        "/var/lib/bs",
+        "",
+        string("invalid path specified: '/var/lib/bs', supported path is '" + def_path + "'")
+    },
+    {
+        // File name is just another bad path.
+        __LINE__,
+        "mylib.so",
+        "",
+        string("invalid path specified: 'mylib.so', supported path is '" + def_path + "'")
+    },
+    {
+        // Valid full path with slash.
+        __LINE__,
+        def_path + "/",
+        def_path,
+        ""
+    },
+    {
+        // Valid full path without slash.
+        __LINE__,
+        def_path,
+        def_path,
+        ""
+    },
+    {
+        // Invalid relative path.
+        __LINE__,
+        "../kea/",
+        "",
+        string("invalid path specified: '../kea/', supported path is '" +
+               def_path + "'")
+    }
+    };
+
+    // Create a PathChecker with a supported path of def_path.
+    PathChecker checker(def_path);
+    for (auto scenario : scenarios) {
+        std::ostringstream oss;
+        oss << " Scenario at line: " << scenario.line_;
+        SCOPED_TRACE(oss.str());
+        std::string validated_path;
+        if (scenario.exp_error_.empty()) {
+            ASSERT_NO_THROW_LOG(validated_path =
+                                checker.validateDirectory(scenario.lib_path_));
+            EXPECT_EQ(validated_path, scenario.exp_path_);
+        } else {
+            ASSERT_THROW_MSG(validated_path =
+                             checker.validateDirectory(scenario.lib_path_),
+                             BadValue, scenario.exp_error_);
+        }
+    }
+}
+
+// Verifies PathChecker::validateDirectory() when enforce_path is false.
+TEST(PathChecker, validateDirectoryEnforcePathFalse) {
+    std::string def_path(TEST_DATA_BUILDDIR);
+    struct Scenario {
+        int line_;
+        std::string lib_path_;
+        std::string exp_path_;
+        std::string exp_error_;
+    };
+
+    std::list<Scenario> scenarios = {
+    {
+        // Invalid parent path but shouldn't care.
+        __LINE__,
+        "/var/lib/bs/",
+        "/var/lib/bs/",
+        ""
+    },
+    {
+        // File name only is valid.
+        __LINE__,
+        "mylib.so",
+        "mylib.so",
+        ""
+    },
+    {
+        // Valid full path.
+        __LINE__,
+        def_path + "/",
+        def_path + "/",
+        ""
+    },
+    {
+        // White space for file name.
+        __LINE__,
+        "      ",
+        "",
+        ""
+    }
+    };
+
+    // Create a PathChecker with a supported path of def_path.
+    PathChecker checker(def_path);
+    for (auto scenario : scenarios) {
+        std::ostringstream oss;
+        oss << " Scenario at line: " << scenario.line_;
+        SCOPED_TRACE(oss.str());
+        std::string validated_path;
+        if (scenario.exp_error_.empty()) {
+            ASSERT_NO_THROW_LOG(validated_path =
+                                checker.validateDirectory(scenario.lib_path_, false));
+            EXPECT_EQ(validated_path, scenario.exp_path_);
+        } else {
+            ASSERT_THROW_MSG(validated_path =
+                             checker.validateDirectory(scenario.lib_path_, false),
+                             BadValue, scenario.exp_error_);
+        }
+    }
+}
+
 }  // namespace