@@ -511,18 +511,22 @@ class PathValidationTest : public ::testing::Test {
511511 // Create a temporary directory for the tests.
512512 base_dir_ = std::filesystem::temp_directory_path () / " PathValidationTest" ;
513513 outside_dir_ = std::filesystem::temp_directory_path () / " outside" ;
514+ whitelisted_dir_ = std::filesystem::temp_directory_path () / " whitelisted" ;
514515 std::filesystem::create_directories (base_dir_);
515516 std::filesystem::create_directories (outside_dir_);
517+ std::filesystem::create_directories (whitelisted_dir_);
516518 }
517519
518520 void TearDown () override {
519521 // Clean up the temporary directory.
520522 std::filesystem::remove_all (base_dir_);
521523 std::filesystem::remove_all (outside_dir_);
524+ std::filesystem::remove_all (whitelisted_dir_);
522525 }
523526
524527 std::filesystem::path base_dir_;
525528 std::filesystem::path outside_dir_;
529+ std::filesystem::path whitelisted_dir_;
526530};
527531
528532// Test cases for ValidateExternalDataPath.
@@ -586,6 +590,85 @@ TEST_F(PathValidationTest, ValidateExternalDataPathWithSymlinkOutside) {
586590 ASSERT_FALSE (utils::ValidateExternalDataPath (base_dir_, " outside_link.bin" ).IsOK ());
587591}
588592
593+ TEST_F (PathValidationTest, ValidateExternalDataPathWithWhitelistedFolder) {
594+ // Path is valid under the whitelisted folder directly.
595+ std::vector<std::filesystem::path> whitelist = {outside_dir_};
596+ ASSERT_STATUS_OK (utils::ValidateExternalDataPath (outside_dir_, " data.bin" , whitelist));
597+ }
598+
599+ TEST_F (PathValidationTest, ValidateExternalDataPathEscapesBaseButMatchesWhitelist) {
600+ std::vector<std::filesystem::path> whitelist = {whitelisted_dir_};
601+ // "data.bin" is valid under base_dir_, no need for whitelist
602+ ASSERT_STATUS_OK (utils::ValidateExternalDataPath (base_dir_, " data.bin" , whitelist));
603+
604+ // Create a subdirectory of whitelisted_dir_ and use that as the whitelisted folder.
605+ auto whitelisted_sub = whitelisted_dir_ / " sub" ;
606+ std::filesystem::create_directories (whitelisted_sub);
607+ std::vector<std::filesystem::path> whitelist2 = {whitelisted_sub};
608+
609+ // "../data.bin" escapes base_dir_ and also escapes whitelisted_sub
610+ ASSERT_FALSE (utils::ValidateExternalDataPath (base_dir_, " ../data.bin" ).IsOK ());
611+ ASSERT_FALSE (utils::ValidateExternalDataPath (base_dir_, " ../data.bin" , whitelist2).IsOK ());
612+ }
613+
614+ TEST_F (PathValidationTest, ValidateExternalDataPathWhitelistSavesEscapingPath) {
615+ // Location "../outside/data.bin" escapes base_dir_ but resolves under outside_dir_.
616+ auto relative_to_outside = std::filesystem::path (" .." ) / " outside" / " data.bin" ;
617+ std::vector<std::filesystem::path> whitelist = {outside_dir_};
618+
619+ // Without whitelist, it should fail.
620+ ASSERT_FALSE (utils::ValidateExternalDataPath (base_dir_, relative_to_outside).IsOK ());
621+
622+ // With whitelist containing outside_dir_, it should succeed because
623+ // outside_dir_ / "../outside/data.bin" resolves under outside_dir_.
624+ ASSERT_STATUS_OK (utils::ValidateExternalDataPath (base_dir_, relative_to_outside, whitelist));
625+ }
626+
627+ TEST_F (PathValidationTest, ValidateExternalDataPathWhitelistDoesNotMatchEither) {
628+ // Location escapes both base_dir and all whitelisted folders.
629+ auto unrelated_dir = std::filesystem::temp_directory_path () / " unrelated_PathValidationTest" ;
630+ std::filesystem::create_directories (unrelated_dir);
631+ auto cleanup = [&]() { std::filesystem::remove_all (unrelated_dir); };
632+
633+ std::vector<std::filesystem::path> whitelist = {whitelisted_dir_};
634+ auto escaping_location = std::filesystem::path (" .." ) / " unrelated_PathValidationTest" / " data.bin" ;
635+ ASSERT_FALSE (utils::ValidateExternalDataPath (base_dir_, escaping_location, whitelist).IsOK ());
636+
637+ cleanup ();
638+ }
639+
640+ TEST_F (PathValidationTest, ValidateExternalDataPathEmptyWhitelist) {
641+ // Empty whitelist should behave the same as no whitelist.
642+ std::vector<std::filesystem::path> empty_whitelist;
643+ ASSERT_STATUS_OK (utils::ValidateExternalDataPath (base_dir_, " data.bin" , empty_whitelist));
644+ ASSERT_FALSE (utils::ValidateExternalDataPath (base_dir_, " ../data.bin" , empty_whitelist).IsOK ());
645+ }
646+
647+ TEST_F (PathValidationTest, ValidateExternalDataPathMultipleWhitelistedFolders) {
648+ // First whitelisted folder doesn't match, second one does.
649+ auto another_dir = std::filesystem::temp_directory_path () / " another_PathValidationTest" ;
650+ std::filesystem::create_directories (another_dir);
651+ auto cleanup = [&]() { std::filesystem::remove_all (another_dir); };
652+
653+ auto relative_to_outside = std::filesystem::path (" .." ) / " outside" / " data.bin" ;
654+ std::vector<std::filesystem::path> whitelist = {another_dir, outside_dir_};
655+
656+ // Escapes base_dir_ but outside_dir_ (second whitelist entry) should match.
657+ ASSERT_STATUS_OK (utils::ValidateExternalDataPath (base_dir_, relative_to_outside, whitelist));
658+
659+ cleanup ();
660+ }
661+
662+ TEST_F (PathValidationTest, ValidateExternalDataPathAbsoluteLocationRejectsEvenWithWhitelist) {
663+ // Absolute paths are always rejected, regardless of whitelist.
664+ std::vector<std::filesystem::path> whitelist = {outside_dir_};
665+ #ifdef _WIN32
666+ ASSERT_FALSE (utils::ValidateExternalDataPath (base_dir_, " C:\\ data.bin" , whitelist).IsOK ());
667+ #else
668+ ASSERT_FALSE (utils::ValidateExternalDataPath (base_dir_, " /data.bin" , whitelist).IsOK ());
669+ #endif
670+ }
671+
589672// Test fixture for ParseWhiteListedPaths tests.
590673class ParseWhiteListedPathsTest : public ::testing::Test {
591674 protected:
@@ -773,5 +856,6 @@ TEST_F(ParseWhiteListedPathsTest, OutParamUnchangedOnError) {
773856 ASSERT_EQ (paths.size (), 1u );
774857 EXPECT_EQ (paths[0 ], sub_dir_a_);
775858}
859+
776860} // namespace test
777861} // namespace onnxruntime
0 commit comments