From 61c2a07a0ce38e268c7bc20871f43ba993c611e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Podg=C3=B3rski?= Date: Sun, 24 Mar 2024 21:58:29 +0100 Subject: [PATCH] Try to fix #123 --- src/zip.c | 46 +++++++++++++-------- test/CMakeLists.txt | 5 +++ test/test_static.c | 98 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 17 deletions(-) create mode 100644 test/test_static.c diff --git a/src/zip.c b/src/zip.c index 252f52f6..b14f9e3b 100644 --- a/src/zip.c +++ b/src/zip.c @@ -248,38 +248,50 @@ static char *zip_strrpl(const char *str, size_t n, char oldchar, char newchar) { return begin; } +static inline int zip_strchr_match(const char *const str, size_t len, char c) { + size_t i; + for (i = 0; i < len; ++i) { + if (str[i] != c) { + return 0; + } + } + + return 1; +} + static char *zip_name_normalize(char *name, char *const nname, size_t len) { - const char *const dot = ".\0"; - const char *const dot2 = "..\0"; - size_t offn = 0; - size_t offnn = 0, ncpy = 0; + size_t offn = 0, ncpy = 0; + char c; if (name == NULL || nname == NULL || len <= 0) { return NULL; } // skip trailing '/' - while (ISSLASH(*name)) + while (ISSLASH(*name)) { name++; + } - for (; offn < len; offn++) { - if (ISSLASH(name[offn])) { - if (ncpy > 0 && strcmp(&nname[offnn], dot) && - strcmp(&nname[offnn], dot2)) { - offnn += ncpy; - nname[offnn++] = name[offn]; // append '/' + while ((c = *name++)) { + if (ISSLASH(c)) { + if (ncpy > 0 && !zip_strchr_match(&nname[offn], ncpy, '.')) { + offn += ncpy; + nname[offn++] = c; // append '/' } ncpy = 0; } else { - nname[offnn + ncpy] = name[offn]; - ncpy++; + nname[offn + ncpy] = c; + if (c) { + ncpy++; + } } } - // at the end, extra check what we've already copied - if (ncpy == 0 || !strcmp(&nname[offnn], dot) || - !strcmp(&nname[offnn], dot2)) { - nname[offnn] = 0; + if (!zip_strchr_match(&nname[offn], ncpy, '.')) { + nname[offn + ncpy] = '\0'; + } else { + nname[offn] = '\0'; } + return nname; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3a8d7cab..fc8bb1df 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,6 +3,11 @@ cmake_minimum_required(VERSION 3.14) find_package(Sanitizers) # tests +set(test_static_out test_static.out) +add_executable(${test_static_out} test_static.c) +add_test(NAME ${test_static_out} COMMAND ${test_static_out}) +add_sanitizers(${test_static_out}) + set(test_write_out test_write.out) add_executable(${test_write_out} test_write.c) target_link_libraries(${test_write_out} zip) diff --git a/test/test_static.c b/test/test_static.c new file mode 100644 index 00000000..ebc70838 --- /dev/null +++ b/test/test_static.c @@ -0,0 +1,98 @@ +#include "minunit.h" + +#ifndef ISSLASH +#define ISSLASH(C) ((C) == '/' || (C) == '\\') +#endif + +static inline int zip_strchr_match(const char *const str, size_t len, char c) { + size_t i; + for (i = 0; i < len; ++i) { + if (str[i] != c) { + return 0; + } + } + + return 1; +} + +static char *zip_name_normalize(char *name, char *const nname, size_t len) { + size_t offn = 0, ncpy = 0; + char c; + + if (name == NULL || nname == NULL || len <= 0) { + return NULL; + } + // skip trailing '/' + while (ISSLASH(*name)) { + name++; + } + + while ((c = *name++)) { + if (ISSLASH(c)) { + if (ncpy > 0 && !zip_strchr_match(&nname[offn], ncpy, '.')) { + offn += ncpy; + nname[offn++] = c; // append '/' + } + ncpy = 0; + } else { + nname[offn + ncpy] = c; + if (c) { + ncpy++; + } + } + } + + if (!zip_strchr_match(&nname[offn], ncpy, '.')) { + nname[offn + ncpy] = '\0'; + } else { + nname[offn] = '\0'; + } + + return nname; +} + +void test_setup(void) {} + +void test_teardown(void) {} + +MU_TEST(test_normalize) { + char nname[512] = {0}; + char *name = + "/../../../../../../../../../../../../../../../../../../../../../../../" + "../../../../../../../../../../../../../../../../../tmp/evil.txt"; + size_t len = strlen(name); + mu_assert_int_eq( + 0, strcmp(zip_name_normalize(name, nname, len), "tmp/evil.txt\0")); + + name = "../.ala/ala/...c.../../"; + len = strlen(name); + mu_assert_int_eq( + 0, strcmp(zip_name_normalize(name, nname, len), ".ala/ala/...c.../\0")); + + name = "../evil.txt/.al"; + len = strlen(name); + mu_assert_int_eq( + 0, strcmp(zip_name_normalize(name, nname, len), "evil.txt/.al\0")); + + name = "/.././.../a..../..../..a./.aaaa"; + len = strlen(name); + mu_assert_int_eq( + 0, strcmp(zip_name_normalize(name, nname, len), "a..../..a./.aaaa\0")); +} + +MU_TEST_SUITE(test_static_suite) { + MU_SUITE_CONFIGURE(&test_setup, &test_teardown); + + MU_RUN_TEST(test_normalize); +} + +#define UNUSED(x) (void)x + +int main(int argc, char *argv[]) { + UNUSED(argc); + UNUSED(argv); + + MU_RUN_SUITE(test_static_suite); + MU_REPORT(); + return MU_EXIT_CODE; +}