Hello community, here is the log from the commit of package libgit2 for openSUSE:Leap:15.2 checked in at 2020-03-01 08:51:11 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Leap:15.2/libgit2 (Old) and /work/SRC/openSUSE:Leap:15.2/.libgit2.new.26092 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libgit2" Sun Mar 1 08:51:11 2020 rev:25 rq:779603 version:0.28.4 Changes: -------- --- /work/SRC/openSUSE:Leap:15.2/libgit2/libgit2.changes 2020-01-30 14:47:40.814815658 +0100 +++ /work/SRC/openSUSE:Leap:15.2/.libgit2.new.26092/libgit2.changes 2020-03-01 08:51:29.277286865 +0100 @@ -1,0 +2,62 @@ +Wed Dec 11 15:58:48 UTC 2019 - Andreas Stieger <[email protected]> + +- libgit2 0.28.4: + * CVE-2019-1348: the fast-import stream command "feature + export-marks=path" allows writing to arbitrary file paths. As + libgit2 does not offer any interface for fast-import, it is not + susceptible to this vulnerability. (boo#1158785) + * CVE-2019-1349: by using NTFS 8.3 short names, backslashes or + alternate filesystreams, it is possible to cause submodules to + be written into pre-existing directories during a recursive + clone using git. As libgit2 rejects cloning into non-empty + directories by default, it is not susceptible to this + vulnerability. (boo#1158787) + * CVE-2019-1350: recursive clones may lead to arbitrary remote + code executing due to improper quoting of command line + arguments. As libgit2 uses libssh2, which does not require us + to perform command line parsing, it is not susceptible to this + vulnerability. (boo#1158788) + * CVE-2019-1351: Windows provides the ability to substitute + drive letters with arbitrary letters, including multi-byte + Unicode letters. To fix any potential issues arising from + interpreting such paths as relative paths, we have extended + detection of DOS drive prefixes to accomodate for such cases. + (boo#1158790) + * CVE-2019-1352: by using NTFS-style alternative file streams for + the ".git" directory, it is possible to overwrite parts of the + repository. While this has been fixed in the past for Windows, + the same vulnerability may also exist on other systems that + write to NTFS filesystems. We now reject any paths starting + with ".git:" on all systems. (boo#1158790) + * CVE-2019-1353: by using NTFS-style 8.3 short names, it was + possible to write to the ".git" directory and thus overwrite + parts of the repository, leading to possible remote code + execution. While this problem was already fixed in the past for + Windows, other systems accessing NTFS filesystems are + vulnerable to this issue too. We now enable NTFS protecions by + default on all systems to fix this attack vector. (boo#1158791) + * CVE-2019-1354: on Windows, backslashes are not a valid part of + a filename but are instead interpreted as directory separators. + As other platforms allowed to use such paths, it was possible + to write such invalid entries into a Git repository and was + thus an attack vector to write into the ".git" dierctory. We + now reject any entries starting with ".git" on all systems. + (boo#1158792) + * CVE-2019-1387: it is possible to let a submodule's git + directory point into a sibling's submodule directory, which may + result in overwriting parts of the Git repository and thus lead + to arbitrary command execution. As libgit2 doesn't provide any + way to do submodule clones natively, it is not susceptible to + this vulnerability. Users of libgit2 that have implemented + recursive submodule clones manually are encouraged to review + their implementation for this vulnerability. (boo#1158793) + +------------------------------------------------------------------- +Wed Dec 11 13:30:43 UTC 2019 - Andreas Stieger <[email protected]> + +- libgit2 0.28.3: + * A carefully constructed commit object with a very large number + of parents may have lead to out-of-bounds writes or potential + denial of service (boo#1158981) + +------------------------------------------------------------------- Old: ---- libgit2-0.28.2.tar.gz New: ---- libgit2-0.28.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libgit2.spec ++++++ --- /var/tmp/diff_new_pack.PlWq7V/_old 2020-03-01 08:51:30.157288615 +0100 +++ /var/tmp/diff_new_pack.PlWq7V/_new 2020-03-01 08:51:30.161288623 +0100 @@ -19,7 +19,7 @@ %define sover 28 Name: libgit2 -Version: 0.28.2 +Version: 0.28.4 Release: 0 Summary: C git library License: GPL-2.0 WITH GCC-exception-2.0 ++++++ libgit2-0.28.2.tar.gz -> libgit2-0.28.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-0.28.2/docs/changelog.md new/libgit2-0.28.4/docs/changelog.md --- old/libgit2-0.28.2/docs/changelog.md 2019-05-21 09:54:19.000000000 +0200 +++ new/libgit2-0.28.4/docs/changelog.md 2019-12-10 20:39:58.000000000 +0100 @@ -1,3 +1,78 @@ +v0.28.4 +-------- + +This is a security release fixing the following issues: + +- CVE-2019-1348: the fast-import stream command "feature + export-marks=path" allows writing to arbitrary file paths. As + libgit2 does not offer any interface for fast-import, it is not + susceptible to this vulnerability. + +- CVE-2019-1349: by using NTFS 8.3 short names, backslashes or + alternate filesystreams, it is possible to cause submodules to + be written into pre-existing directories during a recursive + clone using git. As libgit2 rejects cloning into non-empty + directories by default, it is not susceptible to this + vulnerability. + +- CVE-2019-1350: recursive clones may lead to arbitrary remote + code executing due to improper quoting of command line + arguments. As libgit2 uses libssh2, which does not require us + to perform command line parsing, it is not susceptible to this + vulnerability. + +- CVE-2019-1351: Windows provides the ability to substitute + drive letters with arbitrary letters, including multi-byte + Unicode letters. To fix any potential issues arising from + interpreting such paths as relative paths, we have extended + detection of DOS drive prefixes to accomodate for such cases. + +- CVE-2019-1352: by using NTFS-style alternative file streams for + the ".git" directory, it is possible to overwrite parts of the + repository. While this has been fixed in the past for Windows, + the same vulnerability may also exist on other systems that + write to NTFS filesystems. We now reject any paths starting + with ".git:" on all systems. + +- CVE-2019-1353: by using NTFS-style 8.3 short names, it was + possible to write to the ".git" directory and thus overwrite + parts of the repository, leading to possible remote code + execution. While this problem was already fixed in the past for + Windows, other systems accessing NTFS filesystems are + vulnerable to this issue too. We now enable NTFS protecions by + default on all systems to fix this attack vector. + +- CVE-2019-1354: on Windows, backslashes are not a valid part of + a filename but are instead interpreted as directory separators. + As other platforms allowed to use such paths, it was possible + to write such invalid entries into a Git repository and was + thus an attack vector to write into the ".git" dierctory. We + now reject any entries starting with ".git\" on all systems. + +- CVE-2019-1387: it is possible to let a submodule's git + directory point into a sibling's submodule directory, which may + result in overwriting parts of the Git repository and thus lead + to arbitrary command execution. As libgit2 doesn't provide any + way to do submodule clones natively, it is not susceptible to + this vulnerability. Users of libgit2 that have implemented + recursive submodule clones manually are encouraged to review + their implementation for this vulnerability. + +v0.28.3 +------- + +This is a security release fixing the following issues: + +* A carefully constructed commit object with a very large number + of parents may lead to potential out-of-bounds writes or + potential denial of service. + +* The ProgramData configuration file is always read for compatibility + with Git for Windows and Portable Git installations. The ProgramData + location is not necessarily writable only by administrators, so we + now ensure that the configuration file is owned by the administrator + or the current user. + v0.28.2 ------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-0.28.2/include/git2/version.h new/libgit2-0.28.4/include/git2/version.h --- old/libgit2-0.28.2/include/git2/version.h 2019-05-21 09:54:19.000000000 +0200 +++ new/libgit2-0.28.4/include/git2/version.h 2019-12-10 20:39:58.000000000 +0100 @@ -7,10 +7,10 @@ #ifndef INCLUDE_git_version_h__ #define INCLUDE_git_version_h__ -#define LIBGIT2_VERSION "0.28.2" +#define LIBGIT2_VERSION "0.28.4" #define LIBGIT2_VER_MAJOR 0 #define LIBGIT2_VER_MINOR 28 -#define LIBGIT2_VER_REVISION 2 +#define LIBGIT2_VER_REVISION 4 #define LIBGIT2_VER_PATCH 0 #define LIBGIT2_SOVERSION 28 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-0.28.2/src/commit_list.c new/libgit2-0.28.4/src/commit_list.c --- old/libgit2-0.28.2/src/commit_list.c 2019-05-21 09:54:19.000000000 +0200 +++ new/libgit2-0.28.4/src/commit_list.c 2019-12-10 20:39:58.000000000 +0100 @@ -69,11 +69,15 @@ static git_commit_list_node **alloc_parents( git_revwalk *walk, git_commit_list_node *commit, size_t n_parents) { + size_t bytes; + if (n_parents <= PARENTS_PER_COMMIT) return (git_commit_list_node **)((char *)commit + sizeof(git_commit_list_node)); - return (git_commit_list_node **)git_pool_malloc( - &walk->commit_pool, (uint32_t)(n_parents * sizeof(git_commit_list_node *))); + if (git__multiply_sizet_overflow(&bytes, n_parents, sizeof(git_commit_list_node *))) + return NULL; + + return (git_commit_list_node **)git_pool_malloc(&walk->commit_pool, bytes); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-0.28.2/src/config.c new/libgit2-0.28.4/src/config.c --- old/libgit2-0.28.2/src/config.c 2019-05-21 09:54:19.000000000 +0200 +++ new/libgit2-0.28.4/src/config.c 2019-12-10 20:39:58.000000000 +0100 @@ -1111,8 +1111,15 @@ int git_config_find_programdata(git_buf *path) { + int ret; + git_buf_sanitize(path); - return git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA); + ret = git_sysdir_find_programdata_file(path, + GIT_CONFIG_FILENAME_PROGRAMDATA); + if (ret != GIT_OK) + return ret; + + return git_path_validate_system_file_ownership(path->ptr); } int git_config__global_location(git_buf *buf) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-0.28.2/src/path.c new/libgit2-0.28.4/src/path.c --- old/libgit2-0.28.2/src/path.c 2019-05-21 09:54:19.000000000 +0200 +++ new/libgit2-0.28.4/src/path.c 2019-12-10 20:39:58.000000000 +0100 @@ -14,13 +14,36 @@ #include "win32/w32_buffer.h" #include "win32/w32_util.h" #include "win32/version.h" +#include <AclAPI.h> #else #include <dirent.h> #endif #include <stdio.h> #include <ctype.h> -#define LOOKS_LIKE_DRIVE_PREFIX(S) (git__isalpha((S)[0]) && (S)[1] == ':') +static int dos_drive_prefix_length(const char *path) +{ + int i; + + /* + * Does it start with an ASCII letter (i.e. highest bit not set), + * followed by a colon? + */ + if (!(0x80 & (unsigned char)*path)) + return *path && path[1] == ':' ? 2 : 0; + + /* + * While drive letters must be letters of the English alphabet, it is + * possible to assign virtually _any_ Unicode character via `subst` as + * a drive letter to "virtual drives". Even `1`, or `รค`. Or fun stuff + * like this: + * + * subst ึ: %USERPROFILE%\Desktop + */ + for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++) + ; /* skip first UTF-8 character */ + return path[i] == ':' ? i + 1 : 0; +} #ifdef GIT_WIN32 static bool looks_like_network_computer_name(const char *path, int pos) @@ -122,11 +145,11 @@ GIT_UNUSED(len); #else /* - * Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return - * 'C:/' here + * Mimic unix behavior where '/.git' returns '/': 'C:/.git' + * will return 'C:/' here */ - if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path)) - return 2; + if (dos_drive_prefix_length(path) == len) + return len; /* * Similarly checks if we're dealing with a network computer name @@ -259,11 +282,11 @@ int git_path_root(const char *path) { - int offset = 0; + int offset = 0, prefix_len; /* Does the root of the path look like a windows drive ? */ - if (LOOKS_LIKE_DRIVE_PREFIX(path)) - offset += 2; + if ((prefix_len = dos_drive_prefix_length(path))) + offset += prefix_len; #ifdef GIT_WIN32 /* Are we dealing with a windows network path? */ @@ -1608,8 +1631,12 @@ if (!start) return true; - /* Reject paths like ".git\" */ - if (path[start] == '\\') + /* + * Reject paths that start with Windows-style directory separators + * (".git\") or NTFS alternate streams (".git:") and could be used + * to write to the ".git" directory on Windows platforms. + */ + if (path[start] == '\\' || path[start] == ':') return false; /* Reject paths like '.git ' or '.git.' */ @@ -1621,12 +1648,21 @@ return false; } -GIT_INLINE(bool) only_spaces_and_dots(const char *path) +/* + * Windows paths that end with spaces and/or dots are elided to the + * path without them for backward compatibility. That is to say + * that opening file "foo ", "foo." or even "foo . . ." will all + * map to a filename of "foo". This function identifies spaces and + * dots at the end of a filename, whether the proper end of the + * filename (end of string) or a colon (which would indicate a + * Windows alternate data stream.) + */ +GIT_INLINE(bool) ntfs_end_of_filename(const char *path) { const char *c = path; for (;; c++) { - if (*c == '\0') + if (*c == '\0' || *c == ':') return true; if (*c != ' ' && *c != '.') return false; @@ -1641,13 +1677,13 @@ if (name[0] == '.' && len >= dotgit_len && !strncasecmp(name + 1, dotgit_name, dotgit_len)) { - return !only_spaces_and_dots(name + dotgit_len + 1); + return !ntfs_end_of_filename(name + dotgit_len + 1); } /* Detect the basic NTFS shortname with the first six chars */ if (!strncasecmp(name, dotgit_name, 6) && name[6] == '~' && name[7] >= '1' && name[7] <= '4') - return !only_spaces_and_dots(name + 8); + return !ntfs_end_of_filename(name + 8); /* Catch fallback names */ for (i = 0, saw_tilde = 0; i < 8; i++) { @@ -1669,7 +1705,7 @@ } } - return !only_spaces_and_dots(name + i); + return !ntfs_end_of_filename(name + i); } GIT_INLINE(bool) verify_char(unsigned char c, unsigned int flags) @@ -1803,7 +1839,7 @@ git_repository *repo, unsigned int flags) { - int protectHFS = 0, protectNTFS = 0; + int protectHFS = 0, protectNTFS = 1; int error = 0; flags |= GIT_PATH_REJECT_DOT_GIT_LITERAL; @@ -1812,16 +1848,12 @@ protectHFS = 1; #endif -#ifdef GIT_WIN32 - protectNTFS = 1; -#endif - if (repo && !protectHFS) error = git_repository__cvar(&protectHFS, repo, GIT_CVAR_PROTECTHFS); if (!error && protectHFS) flags |= GIT_PATH_REJECT_DOT_GIT_HFS; - if (repo && !protectNTFS) + if (repo) error = git_repository__cvar(&protectNTFS, repo, GIT_CVAR_PROTECTNTFS); if (!error && protectNTFS) flags |= GIT_PATH_REJECT_DOT_GIT_NTFS; @@ -1909,3 +1941,79 @@ return -1; } } + +int git_path_validate_system_file_ownership(const char *path) +{ +#ifndef GIT_WIN32 + GIT_UNUSED(path); + return GIT_OK; +#else + git_win32_path buf; + PSID owner_sid; + PSECURITY_DESCRIPTOR descriptor = NULL; + HANDLE token; + TOKEN_USER *info = NULL; + DWORD err, len; + int ret; + + if (git_win32_path_from_utf8(buf, path) < 0) + return -1; + + err = GetNamedSecurityInfoW(buf, SE_FILE_OBJECT, + OWNER_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION, + &owner_sid, NULL, NULL, NULL, &descriptor); + + if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { + ret = GIT_ENOTFOUND; + goto cleanup; + } + + if (err != ERROR_SUCCESS) { + git_error_set(GIT_ERROR_OS, "failed to get security information"); + ret = GIT_ERROR; + goto cleanup; + } + + if (!IsValidSid(owner_sid)) { + git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is unknown"); + ret = GIT_ERROR; + goto cleanup; + } + + if (IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) || + IsWellKnownSid(owner_sid, WinLocalSystemSid)) { + ret = GIT_OK; + goto cleanup; + } + + /* Obtain current user's SID */ + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token) && + !GetTokenInformation(token, TokenUser, NULL, 0, &len)) { + info = git__malloc(len); + GIT_ERROR_CHECK_ALLOC(info); + if (!GetTokenInformation(token, TokenUser, info, len, &len)) { + git__free(info); + info = NULL; + } + } + + /* + * If the file is owned by the same account that is running the current + * process, it's okay to read from that file. + */ + if (info && EqualSid(owner_sid, info->User.Sid)) + ret = GIT_OK; + else { + git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is not valid"); + ret = GIT_ERROR; + } + free(info); + +cleanup: + if (descriptor) + LocalFree(descriptor); + + return ret; +#endif +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-0.28.2/src/path.h new/libgit2-0.28.4/src/path.h --- old/libgit2-0.28.2/src/path.h 2019-05-21 09:54:19.000000000 +0200 +++ new/libgit2-0.28.4/src/path.h 2019-12-10 20:39:58.000000000 +0100 @@ -647,4 +647,16 @@ */ int git_path_normalize_slashes(git_buf *out, const char *path); +/** + * Validate a system file's ownership + * + * Verify that the file in question is owned by an administrator or system + * account, or at least by the current user. + * + * This function returns 0 if successful. If the file is not owned by any of + * these, or any other if there have been problems determining the file + * ownership, it returns -1. + */ +int git_path_validate_system_file_ownership(const char *path); + #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-0.28.2/src/repository.h new/libgit2-0.28.4/src/repository.h --- old/libgit2-0.28.2/src/repository.h 2019-05-21 09:54:19.000000000 +0200 +++ new/libgit2-0.28.4/src/repository.h 2019-12-10 20:39:58.000000000 +0100 @@ -113,7 +113,7 @@ /* core.protectHFS */ GIT_PROTECTHFS_DEFAULT = GIT_CVAR_FALSE, /* core.protectNTFS */ - GIT_PROTECTNTFS_DEFAULT = GIT_CVAR_FALSE, + GIT_PROTECTNTFS_DEFAULT = GIT_CVAR_TRUE, /* core.fsyncObjectFiles */ GIT_FSYNCOBJECTFILES_DEFAULT = GIT_CVAR_FALSE, } git_cvar_value; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-0.28.2/tests/checkout/nasty.c new/libgit2-0.28.4/tests/checkout/nasty.c --- old/libgit2-0.28.2/tests/checkout/nasty.c 2019-05-21 09:54:19.000000000 +0200 +++ new/libgit2-0.28.4/tests/checkout/nasty.c 2019-12-10 20:39:58.000000000 +0100 @@ -206,9 +206,8 @@ */ void test_checkout_nasty__git_tilde1(void) { -#ifdef GIT_WIN32 test_checkout_fails("refs/heads/git_tilde1", ".git/foobar"); -#endif + test_checkout_fails("refs/heads/git_tilde1", "git~1/foobar"); } /* A tree that contains an entry "git~2", when we have forced the short @@ -274,6 +273,16 @@ #endif } +/* A tree that contains an entry ".git::$INDEX_ALLOCATION" because NTFS + * will interpret that as a synonym to ".git", even when mounted via SMB + * on macOS. + */ +void test_checkout_nasty__dotgit_alternate_data_stream(void) +{ + test_checkout_fails("refs/heads/dotgit_alternate_data_stream", ".git/dummy-file"); + test_checkout_fails("refs/heads/dotgit_alternate_data_stream", ".git::$INDEX_ALLOCATION/dummy-file"); +} + /* Trees that contains entries with a tree ".git" that contain * byte sequences: * { 0xe2, 0x80, 0x8c } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-0.28.2/tests/clar_libgit2.h new/libgit2-0.28.4/tests/clar_libgit2.h --- old/libgit2-0.28.2/tests/clar_libgit2.h 2019-05-21 09:54:19.000000000 +0200 +++ new/libgit2-0.28.4/tests/clar_libgit2.h 2019-12-10 20:39:58.000000000 +0100 @@ -29,8 +29,8 @@ * calls that are supposed to fail! */ #define cl_git_fail(expr) do { \ - git_error_clear(); \ if ((expr) == 0) \ + git_error_clear(), \ cl_git_report_failure(0, 0, __FILE__, __LINE__, "Function call succeeded: " #expr); \ } while (0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-0.28.2/tests/index/tests.c new/libgit2-0.28.4/tests/index/tests.c --- old/libgit2-0.28.2/tests/index/tests.c 2019-05-21 09:54:19.000000000 +0200 +++ new/libgit2-0.28.4/tests/index/tests.c 2019-12-10 20:39:58.000000000 +0100 @@ -541,7 +541,7 @@ git_repository_free(bare_repo); } -static void add_invalid_filename(git_repository *repo, const char *fn) +static void assert_add_bypath_fails(git_repository *repo, const char *fn) { git_index *index; git_buf path = GIT_BUF_INIT; @@ -562,7 +562,7 @@ } /* Test that writing an invalid filename fails */ -void test_index_tests__add_invalid_filename(void) +void test_index_tests__cannot_add_invalid_filename(void) { git_repository *repo; @@ -577,13 +577,69 @@ if (!git_path_exists("./invalid/.GiT")) cl_must_pass(p_mkdir("./invalid/.GiT", 0777)); - add_invalid_filename(repo, ".git/hello"); - add_invalid_filename(repo, ".GIT/hello"); - add_invalid_filename(repo, ".GiT/hello"); - add_invalid_filename(repo, "./.git/hello"); - add_invalid_filename(repo, "./foo"); - add_invalid_filename(repo, "./bar"); - add_invalid_filename(repo, "subdir/../bar"); + assert_add_bypath_fails(repo, ".git/hello"); + assert_add_bypath_fails(repo, ".GIT/hello"); + assert_add_bypath_fails(repo, ".GiT/hello"); + assert_add_bypath_fails(repo, "./.git/hello"); + assert_add_bypath_fails(repo, "./foo"); + assert_add_bypath_fails(repo, "./bar"); + assert_add_bypath_fails(repo, "subdir/../bar"); + + git_repository_free(repo); + + cl_fixture_cleanup("invalid"); +} + +static void assert_add_fails(git_repository *repo, const char *fn) +{ + git_index *index; + git_buf path = GIT_BUF_INIT; + git_index_entry entry = {{0}}; + + cl_git_pass(git_repository_index(&index, repo)); + cl_assert(git_index_entrycount(index) == 0); + + entry.path = fn; + entry.mode = GIT_FILEMODE_BLOB; + cl_git_pass(git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391")); + + cl_git_fail(git_index_add(index, &entry)); + + cl_assert(git_index_entrycount(index) == 0); + + git_buf_dispose(&path); + git_index_free(index); +} + +/* + * Test that writing an invalid filename fails on filesystem + * specific protected names + */ +void test_index_tests__cannot_add_protected_invalid_filename(void) +{ + git_repository *repo; + git_index *index; + + cl_must_pass(p_mkdir("invalid", 0700)); + + cl_git_pass(git_repository_init(&repo, "./invalid", 0)); + + /* add a file to the repository so we can reference it later */ + cl_git_pass(git_repository_index(&index, repo)); + cl_git_mkfile("invalid/dummy.txt", ""); + cl_git_pass(git_index_add_bypath(index, "dummy.txt")); + cl_must_pass(p_unlink("invalid/dummy.txt")); + cl_git_pass(git_index_remove_bypath(index, "dummy.txt")); + git_index_free(index); + + cl_repo_set_bool(repo, "core.protectHFS", true); + cl_repo_set_bool(repo, "core.protectNTFS", true); + + assert_add_fails(repo, ".git./hello"); + assert_add_fails(repo, ".git\xe2\x80\xad/hello"); + assert_add_fails(repo, "git~1/hello"); + assert_add_fails(repo, ".git\xe2\x81\xaf/hello"); + assert_add_fails(repo, ".git::$INDEX_ALLOCATION/dummy-file"); git_repository_free(repo); @@ -599,7 +655,7 @@ *c = out; } -static void write_invalid_filename(git_repository *repo, const char *fn_orig) +static void assert_write_fails(git_repository *repo, const char *fn_orig) { git_index *index; git_oid expected; @@ -616,6 +672,7 @@ */ fn = git__strdup(fn_orig); replace_char(fn, '/', '_'); + replace_char(fn, ':', '!'); git_buf_joinpath(&path, "./invalid", fn); @@ -627,6 +684,7 @@ /* kids, don't try this at home */ replace_char((char *)entry->path, '_', '/'); + replace_char((char *)entry->path, '!', ':'); /* write-tree */ cl_git_fail(git_index_write_tree(&expected, index)); @@ -672,13 +730,13 @@ cl_git_pass(git_repository_init(&repo, "./invalid", 0)); - write_invalid_filename(repo, ".git/hello"); - write_invalid_filename(repo, ".GIT/hello"); - write_invalid_filename(repo, ".GiT/hello"); - write_invalid_filename(repo, "./.git/hello"); - write_invalid_filename(repo, "./foo"); - write_invalid_filename(repo, "./bar"); - write_invalid_filename(repo, "foo/../bar"); + assert_write_fails(repo, ".git/hello"); + assert_write_fails(repo, ".GIT/hello"); + assert_write_fails(repo, ".GiT/hello"); + assert_write_fails(repo, "./.git/hello"); + assert_write_fails(repo, "./foo"); + assert_write_fails(repo, "./bar"); + assert_write_fails(repo, "foo/../bar"); git_repository_free(repo); @@ -696,16 +754,52 @@ cl_repo_set_bool(repo, "core.protectHFS", true); cl_repo_set_bool(repo, "core.protectNTFS", true); - write_invalid_filename(repo, ".git./hello"); - write_invalid_filename(repo, ".git\xe2\x80\xad/hello"); - write_invalid_filename(repo, "git~1/hello"); - write_invalid_filename(repo, ".git\xe2\x81\xaf/hello"); + assert_write_fails(repo, ".git./hello"); + assert_write_fails(repo, ".git\xe2\x80\xad/hello"); + assert_write_fails(repo, "git~1/hello"); + assert_write_fails(repo, ".git\xe2\x81\xaf/hello"); + assert_write_fails(repo, ".git::$INDEX_ALLOCATION/dummy-file"); + + git_repository_free(repo); + + cl_fixture_cleanup("invalid"); +} + +void test_index_tests__protectntfs_on_by_default(void) +{ + git_repository *repo; + + p_mkdir("invalid", 0700); + + cl_git_pass(git_repository_init(&repo, "./invalid", 0)); + assert_write_fails(repo, ".git./hello"); + assert_write_fails(repo, "git~1/hello"); git_repository_free(repo); cl_fixture_cleanup("invalid"); } +void test_index_tests__can_disable_protectntfs(void) +{ + git_repository *repo; + git_index *index; + + cl_must_pass(p_mkdir("valid", 0700)); + cl_git_rewritefile("valid/git~1", "steal the shortname"); + + cl_git_pass(git_repository_init(&repo, "./valid", 0)); + cl_git_pass(git_repository_index(&index, repo)); + cl_repo_set_bool(repo, "core.protectNTFS", false); + + cl_git_pass(git_index_add_bypath(index, "git~1")); + + git_index_free(index); + git_repository_free(repo); + + cl_fixture_cleanup("valid"); +} + void test_index_tests__remove_entry(void) { git_repository *repo; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-0.28.2/tests/object/tree/write.c new/libgit2-0.28.4/tests/object/tree/write.c --- old/libgit2-0.28.2/tests/object/tree/write.c 2019-05-21 09:54:19.000000000 +0200 +++ new/libgit2-0.28.4/tests/object/tree/write.c 2019-12-10 20:39:58.000000000 +0100 @@ -141,7 +141,7 @@ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); for (i = 0; i < ARRAY_SIZE(entries); ++i) { - git_oid *id = entries[i].attr == GIT_FILEMODE_TREE ? &tid : &bid; + git_oid *id = entries[i].attr == GIT_FILEMODE_TREE ? &tid : &bid; cl_git_pass(git_treebuilder_insert(NULL, builder, entries[i].filename, id, entries[i].attr)); @@ -418,10 +418,8 @@ */ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); -#ifndef GIT_WIN32 - cl_git_pass(git_treebuilder_insert(NULL, builder, ".git.", &bid, GIT_FILEMODE_BLOB)); - cl_git_pass(git_treebuilder_insert(NULL, builder, "git~1", &bid, GIT_FILEMODE_BLOB)); -#endif + cl_git_fail(git_treebuilder_insert(NULL, builder, ".git.", &bid, GIT_FILEMODE_BLOB)); + cl_git_fail(git_treebuilder_insert(NULL, builder, "git~1", &bid, GIT_FILEMODE_BLOB)); #ifndef __APPLE__ cl_git_pass(git_treebuilder_insert(NULL, builder, ".git\xef\xbb\xbf", &bid, GIT_FILEMODE_BLOB)); @@ -444,6 +442,7 @@ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git\xef\xbb\xbf", &bid, GIT_FILEMODE_BLOB)); cl_git_fail(git_treebuilder_insert(NULL, builder, ".git\xe2\x80\xad", &bid, GIT_FILEMODE_BLOB)); + cl_git_fail(git_treebuilder_insert(NULL, builder, ".git::$INDEX_ALLOCATION/dummy-file", &bid, GIT_FILEMODE_BLOB)); git_treebuilder_free(builder); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-0.28.2/tests/path/core.c new/libgit2-0.28.4/tests/path/core.c --- old/libgit2-0.28.2/tests/path/core.c 2019-05-21 09:54:19.000000000 +0200 +++ new/libgit2-0.28.4/tests/path/core.c 2019-12-10 20:39:58.000000000 +0100 @@ -352,3 +352,14 @@ git_buf_dispose(&out); } + +void test_path_core__join_unrooted_respects_funny_windows_roots(void) +{ + test_join_unrooted("๐ฉ:/foo/bar/foobar", 9, "bar/foobar", "๐ฉ:/foo"); + test_join_unrooted("๐ฉ:/foo/bar/foobar", 13, "foobar", "๐ฉ:/foo/bar"); + test_join_unrooted("๐ฉ:/foo", 5, "๐ฉ:/foo", "๐ฉ:/asdf"); + test_join_unrooted("๐ฉ:/foo/bar", 5, "๐ฉ:/foo/bar", "๐ฉ:/asdf"); + test_join_unrooted("๐ฉ:/foo/bar/foobar", 9, "๐ฉ:/foo/bar/foobar", "๐ฉ:/foo"); + test_join_unrooted("๐ฉ:/foo/bar/foobar", 13, "๐ฉ:/foo/bar/foobar", "๐ฉ:/foo/bar"); + test_join_unrooted("๐ฉ:/foo/bar/foobar", 9, "๐ฉ:/foo/bar/foobar", "๐ฉ:/foo/"); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-0.28.2/tests/path/dotgit.c new/libgit2-0.28.4/tests/path/dotgit.c --- old/libgit2-0.28.2/tests/path/dotgit.c 2019-05-21 09:54:19.000000000 +0200 +++ new/libgit2-0.28.4/tests/path/dotgit.c 2019-12-10 20:39:58.000000000 +0100 @@ -116,4 +116,5 @@ cl_assert_equal_b(true, git_path_isvalid(NULL, ".gitmodules", 0, GIT_PATH_REJECT_DOT_GIT_HFS|GIT_PATH_REJECT_DOT_GIT_NTFS)); cl_assert_equal_b(false, git_path_isvalid(NULL, ".gitmodules", S_IFLNK, GIT_PATH_REJECT_DOT_GIT_HFS)); cl_assert_equal_b(false, git_path_isvalid(NULL, ".gitmodules", S_IFLNK, GIT_PATH_REJECT_DOT_GIT_NTFS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, ".gitmodules . .::$DATA", S_IFLNK, GIT_PATH_REJECT_DOT_GIT_NTFS)); } Binary files old/libgit2-0.28.2/tests/resources/nasty/.gitted/objects/33/8190107c7ee7d8f5aa30061fc19b7d5ddcda86 and new/libgit2-0.28.4/tests/resources/nasty/.gitted/objects/33/8190107c7ee7d8f5aa30061fc19b7d5ddcda86 differ Binary files old/libgit2-0.28.2/tests/resources/nasty/.gitted/objects/97/c14994866466aeb73e769a6f34e07c7f4b53f7 and new/libgit2-0.28.4/tests/resources/nasty/.gitted/objects/97/c14994866466aeb73e769a6f34e07c7f4b53f7 differ Binary files old/libgit2-0.28.2/tests/resources/nasty/.gitted/objects/b8/edf3ad62dbcbc983857a5bfee7b0181ee1a513 and new/libgit2-0.28.4/tests/resources/nasty/.gitted/objects/b8/edf3ad62dbcbc983857a5bfee7b0181ee1a513 differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-0.28.2/tests/resources/nasty/.gitted/refs/heads/dotgit_alternate_data_stream new/libgit2-0.28.4/tests/resources/nasty/.gitted/refs/heads/dotgit_alternate_data_stream --- old/libgit2-0.28.2/tests/resources/nasty/.gitted/refs/heads/dotgit_alternate_data_stream 1970-01-01 01:00:00.000000000 +0100 +++ new/libgit2-0.28.4/tests/resources/nasty/.gitted/refs/heads/dotgit_alternate_data_stream 2019-12-10 20:39:58.000000000 +0100 @@ -0,0 +1 @@ +b8edf3ad62dbcbc983857a5bfee7b0181ee1a513
