From 6d9a6c5cecebfaaecb4c39d002c8e0cdd26c4b85 Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Tue, 3 Sep 2013 07:58:21 +0300 Subject: [PATCH] path: properly resolve relative paths --- src/path.c | 31 ++++++++++--- tests-clar/core/path.c | 98 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 119 insertions(+), 10 deletions(-) diff --git a/src/path.c b/src/path.c index a753a734d..50a990b27 100644 --- a/src/path.c +++ b/src/path.c @@ -646,12 +646,33 @@ int git_path_resolve_relative(git_buf *path, size_t ceiling) /* do nothing with singleton dot */; else if (len == 2 && from[0] == '.' && from[1] == '.') { - while (to > base && to[-1] == '/') to--; - while (to > base && to[-1] != '/') to--; - } + /* error out if trying to up one from a hard base */ + if (to == base && ceiling != 0) { + giterr_set(GITERR_INVALID, + "Cannot strip root component off url"); + return -1; + } + + /* no more path segments to strip, + * use '../' as a new base path */ + if (to == base) { + if (*next == '/') + len++; - else { - if (*next == '/') + if (to != from) + memmove(to, from, len); + + to += len; + /* this is now the base, can't back up from a + * relative prefix */ + base = to; + } else { + /* back up a path segment */ + while (to > base && to[-1] == '/') to--; + while (to > base && to[-1] != '/') to--; + } + } else { + if (*next == '/' && *from != '/') len++; if (to != from) diff --git a/tests-clar/core/path.c b/tests-clar/core/path.c index 407770baa..d35a5bda8 100644 --- a/tests-clar/core/path.c +++ b/tests-clar/core/path.c @@ -446,16 +446,15 @@ void test_core_path__14_apply_relative(void) cl_git_pass(git_path_apply_relative(&p, "../../../../../..")); cl_assert_equal_s("/this/", p.ptr); - cl_git_pass(git_path_apply_relative(&p, "../../../../../")); + cl_git_pass(git_path_apply_relative(&p, "../")); cl_assert_equal_s("/", p.ptr); - cl_git_pass(git_path_apply_relative(&p, "../../../../..")); - cl_assert_equal_s("/", p.ptr); + cl_git_fail(git_path_apply_relative(&p, "../../..")); cl_git_pass(git_buf_sets(&p, "d:/another/test")); - cl_git_pass(git_path_apply_relative(&p, "../../../../..")); + cl_git_pass(git_path_apply_relative(&p, "../..")); cl_assert_equal_s("d:/", p.ptr); cl_git_pass(git_path_apply_relative(&p, "from/here/to/../and/./back/.")); @@ -473,8 +472,97 @@ void test_core_path__14_apply_relative(void) cl_git_pass(git_path_apply_relative(&p, "..")); cl_assert_equal_s("https://my.url.com/full/path/", p.ptr); - cl_git_pass(git_path_apply_relative(&p, "../../../../../")); + cl_git_pass(git_path_apply_relative(&p, "../../../")); cl_assert_equal_s("https://", p.ptr); + + cl_git_pass(git_buf_sets(&p, "../../this/is/relative")); + + cl_git_pass(git_path_apply_relative(&p, "../../preserves/the/prefix")); + cl_assert_equal_s("../../this/preserves/the/prefix", p.ptr); + + cl_git_pass(git_path_apply_relative(&p, "../../../../that")); + cl_assert_equal_s("../../that", p.ptr); + + cl_git_pass(git_path_apply_relative(&p, "../there")); + cl_assert_equal_s("../../there", p.ptr); git_buf_free(&p); } + +static inline void assert_resolve_relative(git_buf *buf, const char *expected, const char *path) +{ + cl_git_pass(git_buf_sets(buf, path)); + cl_git_pass(git_path_resolve_relative(buf, 0)); + cl_assert_equal_s(expected, buf->ptr); +} + +void test_core_path__15_resolve_relative(void) +{ + git_buf buf = GIT_BUF_INIT; + + assert_resolve_relative(&buf, "", ""); + assert_resolve_relative(&buf, "", "."); + assert_resolve_relative(&buf, "", "./"); + assert_resolve_relative(&buf, "..", ".."); + assert_resolve_relative(&buf, "../", "../"); + assert_resolve_relative(&buf, "..", "./.."); + assert_resolve_relative(&buf, "../", "./../"); + assert_resolve_relative(&buf, "../", "../."); + assert_resolve_relative(&buf, "../", ".././"); + assert_resolve_relative(&buf, "../..", "../.."); + assert_resolve_relative(&buf, "../../", "../../"); + + assert_resolve_relative(&buf, "/", "/"); + assert_resolve_relative(&buf, "/", "/."); + + assert_resolve_relative(&buf, "", "a/.."); + assert_resolve_relative(&buf, "", "a/../"); + assert_resolve_relative(&buf, "", "a/../."); + + assert_resolve_relative(&buf, "/a", "/a"); + assert_resolve_relative(&buf, "/a/", "/a/."); + assert_resolve_relative(&buf, "/", "/a/../"); + assert_resolve_relative(&buf, "/", "/a/../."); + assert_resolve_relative(&buf, "/", "/a/.././"); + + assert_resolve_relative(&buf, "a", "a"); + assert_resolve_relative(&buf, "a/", "a/"); + assert_resolve_relative(&buf, "a/", "a/."); + assert_resolve_relative(&buf, "a/", "a/./"); + + assert_resolve_relative(&buf, "a/b", "a//b"); + assert_resolve_relative(&buf, "a/b/c", "a/b/c"); + assert_resolve_relative(&buf, "b/c", "./b/c"); + assert_resolve_relative(&buf, "a/c", "a/./c"); + assert_resolve_relative(&buf, "a/b/", "a/b/."); + + assert_resolve_relative(&buf, "/a/b/c", "///a/b/c"); + assert_resolve_relative(&buf, "/a/b/c", "//a/b/c"); + assert_resolve_relative(&buf, "/", "////"); + assert_resolve_relative(&buf, "/a", "///a"); + assert_resolve_relative(&buf, "/", "///."); + assert_resolve_relative(&buf, "/", "///a/.."); + + assert_resolve_relative(&buf, "../../path", "../../test//../././path"); + assert_resolve_relative(&buf, "../d", "a/b/../../../c/../d"); + + cl_git_pass(git_buf_sets(&buf, "/..")); + cl_git_fail(git_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_buf_sets(&buf, "/./..")); + cl_git_fail(git_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_buf_sets(&buf, "/.//..")); + cl_git_fail(git_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_buf_sets(&buf, "/../.")); + cl_git_fail(git_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_buf_sets(&buf, "/../.././../a")); + cl_git_fail(git_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_buf_sets(&buf, "////..")); + cl_git_fail(git_path_resolve_relative(&buf, 0)); + + git_buf_free(&buf); +} -- 2.39.5