path-util: introduce path_is_safe()

The function is similar to path_is_valid(), but it refuses paths which
contain ".." component.
This commit is contained in:
Yu Watanabe
2021-05-01 02:37:31 +09:00
parent 6636883564
commit 32df2e1447
3 changed files with 40 additions and 22 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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();