Version in base suite: 1.9.0+ds-2 Base version: libgit2_1.9.0+ds-2 Target version: libgit2_1.9.0+ds-2+deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/libg/libgit2/libgit2_1.9.0+ds-2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/libg/libgit2/libgit2_1.9.0+ds-2+deb13u1.dsc changelog | 12 + gbp.conf | 4 patches/backports/CVE-2026-53583.patch | 43 ++++ patches/backports/CVE-2026-53584.patch | 343 +++++++++++++++++++++++++++++++++ patches/backports/CVE-2026-53585.patch | 115 +++++++++++ patches/backports/CVE-2026-53586.patch | 62 +++++ patches/backports/CVE-2026-53587.patch | 23 ++ patches/backports/CVE-2026-5917.patch | 92 ++++++++ patches/series | 6 9 files changed, 698 insertions(+), 2 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp0gbsltnw/libgit2_1.9.0+ds-2.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp0gbsltnw/libgit2_1.9.0+ds-2+deb13u1.dsc: no acceptable signature found diff -Nru libgit2-1.9.0+ds/debian/changelog libgit2-1.9.0+ds/debian/changelog --- libgit2-1.9.0+ds/debian/changelog 2025-04-03 15:28:22.000000000 +0000 +++ libgit2-1.9.0+ds/debian/changelog 2026-08-20 05:56:57.000000000 +0000 @@ -1,3 +1,15 @@ +libgit2 (1.9.0+ds-2+deb13u1) trixie-security; urgency=high + + * Fix CVE-2026-5917: shell command injection in SSH transport + (Closes: #1144465) + * Fix CVE-2026-53583: inverted cert validity check for IP addresses + * Fix CVE-2026-53584: unsanitized submodule paths + * Fix CVE-2026-53585: limit pack object size to 2GiB + * Fix CVE-2026-53586: pass correct hostname to auth layer after redirect + * Fix CVE-2026-53587: read buffer overflow in capability check + + -- Timo Röhling Thu, 20 Aug 2026 07:56:57 +0200 + libgit2 (1.9.0+ds-2) unstable; urgency=medium * Upload to unstable. diff -Nru libgit2-1.9.0+ds/debian/gbp.conf libgit2-1.9.0+ds/debian/gbp.conf --- libgit2-1.9.0+ds/debian/gbp.conf 2025-04-03 15:28:22.000000000 +0000 +++ libgit2-1.9.0+ds/debian/gbp.conf 2026-08-16 13:34:30.000000000 +0000 @@ -1,7 +1,7 @@ [DEFAULT] pristine-tar = True -debian-branch = debian/sid -upstream-branch = upstream/sid +debian-branch = debian/trixie +upstream-branch = [pq] patch-numbers = False diff -Nru libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53583.patch libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53583.patch --- libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53583.patch 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53583.patch 2026-08-20 05:53:35.000000000 +0000 @@ -0,0 +1,43 @@ +From: =?utf-8?q?Timo_R=C3=B6hling?= +Date: Sun, 16 Aug 2026 15:42:46 +0200 +Subject: Fix CVE-2026-53583: inverted cert validity check for IP addresses + +Origin: upstream, https://github.com/libgit2/libgit2/commit/c2aa35409ee0e6515df64da49750f13a0a42c47f +--- + src/libgit2/streams/openssl.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/libgit2/streams/openssl.c b/src/libgit2/streams/openssl.c +index e5641e0..ca77ad0 100644 +--- a/src/libgit2/streams/openssl.c ++++ b/src/libgit2/streams/openssl.c +@@ -375,6 +375,7 @@ static int verify_server_cert(SSL *ssl, const char *host) + struct in6_addr addr6; + struct in_addr addr4; + void *addr = NULL; ++ size_t addrlen = 0; + int i = -1, j, error = 0; + + if (SSL_get_verify_result(ssl) != X509_V_OK) { +@@ -386,10 +387,12 @@ static int verify_server_cert(SSL *ssl, const char *host) + if (p_inet_pton(AF_INET, host, &addr4)) { + type = GEN_IPADD; + addr = &addr4; ++ addrlen = sizeof(addr4); + } else { + if (p_inet_pton(AF_INET6, host, &addr6)) { + type = GEN_IPADD; + addr = &addr6; ++ addrlen = sizeof(addr6); + } + } + +@@ -424,7 +427,7 @@ static int verify_server_cert(SSL *ssl, const char *host) + matched = !!check_host_name(host, name); + } else if (type == GEN_IPADD) { + /* Here name isn't so much a name but a binary representation of the IP */ +- matched = addr && !!memcmp(name, addr, namelen); ++ matched = (addr && namelen == addrlen && memcmp(name, addr, namelen) == 0); + } + } + } diff -Nru libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53584.patch libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53584.patch --- libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53584.patch 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53584.patch 2026-08-20 05:53:35.000000000 +0000 @@ -0,0 +1,343 @@ +From: =?utf-8?q?Timo_R=C3=B6hling?= +Date: Sun, 16 Aug 2026 15:45:40 +0200 +Subject: Fix CVE-2026-53584: unsanitized submodule paths + +Origin: upstream, https://github.com/libgit2/libgit2/commit/ec7371da9f359cd8293e9108e7a0b1c1b61b67c4 +--- + src/libgit2/submodule.c | 89 +++++++++++++++++++++++++---- + src/util/fs_path.h | 20 +++---- + tests/libgit2/submodule/escape.c | 60 ++++++++++++++++++- + tests/libgit2/submodule/submodule_helpers.c | 1 + + 4 files changed, 147 insertions(+), 23 deletions(-) + +diff --git a/src/libgit2/submodule.c b/src/libgit2/submodule.c +index a3bfc78..ddaf949 100644 +--- a/src/libgit2/submodule.c ++++ b/src/libgit2/submodule.c +@@ -77,6 +77,7 @@ static void submodule_get_index_status(unsigned int *, git_submodule *); + static void submodule_get_wd_status(unsigned int *, git_submodule *, git_repository *, git_submodule_ignore_t); + static void submodule_update_from_index_entry(git_submodule *sm, const git_index_entry *ie); + static void submodule_update_from_head_data(git_submodule *sm, mode_t mode, const git_oid *id); ++static int path_is_valid(git_repository *repo, const char *path); + + static int submodule_cmp(const void *a, const void *b) + { +@@ -221,6 +222,7 @@ static int load_submodule_names(git_submodule_namemap **out, git_repository *rep + + while ((error = git_config_next(&entry, iter)) == 0) { + const char *fdot, *ldot; ++ + fdot = strchr(entry->name, '.'); + ldot = strrchr(entry->name, '.'); + +@@ -233,6 +235,7 @@ static int load_submodule_names(git_submodule_namemap **out, git_repository *rep + + git_str_clear(&buf); + git_str_put(&buf, fdot + 1, ldot - fdot - 1); ++ + isvalid = git_submodule_name_is_valid(repo, buf.ptr, 0); + if (isvalid < 0) { + error = isvalid; +@@ -241,6 +244,14 @@ static int load_submodule_names(git_submodule_namemap **out, git_repository *rep + if (!isvalid) + continue; + ++ isvalid = path_is_valid(repo, entry->value); ++ if (isvalid < 0) { ++ error = isvalid; ++ goto out; ++ } ++ if (!isvalid) ++ continue; ++ + if ((value = git__strdup(entry->value)) == NULL) { + error = -1; + goto out; +@@ -319,9 +330,9 @@ int git_submodule__lookup_with_cache( + const char *name, /* trailing slash is allowed */ + git_submodule_cache *cache) + { +- int error; +- unsigned int location; + git_submodule *sm; ++ unsigned int location; ++ int error; + + GIT_ASSERT_ARG(repo); + GIT_ASSERT_ARG(name); +@@ -344,7 +355,17 @@ int git_submodule__lookup_with_cache( + if ((error = submodule_alloc(&sm, repo, name)) < 0) + return error; + +- if ((error = git_submodule_reload(sm, false)) < 0) { ++ /* ++ * Only try to reload if they gave us a valid _name_; if this is a ++ * path, we'll do some lookups then try to reload to populate. ++ */ ++ if (git_submodule_name_is_valid(sm->repo, name, 0) <= 0) { ++ error = GIT_ENOTFOUND; ++ } else { ++ error = git_submodule_reload(sm, false); ++ } ++ ++ if (error < 0 && error != GIT_ENOTFOUND) { + git_submodule_free(sm); + return error; + } +@@ -403,6 +424,7 @@ int git_submodule__lookup_with_cache( + /* If we still haven't found it, do the WD check */ + if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) { + git_submodule_free(sm); ++ git_error_set(GIT_ERROR_SUBMODULE, "invalid submodule name: '%s'", name); + error = GIT_ENOTFOUND; + + /* If it's not configured, we still check if there's a repo at the path */ +@@ -454,6 +476,27 @@ int git_submodule_name_is_valid(git_repository *repo, const char *name, int flag + return isvalid; + } + ++static int path_is_valid(git_repository *repo, const char *path) ++{ ++ git_str buf = GIT_STR_INIT; ++ int flags = GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS & ++ ~GIT_FS_PATH_REJECT_EMPTY_COMPONENT; ++ int error, isvalid; ++ ++ /* Avoid allocating a new string if we can avoid it */ ++ if (strchr(path, '\\') != NULL) { ++ if ((error = git_fs_path_normalize_slashes(&buf, path)) < 0) ++ return error; ++ } else { ++ git_str_attach_notowned(&buf, path, strlen(path)); ++ } ++ ++ isvalid = git_path_is_valid(repo, buf.ptr, 0, flags); ++ git_str_dispose(&buf); ++ ++ return isvalid; ++} ++ + static void submodule_free_dup(void *sm) + { + git_submodule_free(sm); +@@ -1714,18 +1757,34 @@ static int submodule_update_head(git_submodule *submodule) + return 0; + } + ++static int submodule_name_error(const char *name) ++{ ++ git_error_set(GIT_ERROR_SUBMODULE, ++ "invalid value for submodule name: '%s'", name); ++ return GIT_EINVALID; ++} ++ ++static int submodule_config_error(const char *property, const char *value) ++{ ++ git_error_set(GIT_ERROR_SUBMODULE, ++ "invalid value for submodule '%s' property: '%s'", property, value); ++ return GIT_EINVALID; ++} ++ + int git_submodule_reload(git_submodule *sm, int force) + { + git_config *mods = NULL; +- int error; ++ int valid, error = 0; + + GIT_UNUSED(force); + + GIT_ASSERT_ARG(sm); + +- if ((error = git_submodule_name_is_valid(sm->repo, sm->name, 0)) <= 0) ++ if ((valid = git_submodule_name_is_valid(sm->repo, sm->name, 0)) <= 0) { + /* This should come with a warning, but we've no API for that */ ++ error = valid ? valid : submodule_name_error(sm->name); + goto out; ++ } + + if (git_repository_is_bare(sm->repo)) + goto out; +@@ -1748,6 +1807,12 @@ int git_submodule_reload(git_submodule *sm, int force) + (error = submodule_update_head(sm)) < 0) + goto out; + ++ if ((valid = path_is_valid(sm->repo, sm->path)) <= 0) { ++ /* This should come with a warning, but we've no API for that */ ++ error = valid ? valid : submodule_config_error("path", sm->path); ++ goto out; ++ } ++ + out: + git_config_free(mods); + return error; +@@ -1931,13 +1996,6 @@ void git_submodule_free(git_submodule *sm) + GIT_REFCOUNT_DEC(sm, submodule_release); + } + +-static int submodule_config_error(const char *property, const char *value) +-{ +- git_error_set(GIT_ERROR_INVALID, +- "invalid value for submodule '%s' property: '%s'", property, value); +- return -1; +-} +- + int git_submodule_parse_ignore(git_submodule_ignore_t *out, const char *value) + { + int val; +@@ -2136,6 +2194,13 @@ static int submodule_load_each(const git_config_entry *entry, void *payload) + goto done; + } + ++ isvalid = path_is_valid(data->repo, sm->path); ++ if (isvalid <= 0) { ++ git_submodule_free(sm); ++ error = isvalid; ++ goto done; ++ } ++ + if ((error = git_submodule_cache_put(cache, sm->name, sm)) < 0) + goto done; + +diff --git a/src/util/fs_path.h b/src/util/fs_path.h +index 43f7951..1f29ba5 100644 +--- a/src/util/fs_path.h ++++ b/src/util/fs_path.h +@@ -632,18 +632,18 @@ extern int git_fs_path_from_url_or_path(git_str *local_path_out, const char *url + */ + #ifdef GIT_WIN32 + # define GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS \ +- GIT_FS_PATH_REJECT_EMPTY_COMPONENT | \ +- GIT_FS_PATH_REJECT_TRAVERSAL | \ +- GIT_FS_PATH_REJECT_BACKSLASH | \ +- GIT_FS_PATH_REJECT_TRAILING_DOT | \ +- GIT_FS_PATH_REJECT_TRAILING_SPACE | \ +- GIT_FS_PATH_REJECT_TRAILING_COLON | \ +- GIT_FS_PATH_REJECT_DOS_PATHS | \ +- GIT_FS_PATH_REJECT_NT_CHARS ++ (GIT_FS_PATH_REJECT_EMPTY_COMPONENT | \ ++ GIT_FS_PATH_REJECT_TRAVERSAL | \ ++ GIT_FS_PATH_REJECT_BACKSLASH | \ ++ GIT_FS_PATH_REJECT_TRAILING_DOT | \ ++ GIT_FS_PATH_REJECT_TRAILING_SPACE | \ ++ GIT_FS_PATH_REJECT_TRAILING_COLON | \ ++ GIT_FS_PATH_REJECT_DOS_PATHS | \ ++ GIT_FS_PATH_REJECT_NT_CHARS) + #else + # define GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS \ +- GIT_FS_PATH_REJECT_EMPTY_COMPONENT | \ +- GIT_FS_PATH_REJECT_TRAVERSAL ++ (GIT_FS_PATH_REJECT_EMPTY_COMPONENT | \ ++ GIT_FS_PATH_REJECT_TRAVERSAL) + #endif + + /** +diff --git a/tests/libgit2/submodule/escape.c b/tests/libgit2/submodule/escape.c +index bcd52b5..e644f83 100644 +--- a/tests/libgit2/submodule/escape.c ++++ b/tests/libgit2/submodule/escape.c +@@ -16,6 +16,9 @@ void test_submodule_escape__cleanup(void) + #define EVIL_SM_NAME_WINDOWS "..\\\\..\\\\modules\\\\evil" + #define EVIL_SM_NAME_WINDOWS_UNESC "..\\..\\modules\\evil" + ++#define ESCAPE_SM_NAME "escape" ++#define ESCAPE_SM_PATH "../escape-target" ++ + static int find_evil(git_submodule *sm, const char *name, void *payload) + { + int *foundit = (int *) payload; +@@ -23,7 +26,8 @@ static int find_evil(git_submodule *sm, const char *name, void *payload) + GIT_UNUSED(sm); + + if (!git__strcmp(EVIL_SM_NAME, name) || +- !git__strcmp(EVIL_SM_NAME_WINDOWS_UNESC, name)) ++ !git__strcmp(EVIL_SM_NAME_WINDOWS_UNESC, name) || ++ !git__strcmp(ESCAPE_SM_NAME, name)) + *foundit = true; + + return 0; +@@ -49,7 +53,9 @@ void test_submodule_escape__from_gitdir(void) + foundit = 0; + cl_git_pass(git_submodule_foreach(g_repo, find_evil, &foundit)); + cl_assert_equal_i(0, foundit); ++ + cl_git_fail_with(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, EVIL_SM_NAME)); ++ + /* + * We do know about this as it's in the index and HEAD, but the data is + * incomplete as there is no configured data for it (we pretend it +@@ -83,7 +89,9 @@ void test_submodule_escape__from_gitdir_windows(void) + foundit = 0; + cl_git_pass(git_submodule_foreach(g_repo, find_evil, &foundit)); + cl_assert_equal_i(0, foundit); ++ + cl_git_fail_with(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, EVIL_SM_NAME_WINDOWS_UNESC)); ++ + /* + * We do know about this as it's in the index and HEAD, but the data is + * incomplete as there is no configured data for it (we pretend it +@@ -96,3 +104,53 @@ void test_submodule_escape__from_gitdir_windows(void) + cl_assert_equal_i(GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_HEAD, sm_location); + git_submodule_free(sm); + } ++ ++void test_submodule_escape__from_path_cannot_be_enumerated(void) ++{ ++ git_str buf = GIT_STR_INIT; ++ int foundit; ++ ++ g_repo = setup_fixture_submodule_simple(); ++ ++ cl_git_pass(git_str_joinpath(&buf, git_repository_workdir(g_repo), ".gitmodules")); ++ cl_git_rewritefile(buf.ptr, ++ "[submodule \"" ESCAPE_SM_NAME "\"]\n" ++ " path = " ESCAPE_SM_PATH "\n" ++ " url = https://github.com/libgit2/TestGitRepository.git\n"); ++ git_str_dispose(&buf); ++ ++ foundit = 0; ++ cl_git_pass(git_submodule_foreach(g_repo, find_evil, &foundit)); ++ cl_assert_equal_i(0, foundit); ++} ++ ++void test_submodule_escape__from_path_cannot_be_lookedup(void) ++{ ++ git_str buf = GIT_STR_INIT; ++ git_submodule *sm; ++ ++ g_repo = setup_fixture_submodule_simple(); ++ ++ cl_git_pass(git_str_joinpath(&buf, git_repository_workdir(g_repo), ".gitmodules")); ++ cl_git_rewritefile(buf.ptr, ++ "[submodule \"" ESCAPE_SM_NAME "\"]\n" ++ " path = " ESCAPE_SM_PATH "\n" ++ " url = https://github.com/libgit2/TestGitRepository.git\n"); ++ git_str_dispose(&buf); ++ ++ cl_git_fail_with(GIT_EINVALID, git_submodule_lookup(&sm, g_repo, ESCAPE_SM_NAME)); ++ cl_git_fail_with(GIT_EINVALID, git_submodule_lookup(&sm, g_repo, ESCAPE_SM_PATH)); ++} ++ ++void test_submodule_escape__during_add(void) ++{ ++ git_submodule *sm; ++ ++ g_repo = setup_fixture_submodule_simple(); ++ ++ cl_git_fail_with(GIT_EINVALID, git_submodule_add_setup( ++ &sm, g_repo, ++ "https://github.com/libgit2/TestGitRepository.git", ++ ESCAPE_SM_PATH, ++ 1)); ++} +diff --git a/tests/libgit2/submodule/submodule_helpers.c b/tests/libgit2/submodule/submodule_helpers.c +index b8fc9f6..d62c734 100644 +--- a/tests/libgit2/submodule/submodule_helpers.c ++++ b/tests/libgit2/submodule/submodule_helpers.c +@@ -4,6 +4,7 @@ + #include "posix.h" + #include "submodule_helpers.h" + #include "git2/sys/repository.h" ++#include "submodule.h" + + /* rewrite gitmodules -> .gitmodules + * rewrite the empty or relative urls inside each module diff -Nru libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53585.patch libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53585.patch --- libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53585.patch 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53585.patch 2026-08-20 05:53:35.000000000 +0000 @@ -0,0 +1,115 @@ +From: =?utf-8?q?Timo_R=C3=B6hling?= +Date: Wed, 19 Aug 2026 09:29:06 +0200 +Subject: Fix CVE-2026-53585: limit pack object size to 2GiB + +Origin: upstream, https://github.com/libgit2/libgit2/commit/0cdfdd5fa8f8514c82413025e1e0808866cf7c30 +--- + include/git2/common.h | 14 +++++++++++++- + src/libgit2/delta.c | 8 ++++++++ + src/libgit2/indexer.c | 5 +++-- + src/libgit2/settings.c | 9 +++++++++ + 4 files changed, 33 insertions(+), 3 deletions(-) + +diff --git a/include/git2/common.h b/include/git2/common.h +index 40a3903..70f51a8 100644 +--- a/include/git2/common.h ++++ b/include/git2/common.h +@@ -257,7 +257,9 @@ typedef enum { + GIT_OPT_GET_SERVER_TIMEOUT, + GIT_OPT_SET_USER_AGENT_PRODUCT, + GIT_OPT_GET_USER_AGENT_PRODUCT, +- GIT_OPT_ADD_SSL_X509_CERT ++ GIT_OPT_ADD_SSL_X509_CERT, ++ GIT_OPT_GET_PACK_MAX_OBJECT_SIZE, ++ GIT_OPT_SET_PACK_MAX_OBJECT_SIZE + } git_libgit2_opt_t; + + /** +@@ -563,6 +565,16 @@ typedef enum { + * > Sets the timeout (in milliseconds) for reading from and writing + * > to a remote server. Set to 0 to use the system default. + * ++ * opts(GIT_OPT_GET_PACK_MAX_OBJECT_SIZE, size_t *out) ++ * > Gets the maximum size of a declared packfile object. This can ++ * > be used to limit maximum memory usage when fetching from a ++ * > remote. ++ * ++ * opts(GIT_OPT_SET_PACK_MAX_OBJECT_SIZE, size_t object_size) ++ * > Set the maximum size of an object that libgit2 will allow in ++ * > a pack file when downloading a pack file from a remote. ++ * > The default is 2 GiB. ++ * + * @param option Option key + * @return 0 on success, <0 on failure + */ +diff --git a/src/libgit2/delta.c b/src/libgit2/delta.c +index 2d2c5fa..8f8833a 100644 +--- a/src/libgit2/delta.c ++++ b/src/libgit2/delta.c +@@ -13,6 +13,8 @@ + #define RABIN_SHIFT 23 + #define RABIN_WINDOW 16 + ++extern size_t git_indexer__max_object_size; ++ + static const unsigned int T[256] = { + 0x00000000, 0xab59b4d1, 0x56b369a2, 0xfdeadd73, 0x063f6795, 0xad66d344, + 0x508c0e37, 0xfbd5bae6, 0x0c7ecf2a, 0xa7277bfb, 0x5acda688, 0xf1941259, +@@ -561,6 +563,12 @@ int git_delta_apply( + return -1; + } + ++ if (res_sz > git_indexer__max_object_size) { ++ git_error_set(GIT_ERROR_INVALID, ++ "failed to apply delta: overly large object"); ++ return -1; ++ } ++ + GIT_ERROR_CHECK_ALLOC_ADD(&alloc_sz, res_sz, 1); + res_dp = git__malloc(alloc_sz); + GIT_ERROR_CHECK_ALLOC(res_dp); +diff --git a/src/libgit2/indexer.c b/src/libgit2/indexer.c +index e62daac..a0199e6 100644 +--- a/src/libgit2/indexer.c ++++ b/src/libgit2/indexer.c +@@ -24,10 +24,11 @@ + #include "object.h" + #include "hashmap_oid.h" + +-size_t git_indexer__max_objects = UINT32_MAX; +- + #define UINT31_MAX (0x7FFFFFFF) + ++size_t git_indexer__max_objects = UINT32_MAX; ++size_t git_indexer__max_object_size = UINT31_MAX; ++ + GIT_HASHMAP_OID_SETUP(git_indexer_oidmap, git_oid *); + + struct entry { +diff --git a/src/libgit2/settings.c b/src/libgit2/settings.c +index f4c2453..7e7ff61 100644 +--- a/src/libgit2/settings.c ++++ b/src/libgit2/settings.c +@@ -44,6 +44,7 @@ extern size_t git_mwindow__window_size; + extern size_t git_mwindow__mapped_limit; + extern size_t git_mwindow__file_limit; + extern size_t git_indexer__max_objects; ++extern size_t git_indexer__max_object_size; + extern bool git_disable_pack_keep_file_checks; + extern int git_odb__packed_priority; + extern int git_odb__loose_priority; +@@ -358,6 +359,14 @@ int git_libgit2_opts(int key, ...) + *(va_arg(ap, size_t *)) = git_indexer__max_objects; + break; + ++ case GIT_OPT_SET_PACK_MAX_OBJECT_SIZE: ++ git_indexer__max_object_size = va_arg(ap, size_t); ++ break; ++ ++ case GIT_OPT_GET_PACK_MAX_OBJECT_SIZE: ++ *(va_arg(ap, size_t *)) = git_indexer__max_object_size; ++ break; ++ + case GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS: + git_disable_pack_keep_file_checks = (va_arg(ap, int) != 0); + break; diff -Nru libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53586.patch libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53586.patch --- libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53586.patch 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53586.patch 2026-08-20 05:53:35.000000000 +0000 @@ -0,0 +1,62 @@ +From: =?utf-8?q?Timo_R=C3=B6hling?= +Date: Sun, 16 Aug 2026 16:07:50 +0200 +Subject: Fix CVE-2026-53586: Pass correct hostname to auth layer after + redirect + +Origin: upstream, https://github.com/libgit2/libgit2/commit/af2b29ad0a74d5bac9751376879ddaf848136d3b +--- + src/libgit2/transports/http.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/libgit2/transports/http.c b/src/libgit2/transports/http.c +index ea81995..f344888 100644 +--- a/src/libgit2/transports/http.c ++++ b/src/libgit2/transports/http.c +@@ -130,12 +130,12 @@ GIT_INLINE(void) free_cred(git_credential **cred) + static int handle_auth( + http_server *server, + const char *server_type, +- const char *url, + unsigned int allowed_schemetypes, + unsigned int allowed_credtypes, + git_credential_acquire_cb callback, + void *callback_payload) + { ++ git_str server_url = GIT_STR_INIT; + int error = 1; + + if (server->cred) +@@ -154,7 +154,8 @@ static int handle_auth( + } + + if (error > 0 && callback) { +- error = callback(&server->cred, url, server->url.username, allowed_credtypes, callback_payload); ++ if ((error = git_net_url_fmt(&server_url, &server->url)) == 0) ++ error = callback(&server->cred, server_url.ptr, server->url.username, allowed_credtypes, callback_payload); + + /* treat GIT_PASSTHROUGH as if callback isn't set */ + if (error == GIT_PASSTHROUGH) +@@ -169,6 +170,7 @@ static int handle_auth( + if (!error) + server->auth_schemetypes = allowed_schemetypes; + ++ git_str_dispose(&server_url); + return error; + } + +@@ -188,7 +190,6 @@ GIT_INLINE(int) handle_remote_auth( + return handle_auth( + &transport->server, + SERVER_TYPE_REMOTE, +- transport->owner->url, + response->server_auth_schemetypes, + response->server_auth_credtypes, + connect_opts->callbacks.credentials, +@@ -211,7 +212,6 @@ GIT_INLINE(int) handle_proxy_auth( + return handle_auth( + &transport->proxy, + SERVER_TYPE_PROXY, +- connect_opts->proxy_opts.url, + response->server_auth_schemetypes, + response->proxy_auth_credtypes, + connect_opts->proxy_opts.credentials, diff -Nru libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53587.patch libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53587.patch --- libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53587.patch 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-53587.patch 2026-08-20 05:53:35.000000000 +0000 @@ -0,0 +1,23 @@ +From: =?utf-8?q?Timo_R=C3=B6hling?= +Date: Sun, 16 Aug 2026 15:51:56 +0200 +Subject: Fix CVE-2026-53587: read buffer overflow in capability check + +Origin: upstream, https://github.com/libgit2/libgit2/commit/affda60c10fcef16723451c0d7dc71b71dc20ad3 +--- + src/libgit2/transports/smart_pkt.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/libgit2/transports/smart_pkt.c b/src/libgit2/transports/smart_pkt.c +index 7ea8676..54a1b2e 100644 +--- a/src/libgit2/transports/smart_pkt.c ++++ b/src/libgit2/transports/smart_pkt.c +@@ -236,7 +236,8 @@ static int set_data( + len > (size_t)((caps - line) + 1)) { + caps++; + +- if (strncmp(caps, "object-format=", CONST_STRLEN("object-format=")) == 0) ++ if (len - (caps - line) >= CONST_STRLEN("object-format=") && ++ strncmp(caps, "object-format=", CONST_STRLEN("object-format=")) == 0) + format_str = caps + CONST_STRLEN("object-format="); + else if ((format_str = strstr(caps, " object-format=")) != NULL) + format_str += CONST_STRLEN(" object-format="); diff -Nru libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-5917.patch libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-5917.patch --- libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-5917.patch 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-1.9.0+ds/debian/patches/backports/CVE-2026-5917.patch 2026-08-20 05:53:35.000000000 +0000 @@ -0,0 +1,92 @@ +From: =?utf-8?q?Timo_R=C3=B6hling?= +Date: Sun, 16 Aug 2026 15:10:13 +0200 +Subject: Fix CVE-2026-5917: shell command injection in SSH transport + +Origin: upstream, https://github.com/libgit2/libgit2/commit/b2105b8e60798cb28086d4c648b1cb4854eadccb +--- + src/libgit2/transports/ssh_libssh2.c | 65 +++++++++++++++++++++++++++++++++++- + 1 file changed, 64 insertions(+), 1 deletion(-) + +diff --git a/src/libgit2/transports/ssh_libssh2.c b/src/libgit2/transports/ssh_libssh2.c +index 6469c8d..376987c 100644 +--- a/src/libgit2/transports/ssh_libssh2.c ++++ b/src/libgit2/transports/ssh_libssh2.c +@@ -57,6 +57,69 @@ static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg) + git_error_set(GIT_ERROR_SSH, "%s: %s", errmsg, ssherr); + } + ++/* Backported to support not only escape prefix, but also escape suffix */ ++static int backported_git_str_puts_escaped( ++ git_str *buf, ++ const char *string, ++ const char *esc_chars, ++ const char *esc_prefix, ++ const char *esc_suffix) ++{ ++ const char *scan; ++ size_t total = 0, count, alloclen; ++ size_t esc_prefix_len = esc_prefix ? strlen(esc_prefix) : 0; ++ size_t esc_suffix_len = esc_suffix ? strlen(esc_suffix) : 0; ++ ++ if (!string) ++ return 0; ++ ++ for (scan = string; *scan; ) { ++ /* count run of non-escaped characters */ ++ count = strcspn(scan, esc_chars); ++ total += count; ++ scan += count; ++ /* count run of escaped characters */ ++ count = strspn(scan, esc_chars); ++ total += count * (esc_prefix_len + esc_suffix_len + 1); ++ scan += count; ++ } ++ ++ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, total, 1); ++ if (git_str_grow_by(buf, alloclen) < 0) ++ return -1; ++ ++ for (scan = string; *scan; ) { ++ count = strcspn(scan, esc_chars); ++ ++ memmove(buf->ptr + buf->size, scan, count); ++ scan += count; ++ buf->size += count; ++ ++ for (count = strspn(scan, esc_chars); count > 0; --count) { ++ /* copy escape prefix sequence */ ++ if (esc_prefix) { ++ memmove(buf->ptr + buf->size, esc_prefix, esc_prefix_len); ++ buf->size += esc_prefix_len; ++ } ++ ++ /* copy character to be escaped */ ++ buf->ptr[buf->size] = *scan; ++ buf->size++; ++ scan++; ++ ++ /* copy escape suffix sequence */ ++ if (esc_suffix) { ++ memmove(buf->ptr + buf->size, esc_suffix, esc_suffix_len); ++ buf->size += esc_suffix_len; ++ } ++ } ++ } ++ ++ buf->ptr[buf->size] = '\0'; ++ ++ return 0; ++} ++ + /* + * Create a git protocol request. + * +@@ -78,7 +141,7 @@ static int gen_proto(git_str *request, const char *cmd, git_net_url *url) + + git_str_puts(request, cmd); + git_str_puts(request, " '"); +- git_str_puts(request, repo); ++ backported_git_str_puts_escaped(request, repo, "'!", "'\\", "'"); + git_str_puts(request, "'"); + + if (git_str_oom(request)) diff -Nru libgit2-1.9.0+ds/debian/patches/series libgit2-1.9.0+ds/debian/patches/series --- libgit2-1.9.0+ds/debian/patches/series 2025-04-03 15:28:22.000000000 +0000 +++ libgit2-1.9.0+ds/debian/patches/series 2026-08-20 05:53:35.000000000 +0000 @@ -1,3 +1,9 @@ disable-online-tests.patch disable-flaky-stat-tests.patch Use-Multi-Arch-destination-for-CMake-config.patch +backports/CVE-2026-5917.patch +backports/CVE-2026-53583.patch +backports/CVE-2026-53584.patch +backports/CVE-2026-53585.patch +backports/CVE-2026-53586.patch +backports/CVE-2026-53587.patch