From ad0055e67c96cb6425ce26e2d8dbfddb86833942 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Sat, 14 Feb 2026 14:58:10 -0500 Subject: [PATCH] libutil: fix `isInDir` rejecting paths starting with dot The old check rejected any relative path whose first character was a dot, producing false negatives for valid descendants like `.ssh` or `.config`. This commit changes the logic such that now it inspects the first path component via `path::begin()`, only rejects `.` and `..` rather than anything dot-prefixed. Fixes #15207. --- src/libutil-tests/file-system.cc | 34 ++++++++++++++++++++++++++------ src/libutil/file-system.cc | 8 +++++--- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/libutil-tests/file-system.cc b/src/libutil-tests/file-system.cc index 34402cb1bf5..e96694ff7c6 100644 --- a/src/libutil-tests/file-system.cc +++ b/src/libutil-tests/file-system.cc @@ -196,20 +196,42 @@ TEST(baseNameOf, absoluteNothingSlashNothing) TEST(isInDir, trivialCase) { - auto p1 = isInDir("/foo/bar", "/foo"); - ASSERT_EQ(p1, true); + EXPECT_TRUE(isInDir("/foo/bar", "/foo")); } TEST(isInDir, notInDir) { - auto p1 = isInDir("/zes/foo/bar", "/foo"); - ASSERT_EQ(p1, false); + EXPECT_FALSE(isInDir("/zes/foo/bar", "/foo")); } TEST(isInDir, emptyDir) { - auto p1 = isInDir("/zes/foo/bar", ""); - ASSERT_EQ(p1, false); + EXPECT_FALSE(isInDir("/zes/foo/bar", "")); +} + +TEST(isInDir, hiddenSubdirectory) +{ + EXPECT_TRUE(isInDir("/foo/.ssh", "/foo")); +} + +TEST(isInDir, ellipsisEntry) +{ + EXPECT_TRUE(isInDir("/foo/...", "/foo")); +} + +TEST(isInDir, sameDir) +{ + EXPECT_FALSE(isInDir("/foo", "/foo")); +} + +TEST(isInDir, sameDirDot) +{ + EXPECT_FALSE(isInDir("/foo/.", "/foo")); +} + +TEST(isInDir, dotDotPrefix) +{ + EXPECT_FALSE(isInDir("/foo/../bar", "/foo")); } /* ---------------------------------------------------------------------------- diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 249b0692eb2..2f671627ff9 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -183,9 +183,11 @@ bool isInDir(const std::filesystem::path & path, const std::filesystem::path & d /* Note that while the standard doesn't guarantee this, the `lexically_*` functions should do no IO and not throw. */ auto rel = path.lexically_relative(dir); - /* Method from - https://stackoverflow.com/questions/62503197/check-if-path-contains-another-in-c++ */ - return !rel.empty() && rel.native()[0] != OS_STR('.'); + if (rel.empty()) + return false; + + auto first = *rel.begin(); + return first != "." && first != ".."; } bool isDirOrInDir(const std::filesystem::path & path, const std::filesystem::path & dir)