Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libgit2 for openSUSE:Factory checked in at 2022-04-16 00:12:48 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libgit2 (Old) and /work/SRC/openSUSE:Factory/.libgit2.new.1941 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libgit2" Sat Apr 16 00:12:48 2022 rev:49 rq:969974 version:1.4.3 Changes: -------- --- /work/SRC/openSUSE:Factory/libgit2/libgit2.changes 2022-03-23 20:15:34.826367821 +0100 +++ /work/SRC/openSUSE:Factory/.libgit2.new.1941/libgit2.changes 2022-04-16 00:12:51.297537396 +0200 @@ -1,0 +2,8 @@ +Wed Apr 13 17:53:54 UTC 2022 - Andreas Stieger <andreas.stie...@gmx.de> + +- update to 1.4.3: + * compatibility with git's changes for CVE-2022-24765 boo#1187234 + * several correctness fixes where invalid input can lead to a + crash and denial of service + +------------------------------------------------------------------- Old: ---- libgit2-1.4.2.tar.gz New: ---- libgit2-1.4.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libgit2.spec ++++++ --- /var/tmp/diff_new_pack.wFD5D1/_old 2022-04-16 00:12:51.797538064 +0200 +++ /var/tmp/diff_new_pack.wFD5D1/_new 2022-04-16 00:12:51.801538069 +0200 @@ -19,7 +19,7 @@ %define sover 1_4 Name: libgit2 -Version: 1.4.2 +Version: 1.4.3 Release: 0 Summary: C git library License: GPL-2.0-only WITH GCC-exception-2.0 ++++++ libgit2-1.4.2.tar.gz -> libgit2-1.4.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/CMakeLists.txt new/libgit2-1.4.3/CMakeLists.txt --- old/libgit2-1.4.2/CMakeLists.txt 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/CMakeLists.txt 2022-04-12 22:17:07.000000000 +0200 @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.5.1) -project(libgit2 VERSION "1.4.0" LANGUAGES C) +project(libgit2 VERSION "1.4.3" LANGUAGES C) # Add find modules to the path set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/docs/changelog.md new/libgit2-1.4.3/docs/changelog.md --- old/libgit2-1.4.2/docs/changelog.md 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/docs/changelog.md 2022-04-12 22:17:07.000000000 +0200 @@ -1,3 +1,23 @@ +v1.4.3 +------ + +???? This is a security release to provide compatibility with git's changes to address [CVE 2022-24765](https://github.blog/2022-04-12-git-security-vulnerability-announced/). + +**libgit2 is not directly affected** by this vulnerability, because libgit2 does not directly invoke any executable. But we are providing these changes as a security release for any users that use libgit2 for repository discovery and then _also_ use git on that repository. In this release, we will now validate that the user opening the repository is the same user that owns the on-disk repository. This is to match git's behavior. + +In addition, we are providing several correctness fixes where invalid input can lead to a crash. These may prevent possible denial of service attacks. At this time there are not known exploits to these issues. + +Full list of changes: + +* Validate repository directory ownership (v1.4) by @ethomson in https://github.com/libgit2/libgit2/pull/6267 +* midx: Fix an undefined behavior (left-shift signed overflow) by @lhchavez in https://github.com/libgit2/libgit2/pull/6260 +* fetch: support OID refspec without dst by @ethomson in https://github.com/libgit2/libgit2/pull/6251 +* Fix crash when regenerating a patch with unquoted spaces in filename by @jorio in https://github.com/libgit2/libgit2/pull/6244 + +All users of the v1.4 release line are recommended to upgrade. + +**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.4.2...v1.4.3 + v1.4.2 ------ Binary files old/libgit2-1.4.2/fuzzers/corpora/midx/666a779eed16847c6930a71c0547a34e52db409e and new/libgit2-1.4.3/fuzzers/corpora/midx/666a779eed16847c6930a71c0547a34e52db409e differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/include/git2/common.h new/libgit2-1.4.3/include/git2/common.h --- old/libgit2-1.4.2/include/git2/common.h 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/include/git2/common.h 2022-04-12 22:17:07.000000000 +0200 @@ -214,7 +214,9 @@ GIT_OPT_SET_ODB_PACKED_PRIORITY, GIT_OPT_SET_ODB_LOOSE_PRIORITY, GIT_OPT_GET_EXTENSIONS, - GIT_OPT_SET_EXTENSIONS + GIT_OPT_SET_EXTENSIONS, + GIT_OPT_GET_OWNER_VALIDATION, + GIT_OPT_SET_OWNER_VALIDATION } git_libgit2_opt_t; /** @@ -452,6 +454,14 @@ * > to support repositories with the `noop` extension but does want * > to support repositories with the `newext` extension. * + * opts(GIT_OPT_GET_OWNER_VALIDATION, int *enabled) + * > Gets the owner validation setting for repository + * > directories. + * + * opts(GIT_OPT_SET_OWNER_VALIDATION, int enabled) + * > Set that repository directories should be owned by the current + * > user. The default is to validate ownership. + * * @param option Option key * @param ... value to set the option * @return 0 on success, <0 on failure diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/include/git2/errors.h new/libgit2-1.4.3/include/git2/errors.h --- old/libgit2-1.4.2/include/git2/errors.h 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/include/git2/errors.h 2022-04-12 22:17:07.000000000 +0200 @@ -57,7 +57,8 @@ GIT_RETRY = -32, /**< Internal only */ GIT_EMISMATCH = -33, /**< Hashsum mismatch in object */ GIT_EINDEXDIRTY = -34, /**< Unsaved changes in the index would be overwritten */ - GIT_EAPPLYFAIL = -35 /**< Patch application failed */ + GIT_EAPPLYFAIL = -35, /**< Patch application failed */ + GIT_EOWNER = -36 /**< The object is not owned by the current user */ } git_error_code; /** diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/include/git2/version.h new/libgit2-1.4.3/include/git2/version.h --- old/libgit2-1.4.2/include/git2/version.h 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/include/git2/version.h 2022-04-12 22:17:07.000000000 +0200 @@ -7,10 +7,10 @@ #ifndef INCLUDE_git_version_h__ #define INCLUDE_git_version_h__ -#define LIBGIT2_VERSION "1.4.2" +#define LIBGIT2_VERSION "1.4.3" #define LIBGIT2_VER_MAJOR 1 #define LIBGIT2_VER_MINOR 4 -#define LIBGIT2_VER_REVISION 2 +#define LIBGIT2_VER_REVISION 3 #define LIBGIT2_VER_PATCH 0 #define LIBGIT2_SOVERSION "1.4" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/package.json new/libgit2-1.4.3/package.json --- old/libgit2-1.4.2/package.json 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/package.json 2022-04-12 22:17:07.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "libgit2", - "version": "1.4.0", + "version": "1.4.3", "repo": "https://github.com/libgit2/libgit2", "description": " A cross-platform, linkable library implementation of Git that you can use in your application.", "install": "mkdir build && cd build && cmake .. && cmake --build ." diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/src/config.c new/libgit2-1.4.3/src/config.c --- old/libgit2-1.4.2/src/config.c 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/src/config.c 2022-04-12 22:17:07.000000000 +0200 @@ -1170,14 +1170,18 @@ int git_config__find_programdata(git_str *path) { - int ret; + bool is_safe; - ret = git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA); + if (git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA) < 0 || + git_fs_path_owner_is_system_or_current_user(&is_safe, path->ptr) < 0) + return -1; - if (ret != GIT_OK) - return ret; + if (!is_safe) { + git_error_set(GIT_ERROR_CONFIG, "programdata path has invalid ownership"); + return -1; + } - return git_fs_path_validate_system_file_ownership(path->ptr); + return 0; } int git_config__global_location(git_str *buf) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/src/diff_print.c new/libgit2-1.4.3/src/diff_print.c --- old/libgit2-1.4.2/src/diff_print.c 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/src/diff_print.c 2022-04-12 22:17:07.000000000 +0200 @@ -316,6 +316,11 @@ static int diff_delta_format_path( git_str *out, const char *prefix, const char *filename) { + if (!filename) { + /* don't prefix "/dev/null" */ + return git_str_puts(out, "/dev/null"); + } + if (git_str_joinpath(out, prefix, filename) < 0) return -1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/src/fetch.c new/libgit2-1.4.3/src/fetch.c --- old/libgit2-1.4.2/src/fetch.c 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/src/fetch.c 2022-04-12 22:17:07.000000000 +0200 @@ -76,8 +76,11 @@ GIT_ERROR_CHECK_ALLOC(oid_head); git_oid_fromstr(&oid_head->oid, spec->src); - oid_head->name = git__strdup(spec->dst); - GIT_ERROR_CHECK_ALLOC(oid_head->name); + + if (spec->dst) { + oid_head->name = git__strdup(spec->dst); + GIT_ERROR_CHECK_ALLOC(oid_head->name); + } if (git_vector_insert(&remote->local_heads, oid_head) < 0 || git_vector_insert(&remote->refs, oid_head) < 0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/src/fs_path.c new/libgit2-1.4.3/src/fs_path.c --- old/libgit2-1.4.2/src/fs_path.c 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/src/fs_path.c 2022-04-12 22:17:07.000000000 +0200 @@ -1779,82 +1779,241 @@ return supported; } -int git_fs_path_validate_system_file_ownership(const char *path) +static git_fs_path__mock_owner_t mock_owner = GIT_FS_PATH_MOCK_OWNER_NONE; + +void git_fs_path__set_owner(git_fs_path__mock_owner_t owner) +{ + mock_owner = owner; +} + +#ifdef GIT_WIN32 +static PSID *sid_dup(PSID sid) +{ + DWORD len; + PSID dup; + + len = GetLengthSid(sid); + + if ((dup = git__malloc(len)) == NULL) + return NULL; + + if (!CopySid(len, dup, sid)) { + git_error_set(GIT_ERROR_OS, "could not duplicate sid"); + git__free(dup); + return NULL; + } + + return dup; +} + +static int current_user_sid(PSID *out) { -#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; + HANDLE token = NULL; + DWORD len = 0; + int error = -1; - if (git_win32_path_from_utf8(buf, path) < 0) - return -1; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) { + git_error_set(GIT_ERROR_OS, "could not lookup process information"); + goto done; + } - err = GetNamedSecurityInfoW(buf, SE_FILE_OBJECT, - OWNER_SECURITY_INFORMATION | - DACL_SECURITY_INFORMATION, - &owner_sid, NULL, NULL, NULL, &descriptor); + if (GetTokenInformation(token, TokenUser, NULL, 0, &len) || + GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + git_error_set(GIT_ERROR_OS, "could not lookup token metadata"); + goto done; + } - if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { - ret = GIT_ENOTFOUND; - goto cleanup; + info = git__malloc(len); + GIT_ERROR_CHECK_ALLOC(info); + + if (!GetTokenInformation(token, TokenUser, info, len, &len)) { + git_error_set(GIT_ERROR_OS, "could not lookup current user"); + goto done; } - if (err != ERROR_SUCCESS) { + if ((*out = sid_dup(info->User.Sid))) + error = 0; + +done: + if (token) + CloseHandle(token); + + git__free(info); + return error; +} + +static int file_owner_sid(PSID *out, const char *path) +{ + git_win32_path path_w32; + PSECURITY_DESCRIPTOR descriptor = NULL; + PSID owner_sid; + DWORD ret; + int error = -1; + + if (git_win32_path_from_utf8(path_w32, path) < 0) + return -1; + + ret = GetNamedSecurityInfoW(path_w32, SE_FILE_OBJECT, + OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + &owner_sid, NULL, NULL, NULL, &descriptor); + + if (ret == ERROR_FILE_NOT_FOUND || ret == ERROR_PATH_NOT_FOUND) + error = GIT_ENOTFOUND; + else if (ret != ERROR_SUCCESS) git_error_set(GIT_ERROR_OS, "failed to get security information"); - ret = GIT_ERROR; - goto cleanup; + else if (!IsValidSid(owner_sid)) + git_error_set(GIT_ERROR_OS, "file owner is not valid"); + else if ((*out = sid_dup(owner_sid))) + error = 0; + + if (descriptor) + LocalFree(descriptor); + + return error; +} + +int git_fs_path_owner_is_current_user(bool *out, const char *path) +{ + PSID owner_sid = NULL, user_sid = NULL; + int error = -1; + + if (mock_owner) { + *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_CURRENT_USER); + return 0; } - if (!IsValidSid(owner_sid)) { - git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is unknown"); - ret = GIT_ERROR; - goto cleanup; + if ((error = file_owner_sid(&owner_sid, path)) < 0 || + (error = current_user_sid(&user_sid)) < 0) + goto done; + + *out = EqualSid(owner_sid, user_sid); + error = 0; + +done: + git__free(owner_sid); + git__free(user_sid); + return error; +} + +int git_fs_path_owner_is_system(bool *out, const char *path) +{ + PSID owner_sid; + + if (mock_owner) { + *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_SYSTEM); + return 0; } + if (file_owner_sid(&owner_sid, path) < 0) + return -1; + + *out = IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) || + IsWellKnownSid(owner_sid, WinLocalSystemSid); + + git__free(owner_sid); + return 0; +} + +int git_fs_path_owner_is_system_or_current_user(bool *out, const char *path) +{ + PSID owner_sid = NULL, user_sid = NULL; + int error = -1; + + if (mock_owner) { + *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_SYSTEM || + mock_owner == GIT_FS_PATH_MOCK_OWNER_CURRENT_USER); + return 0; + } + + if (file_owner_sid(&owner_sid, path) < 0) + goto done; + if (IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) || IsWellKnownSid(owner_sid, WinLocalSystemSid)) { - ret = GIT_OK; - goto cleanup; + *out = 1; + error = 0; + goto done; + } + + if (current_user_sid(&user_sid) < 0) + goto done; + + *out = EqualSid(owner_sid, user_sid); + error = 0; + +done: + git__free(owner_sid); + git__free(user_sid); + return error; +} + +#else + +static int fs_path_owner_is(bool *out, const char *path, uid_t *uids, size_t uids_len) +{ + struct stat st; + size_t i; + + *out = false; + + if (p_lstat(path, &st) != 0) { + if (errno == ENOENT) + return GIT_ENOTFOUND; + + git_error_set(GIT_ERROR_OS, "could not stat '%s'", path); + return -1; } - /* 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; + for (i = 0; i < uids_len; i++) { + if (uids[i] == st.st_uid) { + *out = true; + break; } } - /* - * 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; + return 0; +} + +int git_fs_path_owner_is_current_user(bool *out, const char *path) +{ + uid_t userid = geteuid(); + + if (mock_owner) { + *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_CURRENT_USER); + return 0; } - git__free(info); -cleanup: - if (descriptor) - LocalFree(descriptor); + return fs_path_owner_is(out, path, &userid, 1); +} - return ret; -#endif +int git_fs_path_owner_is_system(bool *out, const char *path) +{ + uid_t userid = 0; + + if (mock_owner) { + *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_SYSTEM); + return 0; + } + + return fs_path_owner_is(out, path, &userid, 1); } +int git_fs_path_owner_is_system_or_current_user(bool *out, const char *path) +{ + uid_t userids[2] = { geteuid(), 0 }; + + if (mock_owner) { + *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_SYSTEM || + mock_owner == GIT_FS_PATH_MOCK_OWNER_CURRENT_USER); + return 0; + } + + return fs_path_owner_is(out, path, userids, 2); +} + +#endif + int git_fs_path_find_executable(git_str *fullpath, const char *executable) { #ifdef GIT_WIN32 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/src/fs_path.h new/libgit2-1.4.3/src/fs_path.h --- old/libgit2-1.4.2/src/fs_path.h 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/src/fs_path.h 2022-04-12 22:17:07.000000000 +0200 @@ -731,17 +731,37 @@ bool git_fs_path_supports_symlinks(const char *dir); +typedef enum { + GIT_FS_PATH_MOCK_OWNER_NONE = 0, /* do filesystem lookups as normal */ + GIT_FS_PATH_MOCK_OWNER_SYSTEM = 1, + GIT_FS_PATH_MOCK_OWNER_CURRENT_USER = 2, + GIT_FS_PATH_MOCK_OWNER_OTHER = 3 +} git_fs_path__mock_owner_t; + +/** + * Sets the mock ownership for files; subsequent calls to + * `git_fs_path_owner_is_*` functions will return this data until cleared + * with `GIT_FS_PATH_MOCK_OWNER_NONE`. + */ +void git_fs_path__set_owner(git_fs_path__mock_owner_t owner); + +/** + * Verify that the file in question is owned by an administrator or system + * account. + */ +int git_fs_path_owner_is_system(bool *out, const char *path); + +/** + * Verify that the file in question is owned by the current user; + */ + +int git_fs_path_owner_is_current_user(bool *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. + * account _or_ the current user; */ -int git_fs_path_validate_system_file_ownership(const char *path); +int git_fs_path_owner_is_system_or_current_user(bool *out, const char *path); /** * Search the current PATH for the given executable, returning the full diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/src/libgit2.c new/libgit2-1.4.3/src/libgit2.c --- old/libgit2-1.4.2/src/libgit2.c 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/src/libgit2.c 2022-04-12 22:17:07.000000000 +0200 @@ -401,6 +401,14 @@ } break; + case GIT_OPT_GET_OWNER_VALIDATION: + *(va_arg(ap, int *)) = git_repository__validate_ownership; + break; + + case GIT_OPT_SET_OWNER_VALIDATION: + git_repository__validate_ownership = (va_arg(ap, int) != 0); + break; + default: git_error_set(GIT_ERROR_INVALID, "invalid option key"); error = -1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/src/midx.c new/libgit2-1.4.3/src/midx.c --- old/libgit2-1.4.2/src/midx.c 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/src/midx.c 2022-04-12 22:17:07.000000000 +0200 @@ -225,8 +225,13 @@ chunk_hdr = data + sizeof(struct git_midx_header); last_chunk = NULL; for (i = 0; i < hdr->chunks; ++i, chunk_hdr += 12) { - chunk_offset = ((off64_t)ntohl(*((uint32_t *)(chunk_hdr + 4)))) << 32 | - ((off64_t)ntohl(*((uint32_t *)(chunk_hdr + 8)))); + uint32_t chunk_id = ntohl(*((uint32_t *)(chunk_hdr + 0))); + uint64_t high_offset = ((uint64_t)ntohl(*((uint32_t *)(chunk_hdr + 4)))) & 0xffffffffu; + uint64_t low_offset = ((uint64_t)ntohl(*((uint32_t *)(chunk_hdr + 8)))) & 0xffffffffu; + + if (high_offset >= INT32_MAX) + return midx_error("chunk offset out of range"); + chunk_offset = (off64_t)(high_offset << 32 | low_offset); if (chunk_offset < last_chunk_offset) return midx_error("chunks are non-monotonic"); if (chunk_offset >= trailer_offset) @@ -235,7 +240,7 @@ last_chunk->length = (size_t)(chunk_offset - last_chunk_offset); last_chunk_offset = chunk_offset; - switch (ntohl(*((uint32_t *)(chunk_hdr + 0)))) { + switch (chunk_id) { case MIDX_PACKFILE_NAMES_ID: chunk_packfile_names.offset = last_chunk_offset; last_chunk = &chunk_packfile_names; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/src/remote.c new/libgit2-1.4.3/src/remote.c --- old/libgit2-1.4.2/src/remote.c 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/src/remote.c 2022-04-12 22:17:07.000000000 +0200 @@ -1895,8 +1895,11 @@ if (git_oid__is_hexstr(spec->src)) { git_oid id; - if ((error = git_oid_fromstr(&id, spec->src)) < 0 || - (error = update_ref(remote, spec->dst, &id, log_message, callbacks)) < 0) + if ((error = git_oid_fromstr(&id, spec->src)) < 0) + goto on_error; + + if (spec->dst && + (error = update_ref(remote, spec->dst, &id, log_message, callbacks)) < 0) goto on_error; git_oid_cpy(&oid_head.oid, &id); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/src/repository.c new/libgit2-1.4.3/src/repository.c --- old/libgit2-1.4.2/src/repository.c 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/src/repository.c 2022-04-12 22:17:07.000000000 +0200 @@ -39,6 +39,7 @@ # include "win32/w32_util.h" #endif +bool git_repository__validate_ownership = true; bool git_repository__fsync_gitdir = false; static const struct { @@ -65,6 +66,7 @@ static int check_repositoryformatversion(int *version, git_config *config); static int check_extensions(git_config *config, int version); +static int load_global_config(git_config **config); #define GIT_COMMONDIR_FILE "commondir" #define GIT_GITDIR_FILE "gitdir" @@ -483,6 +485,63 @@ return error; } +typedef struct { + const char *repo_path; + git_str tmp; + bool is_safe; +} validate_ownership_data; + +static int validate_ownership_cb(const git_config_entry *entry, void *payload) +{ + validate_ownership_data *data = payload; + + if (strcmp(entry->value, "") == 0) + data->is_safe = false; + + if (git_fs_path_prettify_dir(&data->tmp, entry->value, NULL) == 0 && + strcmp(data->tmp.ptr, data->repo_path) == 0) + data->is_safe = true; + + return 0; +} + +static int validate_ownership(const char *repo_path) +{ + git_config *config = NULL; + validate_ownership_data data = { repo_path, GIT_STR_INIT, false }; + bool is_safe; + int error; + + if ((error = git_fs_path_owner_is_current_user(&is_safe, repo_path)) < 0) { + if (error == GIT_ENOTFOUND) + error = 0; + + goto done; + } + + if (is_safe) { + error = 0; + goto done; + } + + if (load_global_config(&config) == 0) { + error = git_config_get_multivar_foreach(config, "safe.directory", NULL, validate_ownership_cb, &data); + + if (!error && data.is_safe) + goto done; + } + + git_error_set(GIT_ERROR_CONFIG, + "repository path '%s' is not owned by current user", + repo_path); + error = GIT_EOWNER; + +done: + git_config_free(config); + git_str_dispose(&data.tmp); + return error; +} + static int find_repo( git_str *gitdir_path, git_str *workdir_path, @@ -856,6 +915,7 @@ gitlink = GIT_STR_INIT, commondir = GIT_STR_INIT; git_repository *repo = NULL; git_config *config = NULL; + const char *validation_path; int version = 0; if (flags & GIT_REPOSITORY_OPEN_FROM_ENV) @@ -904,16 +964,24 @@ if ((error = check_extensions(config, version)) < 0) goto cleanup; - if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0) + if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0) { repo->is_bare = 1; - else { - + } else { if (config && ((error = load_config_data(repo, config)) < 0 || (error = load_workdir(repo, config, &workdir)) < 0)) goto cleanup; } + /* + * Ensure that the git directory is owned by the current user. + */ + validation_path = repo->is_bare ? repo->gitdir : repo->workdir; + + if (git_repository__validate_ownership && + (error = validate_ownership(validation_path)) < 0) + goto cleanup; + cleanup: git_str_dispose(&gitdir); git_str_dispose(&workdir); @@ -1606,13 +1674,40 @@ return is_insensitive; } -static bool are_symlinks_supported(const char *wd_path) +/* + * Return a configuration object with only the global and system + * configurations; no repository-level configuration. + */ +static int load_global_config(git_config **config) { - git_config *config = NULL; git_str global_buf = GIT_STR_INIT; git_str xdg_buf = GIT_STR_INIT; git_str system_buf = GIT_STR_INIT; git_str programdata_buf = GIT_STR_INIT; + int error; + + git_config__find_global(&global_buf); + git_config__find_xdg(&xdg_buf); + git_config__find_system(&system_buf); + git_config__find_programdata(&programdata_buf); + + error = load_config(config, NULL, + path_unless_empty(&global_buf), + path_unless_empty(&xdg_buf), + path_unless_empty(&system_buf), + path_unless_empty(&programdata_buf)); + + git_str_dispose(&global_buf); + git_str_dispose(&xdg_buf); + git_str_dispose(&system_buf); + git_str_dispose(&programdata_buf); + + return error; +} + +static bool are_symlinks_supported(const char *wd_path) +{ + git_config *config = NULL; int symlinks = 0; /* @@ -1623,19 +1718,9 @@ * _not_ set, then we do not test or enable symlink support. */ #ifdef GIT_WIN32 - git_config__find_global(&global_buf); - git_config__find_xdg(&xdg_buf); - git_config__find_system(&system_buf); - git_config__find_programdata(&programdata_buf); - - if (load_config(&config, NULL, - path_unless_empty(&global_buf), - path_unless_empty(&xdg_buf), - path_unless_empty(&system_buf), - path_unless_empty(&programdata_buf)) < 0) - goto done; - - if (git_config_get_bool(&symlinks, config, "core.symlinks") < 0 || !symlinks) + if (load_global_config(&config) < 0 || + git_config_get_bool(&symlinks, config, "core.symlinks") < 0 || + !symlinks) goto done; #endif @@ -1643,10 +1728,6 @@ goto done; done: - git_str_dispose(&global_buf); - git_str_dispose(&xdg_buf); - git_str_dispose(&system_buf); - git_str_dispose(&programdata_buf); git_config_free(config); return symlinks != 0; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/src/repository.h new/libgit2-1.4.3/src/repository.h --- old/libgit2-1.4.2/src/repository.h 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/src/repository.h 2022-04-12 22:17:07.000000000 +0200 @@ -34,6 +34,7 @@ #define GIT_DIR_SHORTNAME "GIT~1" extern bool git_repository__fsync_gitdir; +extern bool git_repository__validate_ownership; /** Cvar cache identifiers */ typedef enum { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/tests/clar_libgit2.c new/libgit2-1.4.3/tests/clar_libgit2.c --- old/libgit2-1.4.2/tests/clar_libgit2.c 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/tests/clar_libgit2.c 2022-04-12 22:17:07.000000000 +0200 @@ -599,6 +599,11 @@ git_str_dispose(&path); } +void cl_sandbox_disable_ownership_validation(void) +{ + git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 0); +} + #ifdef GIT_WIN32 bool cl_sandbox_supports_8dot3(void) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/tests/clar_libgit2.h new/libgit2-1.4.3/tests/clar_libgit2.h --- old/libgit2-1.4.2/tests/clar_libgit2.h 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/tests/clar_libgit2.h 2022-04-12 22:17:07.000000000 +0200 @@ -222,6 +222,7 @@ void cl_fake_home_cleanup(void *); void cl_sandbox_set_search_path_defaults(void); +void cl_sandbox_disable_ownership_validation(void); #ifdef GIT_WIN32 # define cl_msleep(x) Sleep(x) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/tests/core/path.c new/libgit2-1.4.3/tests/core/path.c --- old/libgit2-1.4.2/tests/core/path.c 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/tests/core/path.c 2022-04-12 22:17:07.000000000 +0200 @@ -737,3 +737,28 @@ git_str_dispose(&sandbox_path); git__free(orig_path); } + +void test_core_path__validate_current_user_ownership(void) +{ + bool is_cur; + + cl_must_pass(p_mkdir("testdir", 0777)); + cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "testdir")); + cl_assert_equal_i(is_cur, 1); + + cl_git_rewritefile("testfile", "This is a test file."); + cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "testfile")); + cl_assert_equal_i(is_cur, 1); + +#ifdef GIT_WIN32 + cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "C:\\")); + cl_assert_equal_i(is_cur, 0); + + cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "c:\\path\\does\\not\\exist")); +#else + cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "/")); + cl_assert_equal_i(is_cur, 0); + + cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "/path/does/not/exist")); +#endif +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/tests/diff/parse.c new/libgit2-1.4.3/tests/diff/parse.c --- old/libgit2-1.4.2/tests/diff/parse.c 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/tests/diff/parse.c 2022-04-12 22:17:07.000000000 +0200 @@ -431,6 +431,32 @@ git_diff_free(diff); } +void test_diff_parse__new_file_with_space_and_regenerate_patch(void) +{ + const char *content = PATCH_ORIGINAL_NEW_FILE_WITH_SPACE; + git_diff *diff = NULL; + git_buf buf = GIT_BUF_INIT; + + cl_git_pass(git_diff_from_buffer(&diff, content, strlen(content))); + cl_git_pass(git_diff_to_buf(&buf, diff, GIT_DIFF_FORMAT_PATCH)); + + git_buf_dispose(&buf); + git_diff_free(diff); +} + +void test_diff_parse__delete_file_with_space_and_regenerate_patch(void) +{ + const char *content = PATCH_DELETE_FILE_WITH_SPACE; + git_diff *diff = NULL; + git_buf buf = GIT_BUF_INIT; + + cl_git_pass(git_diff_from_buffer(&diff, content, strlen(content))); + cl_git_pass(git_diff_to_buf(&buf, diff, GIT_DIFF_FORMAT_PATCH)); + + git_buf_dispose(&buf); + git_diff_free(diff); +} + void test_diff_parse__crlf(void) { const char *text = PATCH_CRLF; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/tests/main.c new/libgit2-1.4.3/tests/main.c --- old/libgit2-1.4.2/tests/main.c 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/tests/main.c 2022-04-12 22:17:07.000000000 +0200 @@ -26,6 +26,7 @@ cl_global_trace_register(); cl_sandbox_set_search_path_defaults(); + cl_sandbox_disable_ownership_validation(); /* Run the test suite */ res = clar_test_run(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/tests/online/clone.c new/libgit2-1.4.3/tests/online/clone.c --- old/libgit2-1.4.2/tests/online/clone.c 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/tests/online/clone.c 2022-04-12 22:17:07.000000000 +0200 @@ -8,9 +8,9 @@ #define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository" #define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository" -#define BB_REPO_URL "https://libg...@bitbucket.org/libgit2/testgitrepository.git" -#define BB_REPO_URL_WITH_PASS "https://libgit3:libg...@bitbucket.org/libgit2/testgitrepository.git" -#define BB_REPO_URL_WITH_WRONG_PASS "https://libgit3:wr...@bitbucket.org/libgit2/testgitrepository.git" +#define BB_REPO_URL "https://libgit2-t...@bitbucket.org/libgit2-test/testgitrepository.git" +#define BB_REPO_URL_WITH_PASS "https://libgit2-test:yt77ppm2nq8w4tyjg...@bitbucket.org/libgit2-test/testgitrepository.git" +#define BB_REPO_URL_WITH_WRONG_PASS "https://libgit2-test:wr...@bitbucket.org/libgit2-test/testgitrepository.git" #define GOOGLESOURCE_REPO_URL "https://chromium.googlesource.com/external/github.com/sergi/go-diff" #define SSH_REPO_URL "ssh://github.com/libgit2/TestGitRepository" @@ -413,7 +413,7 @@ void test_online_clone__credentials_via_custom_headers(void) { - const char *creds = "libgit3:libgit3"; + const char *creds = "libgit2-test:YT77Ppm2nq8w4TYjGS8U"; git_str auth = GIT_STR_INIT; cl_git_pass(git_str_puts(&auth, "Authorization: Basic ")); @@ -421,7 +421,7 @@ g_options.fetch_opts.custom_headers.count = 1; g_options.fetch_opts.custom_headers.strings = &auth.ptr; - cl_git_pass(git_clone(&g_repo, "https://bitbucket.org/libgit2/testgitrepository.git", "./foo", &g_options)); + cl_git_pass(git_clone(&g_repo, "https://bitbucket.org/libgit2-test/testgitrepository.git", "./foo", &g_options)); git_str_dispose(&auth); } @@ -429,7 +429,7 @@ void test_online_clone__bitbucket_style(void) { git_credential_userpass_payload user_pass = { - "libgit3", "libgit3" + "libgit2-test", "YT77Ppm2nq8w4TYjGS8U" }; g_options.fetch_opts.callbacks.credentials = git_credential_userpass; @@ -443,7 +443,7 @@ void test_online_clone__bitbucket_uses_creds_in_url(void) { git_credential_userpass_payload user_pass = { - "libgit2", "wrong" + "libgit2-test", "wrong" }; g_options.fetch_opts.callbacks.credentials = git_credential_userpass; @@ -461,7 +461,7 @@ void test_online_clone__bitbucket_falls_back_to_specified_creds(void) { git_credential_userpass_payload user_pass = { - "libgit2", "libgit2" + "libgit2-test", "libgit2" }; g_options.fetch_opts.callbacks.credentials = git_credential_userpass; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/tests/online/fetch.c new/libgit2-1.4.3/tests/online/fetch.c --- old/libgit2-1.4.2/tests/online/fetch.c 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/tests/online/fetch.c 2022-04-12 22:17:07.000000000 +0200 @@ -321,3 +321,32 @@ git_object_free(obj); git_remote_free(remote); } + +void test_online_fetch__reachable_commit_without_destination(void) +{ + git_remote *remote; + git_strarray refspecs; + git_object *obj; + git_oid expected_id; + git_str fetchhead = GIT_STR_INIT; + char *refspec = "2c349335b7f797072cf729c4f3bb0914ecb6dec9"; + + refspecs.strings = &refspec; + refspecs.count = 1; + + git_oid_fromstr(&expected_id, "2c349335b7f797072cf729c4f3bb0914ecb6dec9"); + + cl_git_pass(git_remote_create(&remote, _repo, "test", + "https://github.com/libgit2/TestGitRepository")); + cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, NULL)); + + cl_git_fail_with(GIT_ENOTFOUND, git_revparse_single(&obj, _repo, "refs/success")); + + cl_git_pass(git_futils_readbuffer(&fetchhead, "./fetch/.git/FETCH_HEAD")); + cl_assert_equal_s(fetchhead.ptr, + "2c349335b7f797072cf729c4f3bb0914ecb6dec9\t\t'2c349335b7f797072cf729c4f3bb0914ecb6dec9' of https://github.com/libgit2/TestGitRepository\n"); + + git_str_dispose(&fetchhead); + git_object_free(obj); + git_remote_free(remote); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/tests/patch/patch_common.h new/libgit2-1.4.3/tests/patch/patch_common.h --- old/libgit2-1.4.2/tests/patch/patch_common.h 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/tests/patch/patch_common.h 2022-04-12 22:17:07.000000000 +0200 @@ -933,6 +933,15 @@ "@@ -0,0 +1 @@\n" \ "+a\n" +#define PATCH_DELETE_FILE_WITH_SPACE \ + "diff --git a/sp ace.txt b/sp ace.txt\n" \ + "deleted file mode 100644\n" \ + "index 789819226..000000000\n" \ + "--- a/sp ace.txt\n" \ + "+++ /dev/null\n" \ + "@@ -1 +0,0 @@\n" \ + "-a\n" + #define PATCH_CRLF \ "diff --git a/test-file b/test-file\r\n" \ "new file mode 100644\r\n" \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/tests/repo/config.c new/libgit2-1.4.3/tests/repo/config.c --- old/libgit2-1.4.2/tests/repo/config.c 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/tests/repo/config.c 2022-04-12 22:17:07.000000000 +0200 @@ -28,7 +28,6 @@ cl_assert(!git_fs_path_isdir("alternate")); cl_fixture_cleanup("empty_standard_repo"); - } void test_repo_config__can_open_global_when_there_is_no_file(void) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libgit2-1.4.2/tests/repo/open.c new/libgit2-1.4.3/tests/repo/open.c --- old/libgit2-1.4.2/tests/repo/open.c 2022-02-26 20:50:04.000000000 +0100 +++ new/libgit2-1.4.3/tests/repo/open.c 2022-04-12 22:17:07.000000000 +0200 @@ -3,13 +3,30 @@ #include "sysdir.h" #include <ctype.h> +static int validate_ownership = 0; +static git_buf config_path = GIT_BUF_INIT; + +void test_repo_open__initialize(void) +{ + cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &config_path)); + cl_git_pass(git_libgit2_opts(GIT_OPT_GET_OWNER_VALIDATION, &validate_ownership)); +} void test_repo_open__cleanup(void) { cl_git_sandbox_cleanup(); + cl_fixture_cleanup("empty_standard_repo"); + cl_fixture_cleanup("__global_config"); if (git_fs_path_isdir("alternate")) git_futils_rmdir_r("alternate", NULL, GIT_RMDIR_REMOVE_FILES); + + git_fs_path__set_owner(GIT_FS_PATH_MOCK_OWNER_NONE); + + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, config_path.ptr)); + git_buf_dispose(&config_path); + + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, validate_ownership)); } void test_repo_open__bare_empty_repo(void) @@ -453,3 +470,133 @@ git_repository_free(barerepo); } +void test_repo_open__validates_dir_ownership(void) +{ + git_repository *repo; + + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1)); + + cl_fixture_sandbox("empty_standard_repo"); + cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git")); + + /* When the current user owns the repo config, that's acceptable */ + git_fs_path__set_owner(GIT_FS_PATH_MOCK_OWNER_CURRENT_USER); + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository_free(repo); + + /* When the system user owns the repo config, fail */ + git_fs_path__set_owner(GIT_FS_PATH_MOCK_OWNER_SYSTEM); + cl_git_fail(git_repository_open(&repo, "empty_standard_repo")); + + /* When an unknown user owns the repo config, fail */ + git_fs_path__set_owner(GIT_FS_PATH_MOCK_OWNER_OTHER); + cl_git_fail(git_repository_open(&repo, "empty_standard_repo")); +} + +void test_repo_open__can_allowlist_dirs_with_problematic_ownership(void) +{ + git_repository *repo; + git_str config_path = GIT_STR_INIT, + config_filename = GIT_STR_INIT, + config_data = GIT_STR_INIT; + + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1)); + + cl_fixture_sandbox("empty_standard_repo"); + cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git")); + + git_fs_path__set_owner(GIT_FS_PATH_MOCK_OWNER_OTHER); + cl_git_fail(git_repository_open(&repo, "empty_standard_repo")); + + /* Add safe.directory options to the global configuration */ + git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config"); + cl_must_pass(p_mkdir(config_path.ptr, 0777)); + git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, config_path.ptr); + + git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig"); + + git_str_printf(&config_data, + "[foo]\n" \ + "\tbar = Foobar\n" \ + "\tbaz = Baz!\n" \ + "[safe]\n" \ + "\tdirectory = /non/existent/path\n" \ + "\tdirectory = /\n" \ + "\tdirectory = c:\\\\temp\n" \ + "\tdirectory = %s/%s\n" \ + "\tdirectory = /tmp\n" \ + "[bar]\n" \ + "\tfoo = barfoo\n", + clar_sandbox_path(), "empty_standard_repo"); + cl_git_rewritefile(config_filename.ptr, config_data.ptr); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository_free(repo); + + git_str_dispose(&config_path); + git_str_dispose(&config_filename); + git_str_dispose(&config_data); +} + +void test_repo_open__can_reset_safe_directory_list(void) +{ + git_repository *repo; + git_str config_path = GIT_STR_INIT, + config_filename = GIT_STR_INIT, + config_data = GIT_STR_INIT; + + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1)); + + cl_fixture_sandbox("empty_standard_repo"); + cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git")); + + git_fs_path__set_owner(GIT_FS_PATH_MOCK_OWNER_OTHER); + cl_git_fail(git_repository_open(&repo, "empty_standard_repo")); + + /* Add safe.directory options to the global configuration */ + git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config"); + cl_must_pass(p_mkdir(config_path.ptr, 0777)); + git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, config_path.ptr); + + git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig"); + + /* The blank resets our sandbox directory and opening fails */ + + git_str_printf(&config_data, + "[foo]\n" \ + "\tbar = Foobar\n" \ + "\tbaz = Baz!\n" \ + "[safe]\n" \ + "\tdirectory = %s/%s\n" \ + "\tdirectory = \n" \ + "\tdirectory = /tmp\n" \ + "[bar]\n" \ + "\tfoo = barfoo\n", + clar_sandbox_path(), "empty_standard_repo"); + cl_git_rewritefile(config_filename.ptr, config_data.ptr); + + cl_git_fail(git_repository_open(&repo, "empty_standard_repo")); + + /* The blank resets tmp and allows subsequent declarations to succeed */ + + git_str_clear(&config_data); + git_str_printf(&config_data, + "[foo]\n" \ + "\tbar = Foobar\n" \ + "\tbaz = Baz!\n" \ + "[safe]\n" \ + "\tdirectory = /tmp\n" \ + "\tdirectory = \n" \ + "\tdirectory = %s/%s\n" \ + "[bar]\n" \ + "\tfoo = barfoo\n", + clar_sandbox_path(), "empty_standard_repo"); + cl_git_rewritefile(config_filename.ptr, config_data.ptr); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository_free(repo); + + git_str_dispose(&config_path); + git_str_dispose(&config_filename); + git_str_dispose(&config_data); +}