From 32df2e1447bb74ee20cff33ae7ff8aed080b8a91 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 1 May 2021 02:37:31 +0900 Subject: [PATCH] path-util: introduce path_is_safe() The function is similar to path_is_valid(), but it refuses paths which contain ".." component. --- src/basic/path-util.c | 4 ++-- src/basic/path-util.h | 8 ++++++- src/test/test-path-util.c | 50 ++++++++++++++++++++++++--------------- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 752590bf70a..48535da7256 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -1037,14 +1037,14 @@ bool filename_is_valid(const char *p) { return true; } -bool path_is_valid(const char *p) { +bool path_is_valid_full(const char *p, bool accept_dot_dot) { if (isempty(p)) return false; for (const char *e = p;;) { int r; - r = path_find_first_component(&e, /* accept_dot_dot= */ true, NULL); + r = path_find_first_component(&e, accept_dot_dot, NULL); if (r < 0) return false; diff --git a/src/basic/path-util.h b/src/basic/path-util.h index d83a1effdae..52ceb9b5a95 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -155,7 +155,13 @@ int path_extract_filename(const char *p, char **ret); int path_extract_directory(const char *p, char **ret); bool filename_is_valid(const char *p) _pure_; -bool path_is_valid(const char *p) _pure_; +bool path_is_valid_full(const char *p, bool accept_dot_dot) _pure_; +static inline bool path_is_valid(const char *p) { + return path_is_valid_full(p, true); +} +static inline bool path_is_safe(const char *p) { + return path_is_valid_full(p, false); +} bool path_is_normalized(const char *p) _pure_; char *file_in_same_dir(const char *path, const char *filename); diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index bb175145746..c68603fc9ba 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -806,36 +806,48 @@ static void test_filename_is_valid(void) { assert_se(filename_is_valid("o.o")); } -static void test_path_is_valid(void) { +static void test_path_is_valid_and_safe_one(const char *p, bool ret) { + log_debug("/* %s(\"%s\")*/", __func__, strnull(p)); + + assert_se(path_is_valid(p) == ret); + if (ret) + ret = !streq(p, "..") && + !startswith(p, "../") && + !endswith(p, "/..") && + !strstr(p, "/../"); + assert_se(path_is_safe(p) == ret); +} + +static void test_path_is_valid_and_safe(void) { char foo[PATH_MAX+2]; const char *c; log_info("/* %s */", __func__); - assert_se(!path_is_valid("")); - assert_se(path_is_valid("/bar/foo")); - assert_se(path_is_valid("/bar/foo/")); - assert_se(path_is_valid("/bar/foo/")); - assert_se(path_is_valid("//bar//foo//")); - assert_se(path_is_valid("/")); - assert_se(path_is_valid("/////")); - assert_se(path_is_valid("/////.///.////...///..//.")); - assert_se(path_is_valid(".")); - assert_se(path_is_valid("..")); - assert_se(path_is_valid("bar/foo")); - assert_se(path_is_valid("bar/foo/")); - assert_se(path_is_valid("bar//")); + test_path_is_valid_and_safe_one("", false); + test_path_is_valid_and_safe_one("/bar/foo", true); + test_path_is_valid_and_safe_one("/bar/foo/", true); + test_path_is_valid_and_safe_one("/bar/foo/", true); + test_path_is_valid_and_safe_one("//bar//foo//", true); + test_path_is_valid_and_safe_one("/", true); + test_path_is_valid_and_safe_one("/////", true); + test_path_is_valid_and_safe_one("/////.///.////...///..//.", true); + test_path_is_valid_and_safe_one(".", true); + test_path_is_valid_and_safe_one("..", true); + test_path_is_valid_and_safe_one("bar/foo", true); + test_path_is_valid_and_safe_one("bar/foo/", true); + test_path_is_valid_and_safe_one("bar//", true); memset(foo, 'a', sizeof(foo) -1); char_array_0(foo); - assert_se(!path_is_valid(foo)); + test_path_is_valid_and_safe_one(foo, false); c = strjoina("/xxx/", foo, "/yyy"); - assert_se(!path_is_valid(c)); + test_path_is_valid_and_safe_one(c, false); - assert_se(path_is_valid("foo_bar-333")); - assert_se(path_is_valid("o.o")); + test_path_is_valid_and_safe_one("foo_bar-333", true); + test_path_is_valid_and_safe_one("o.o", true); } static void test_hidden_or_backup_file(void) { @@ -983,7 +995,7 @@ int main(int argc, char **argv) { test_path_extract_filename(); test_path_extract_directory(); test_filename_is_valid(); - test_path_is_valid(); + test_path_is_valid_and_safe(); test_hidden_or_backup_file(); test_skip_dev_prefix(); test_empty_or_root(); -- 2.47.3