From 95d092e9b38ee9781f144b1a0d18d09b5d4cdd34 Mon Sep 17 00:00:00 2001 From: Jalin Wang Date: Thu, 16 Apr 2026 15:26:15 +0800 Subject: [PATCH] fix: relax collection path restriction (#340) --- .../detail/test_collection_create_and_open.py | 5 ++- src/db/collection.cc | 4 +-- src/db/common/constants.h | 3 -- src/db/common/file_helper.h | 16 ++++++++++ tests/db/collection_test.cc | 32 ++++++------------- 5 files changed, 29 insertions(+), 31 deletions(-) diff --git a/python/tests/detail/test_collection_create_and_open.py b/python/tests/detail/test_collection_create_and_open.py index af175b6..f746f78 100644 --- a/python/tests/detail/test_collection_create_and_open.py +++ b/python/tests/detail/test_collection_create_and_open.py @@ -127,12 +127,11 @@ long_names = [ valid_path_list = [ "/tmp/nonexistent/directory/test_collection", "test/collection/with/slashes", + "test/collection/with/slashes/哈哈", ] invalid_path_list = [ - "invalid:path", + "invalid\0path", "", - "test_collection_with_spaces ", - "test@#$%collection", ] diff --git a/src/db/collection.cc b/src/db/collection.cc index 3414938..bd29436 100644 --- a/src/db/collection.cc +++ b/src/db/collection.cc @@ -1725,9 +1725,9 @@ Status CollectionImpl::create() { if (path_.empty()) { return Status::InvalidArgument("path validate failed: path is empty"); } - if (!std::regex_match(path_, COLLECTION_PATH_REGEX)) { + if (!FileHelper::PathSimpleValidation(path_)) { return Status::InvalidArgument("path validate failed: path[", path_, - "] cannot pass the regex verification"); + "] is not a valid path"); } if (ailego::FileHelper::IsExist(path_.c_str())) { return Status::InvalidArgument("path validate failed: path[", path_, diff --git a/src/db/common/constants.h b/src/db/common/constants.h index 4b5e256..39aa834 100644 --- a/src/db/common/constants.h +++ b/src/db/common/constants.h @@ -46,9 +46,6 @@ const std::regex FIELD_NAME_REGEX("^[a-zA-Z0-9_-]{1,32}$"); const std::regex DOC_PK_REGEX("^[a-zA-Z0-9_!@#$%+=.-]{1,64}$"); -const std::regex COLLECTION_PATH_REGEX( - R"(^(?:[a-zA-Z]:)?[/\\]?(?:[a-zA-Z0-9_.\-]+[/\\])*[a-zA-Z0-9_.\-]+$)"); - constexpr uint32_t kMaxDenseDimSize = 20000; constexpr uint32_t kMaxScalarFieldSize = 1024; diff --git a/src/db/common/file_helper.h b/src/db/common/file_helper.h index ee2408a..7be6090 100644 --- a/src/db/common/file_helper.h +++ b/src/db/common/file_helper.h @@ -231,6 +231,22 @@ class FileHelper { return ailego::FileHelper::FileSize(file_path.c_str()); } + //! Perform a lightweight sanity check on the path string. + //! This only catches obvious invalid input and does NOT guarantee the path + //! is usable. + static bool PathSimpleValidation(const std::string &path) { + if (path.empty()) return false; + + if (path.find('\0') != std::string::npos) return false; + +#ifdef _WIN32 + // Characters forbidden in Windows path components. + if (path.find_first_of("<>\"|?*") != std::string::npos) return false; +#endif + + return true; + } + //! Copy file //! src_file_path and dst_file_path must be the full path //! dst_file_path/.. must exist diff --git a/tests/db/collection_test.cc b/tests/db/collection_test.cc index 112b979..486b740 100644 --- a/tests/db/collection_test.cc +++ b/tests/db/collection_test.cc @@ -224,6 +224,7 @@ TEST_F(CollectionTest, Feature_CreateAndOpen_PathValidate) { ".hidden", "file.txt", "abs_test/nested/path", + "abs test/nested/path", "nested/a/b/c", "_", "-", @@ -242,30 +243,15 @@ TEST_F(CollectionTest, Feature_CreateAndOpen_PathValidate) { } { - std::vector inalid_paths = { - " ", "", - "file name", // space - "file$name", // $ - "a&b", // & - "a|b", // | - "ab", // > - "a\"b", // " - "a'b", // ' - "a;b", // ; - "a?b", // ? - "a*b", // * - "a[b]", // [] - "a{b}", // {} - "a~b", // ~ - "a#b", // # - "a\tb", // tab - "a\nb", // newline - "a\rb", // carriage return + using std::string_literals::operator""s; + std::vector invalid_paths = { + "", + "v\0v"s, // NUL +#if _WIN32 + "v?v"s, +#endif }; - for (auto path : inalid_paths) { - ailego::FileHelper::RemoveDirectory(path.c_str()); - + for (auto path : invalid_paths) { auto result = Collection::CreateAndOpen(path, *schema, options); if (!result.has_value()) { std::cout << result.error().message() << std::endl;