Hello, Bookworm is on libgit2 version 1.5.1+ds-1+deb12u1. Four of the six CVEs that Trixie fixed in DSA-6453-1 hit that version, so I've backported the Trixie patches into 1.5.1+ds-1+deb12u2. The debdiff is attached, and I've pushed the changes to a Salsa fork here: https://salsa.debian.org/tsimonq2/libgit2/-/tree/debian/bookworm?ref_type=heads
What was fixed: - CVE-2026-53584: submodule paths from .gitmodules weren't checked for "..", so a malicious repo could push submodule lookup/add/init/update outside the work tree. Those paths now return GIT_EINVALID. - CVE-2026-53585: git_delta_apply would allocate whatever size a delta header claimed, so a tiny pack could force a huge malloc. Delta results are capped at 2 GiB by default (GIT_OPT_SET_PACK_MAX_OBJECT_SIZE can raise it). The two new GIT_OPT_*_PACK_MAX_OBJECT_SIZE values are stuck on the end of git_libgit2_opt_t so existing values don't shift. - CVE-2026-53586: after an offsite redirect on the first HTTP request, the credential callback still saw the original URL, so a helper could hand the first site's credentials to the new host. It now gets the host it's actually talking to. - CVE-2026-5917: the repository path went into single quotes unescaped in the SSH command, so a ' in the path could run commands on the server. It's escaped now. CVE-2026-5917 also closes #1144465 (already closed in unstable by 1.9.7+ds-1). CVE-2026-53583 and CVE-2026-53587 are not an issue on Bookworm. I marked them not-affected in security-tracker commit e5fda4f0c6a330341f8b559c26c84f0665769e22. CVE-2026-53583 lives in the OpenSSL stream backend. Bookworm builds with -DUSE_HTTPS=mbedTLS, openssl.c is behind #ifdef GIT_OPENSSL, and the shipped .so links mbedTLS, not libssl. CVE-2026-53587 is in the object-format capability parsing that landed in 1.6.1. 1.5.1 simply doesn't have that code. Built with sbuild using a Bookworm schroot. dh_auto_test is fine for static and shared, patches apply cleanly, and the only Lintian warning is the same pkg-config one we already had. Offline clar suite: 2804/0/46 on deb12u1, 2807/0/46 on this package (the three new ones are upstream's submodule escape tests). Invasive and util suites match. 936 exported symbols, identical; gensymbols and abidiff are clean. autopkgtest passed. I also tested each fix against deb12u1 and then this package: - CVE-2026-53584: upstream's submodule::escape tests fail 3/5 on deb12u1, and a submodule with path ../escape-target still shows up. All 5 pass here. - CVE-2026-53585: indexing upstream's 150-byte delta_100mb.pack (claims a 100 MiB object) sat around 107 MiB RSS on deb12u1. Here, with the cap at 42 MiB, it refuses it as too large at ~8 MiB RSS. A pack claiming 3 GiB made deb12u1 try a 3 GiB malloc; this package refuses it with the default cap before allocating. - CVE-2026-53586: upstream's redirect_authentication_is_current_host test against a localhost redirect from :7101 to :7102. Callback got :7101 on deb12u1, :7102 on this package. - CVE-2026-5917: no upstream test, so I used a local sshd. A path ending in '; touch /tmp/pwned_5917; ' created the file on deb12u1. Here the path went out escaped, no file, and the server just said it wasn't a git repository. I rebuilt the reverse dependencies against both deb12u1 and this package in Bookworm schroots, and found no regressions. If you would like more details on this, or if I should test something specific, just let me know. Salsa pipeline: https://salsa.debian.org/tsimonq2/libgit2/-/pipelines/1186327 Debusine work request: https://debusine.debian.net/debian/developers/work-request/1434734/ This is my first LTS NMU; feedback is appreciated. If nothing showstopping comes up, I'll upload this and issue the DLA on Monday morning (October 12th, 2026). Best regards, Simon Quigley [email protected]
diff -Nru libgit2-1.5.1+ds/debian/changelog libgit2-1.5.1+ds/debian/changelog --- libgit2-1.5.1+ds/debian/changelog 2024-02-08 05:31:43.000000000 -0600 +++ libgit2-1.5.1+ds/debian/changelog 2026-10-08 12:29:11.000000000 -0500 @@ -1,3 +1,17 @@ +libgit2 (1.5.1+ds-1+deb12u2) bookworm-security; urgency=high + + * Non-maintainer upload by the LTS Team. + * CVE-2026-53584: Reject path traversal in submodule paths read from + .gitmodules. + * CVE-2026-53585: Limit the size of objects produced by delta application + (2 GiB by default) to prevent memory exhaustion. + * CVE-2026-53586: Pass the current host to the credential callback after an + HTTP redirect. + * CVE-2026-5917: Escape the repository path in the SSH remote command to + prevent shell command injection. (Closes: #1144465) + + -- Simon Quigley <[email protected]> Thu, 08 Oct 2026 12:29:11 -0500 + libgit2 (1.5.1+ds-1+deb12u1) bookworm-security; urgency=high * Team upload. diff -Nru libgit2-1.5.1+ds/debian/patches/backports/CVE-2026-53584.patch libgit2-1.5.1+ds/debian/patches/backports/CVE-2026-53584.patch --- libgit2-1.5.1+ds/debian/patches/backports/CVE-2026-53584.patch 1969-12-31 18:00:00.000000000 -0600 +++ libgit2-1.5.1+ds/debian/patches/backports/CVE-2026-53584.patch 2026-10-08 12:15:02.000000000 -0500 @@ -0,0 +1,348 @@ +Description: submodule: reject path traversal in submodule paths (CVE-2026-53584) + Submodule paths loaded from .gitmodules were not checked for ".." + traversal components, so a crafted repository could make submodule + lookup, add, init and update operate on directories outside the + repository working tree. Validate the path as upstream does and reject + invalid entries with GIT_EINVALID. + . + Backported from the trixie patch (1.9.0+ds-2+deb13u1, DSA-6453-1); the + context was adjusted for 1.5 (git_strmap instead of + git_submodule_namemap). Upstream's submodule::escape tests are included. +Author: Edward Thomson <[email protected]> +Origin: backport, https://github.com/libgit2/libgit2/commit/ec7371da9f359cd8293e9108e7a0b1c1b61b67c4 +Bug: https://github.com/libgit2/libgit2/security/advisories/GHSA-cw77-j82w-mchm +Applied-Upstream: 1.9.5, https://github.com/libgit2/libgit2/commit/ec7371da9f359cd8293e9108e7a0b1c1b61b67c4 +Forwarded: not-needed +Last-Update: 2026-10-08 +--- +diff --git a/src/libgit2/submodule.c b/src/libgit2/submodule.c +index 0f4f072..9cadeac 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) + { +@@ -213,6 +214,7 @@ static int load_submodule_names(git_strmap **out, git_repository *repo, git_conf + + while ((error = git_config_next(&entry, iter)) == 0) { + const char *fdot, *ldot; ++ + fdot = strchr(entry->name, '.'); + ldot = strrchr(entry->name, '.'); + +@@ -225,6 +227,7 @@ static int load_submodule_names(git_strmap **out, git_repository *repo, git_conf + + 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; +@@ -233,6 +236,14 @@ static int load_submodule_names(git_strmap **out, git_repository *repo, git_conf + if (!isvalid) + continue; + ++ isvalid = path_is_valid(repo, entry->value); ++ if (isvalid < 0) { ++ error = isvalid; ++ goto out; ++ } ++ if (!isvalid) ++ continue; ++ + if ((error = git_strmap_set(names, git__strdup(entry->value), git_str_detach(&buf))) < 0) { + git_error_set(GIT_ERROR_NOMEMORY, "error inserting submodule into hash table"); + error = -1; +@@ -294,9 +305,9 @@ int git_submodule__lookup_with_cache( + const char *name, /* trailing slash is allowed */ + git_strmap *cache) + { +- int error; +- unsigned int location; + git_submodule *sm; ++ unsigned int location; ++ int error; + + GIT_ASSERT_ARG(repo); + GIT_ASSERT_ARG(name); +@@ -319,7 +330,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; + } +@@ -378,6 +399,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 */ +@@ -429,6 +451,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); +@@ -1667,18 +1710,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; +@@ -1701,6 +1760,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; +@@ -1884,13 +1949,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; +@@ -2089,6 +2147,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_strmap_set(map, sm->name, sm)) < 0) + goto done; + +diff --git a/src/util/fs_path.h b/src/util/fs_path.h +index e5ca673..31da593 100644 +--- a/src/util/fs_path.h ++++ b/src/util/fs_path.h +@@ -609,18 +609,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.5.1+ds/debian/patches/backports/CVE-2026-53585.patch libgit2-1.5.1+ds/debian/patches/backports/CVE-2026-53585.patch --- libgit2-1.5.1+ds/debian/patches/backports/CVE-2026-53585.patch 1969-12-31 18:00:00.000000000 -0600 +++ libgit2-1.5.1+ds/debian/patches/backports/CVE-2026-53585.patch 2026-10-08 12:15:02.000000000 -0500 @@ -0,0 +1,122 @@ +Description: delta: limit the size of objects produced by delta application (CVE-2026-53585) + git_delta_apply() trusted the result size declared in a delta header and + allocated that much memory before validating the delta, so a very small + crafted pack could force huge allocations. Refuse delta results larger + than a configurable maximum (2 GiB by default) and add + GIT_OPT_GET_PACK_MAX_OBJECT_SIZE / GIT_OPT_SET_PACK_MAX_OBJECT_SIZE to + adjust it. The new option values are appended to the enum, so existing + values and the ABI are unchanged. + . + Hand-ported to 1.5.1: the option handling lives in src/libgit2/libgit2.c + (there is no settings.c in 1.5) and the include/git2/common.h enum + context differs. Upstream's test and its binary fixture are not included. +Author: Edward Thomson <[email protected]> +Origin: backport, https://github.com/libgit2/libgit2/commit/0cdfdd5fa8f8514c82413025e1e0808866cf7c30 +Bug: https://github.com/libgit2/libgit2/security/advisories/GHSA-27m5-gxxh-x79j +Applied-Upstream: 1.9.5, https://github.com/libgit2/libgit2/commit/0cdfdd5fa8f8514c82413025e1e0808866cf7c30 +Forwarded: not-needed +Last-Update: 2026-10-08 +--- +diff --git a/include/git2/common.h b/include/git2/common.h +index c3e3e7b..495e0bc 100644 +--- a/include/git2/common.h ++++ b/include/git2/common.h +@@ -227,7 +227,9 @@ typedef enum { + GIT_OPT_GET_EXTENSIONS, + GIT_OPT_SET_EXTENSIONS, + GIT_OPT_GET_OWNER_VALIDATION, +- GIT_OPT_SET_OWNER_VALIDATION ++ GIT_OPT_SET_OWNER_VALIDATION, ++ GIT_OPT_GET_PACK_MAX_OBJECT_SIZE, ++ GIT_OPT_SET_PACK_MAX_OBJECT_SIZE + } git_libgit2_opt_t; + + /** +@@ -473,6 +475,16 @@ typedef enum { + * > Set that repository directories should be owned by the current + * > user. The default is to validate ownership. + * ++ * 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 + * @param ... value to set the option + * @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 afb9ce8..c910f18 100644 +--- a/src/libgit2/indexer.c ++++ b/src/libgit2/indexer.c +@@ -24,10 +24,11 @@ + #include "zstream.h" + #include "object.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; ++ + struct entry { + git_oid oid; + uint32_t crc; +diff --git a/src/libgit2/libgit2.c b/src/libgit2/libgit2.c +index 2fda072..c400f83 100644 +--- a/src/libgit2/libgit2.c ++++ b/src/libgit2/libgit2.c +@@ -43,6 +43,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; +@@ -368,6 +369,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.5.1+ds/debian/patches/backports/CVE-2026-53586.patch libgit2-1.5.1+ds/debian/patches/backports/CVE-2026-53586.patch --- libgit2-1.5.1+ds/debian/patches/backports/CVE-2026-53586.patch 1969-12-31 18:00:00.000000000 -0600 +++ libgit2-1.5.1+ds/debian/patches/backports/CVE-2026-53586.patch 2026-10-08 12:15:02.000000000 -0500 @@ -0,0 +1,68 @@ +Description: http: give the credential callback the current host after a redirect (CVE-2026-53586) + After following an offsite initial redirect, the HTTP transport passed + the original remote (or proxy) URL to the credential callback instead of + the redirect target, so a callback that scopes credentials to the + original host could hand them to the redirected host. Format the current + server URL and pass that to the callback, as upstream does. + . + Backported from the trixie patch (applies with offsets only). Upstream's + ci/test.sh change for the online tests does not apply and is left out. +Author: Edward Thomson <[email protected]> +Origin: backport, https://github.com/libgit2/libgit2/commit/af2b29ad0a74d5bac9751376879ddaf848136d3b +Bug: https://github.com/libgit2/libgit2/security/advisories/GHSA-2889-x8f6-mc4x +Applied-Upstream: 1.9.5, https://github.com/libgit2/libgit2/commit/af2b29ad0a74d5bac9751376879ddaf848136d3b +Forwarded: not-needed +Last-Update: 2026-10-08 +--- +diff --git a/src/libgit2/transports/http.c b/src/libgit2/transports/http.c +index 7db5582..ff1c32b 100644 +--- a/src/libgit2/transports/http.c ++++ b/src/libgit2/transports/http.c +@@ -132,12 +132,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) +@@ -156,7 +156,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) +@@ -171,6 +172,7 @@ static int handle_auth( + if (!error) + server->auth_schemetypes = allowed_schemetypes; + ++ git_str_dispose(&server_url); + return error; + } + +@@ -190,7 +192,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, +@@ -213,7 +214,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.5.1+ds/debian/patches/backports/CVE-2026-5917.patch libgit2-1.5.1+ds/debian/patches/backports/CVE-2026-5917.patch --- libgit2-1.5.1+ds/debian/patches/backports/CVE-2026-5917.patch 1969-12-31 18:00:00.000000000 -0600 +++ libgit2-1.5.1+ds/debian/patches/backports/CVE-2026-5917.patch 2026-10-08 12:15:02.000000000 -0500 @@ -0,0 +1,100 @@ +Description: ssh: escape the repository path in the remote command (CVE-2026-5917) + gen_proto() inserted the repository path unescaped inside single quotes + in the command sent with libssh2_channel_exec(), so a path containing a + single quote could inject shell commands that run on the SSH server. + Escape ' and ! in the path as upstream does. + . + 1.5's git_str_puts_escaped() has no suffix argument, so this uses the + self-contained backported_git_str_puts_escaped() helper from the trixie + patch. The file is src/libgit2/transports/ssh.c in 1.5 (ssh_libssh2.c + upstream). +Author: Stephen Gallagher <[email protected]> +Origin: backport, https://github.com/libgit2/libgit2/commit/b2105b8e60798cb28086d4c648b1cb4854eadccb +Bug-Debian: https://bugs.debian.org/1144465 +Applied-Upstream: 1.9.7, https://github.com/libgit2/libgit2/commit/b2105b8e60798cb28086d4c648b1cb4854eadccb +Forwarded: not-needed +Last-Update: 2026-10-08 +--- +diff --git a/src/libgit2/transports/ssh.c b/src/libgit2/transports/ssh.c +index 85e7797..6ab762c 100644 +--- a/src/libgit2/transports/ssh.c ++++ b/src/libgit2/transports/ssh.c +@@ -56,6 +56,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. + * +@@ -77,7 +140,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.5.1+ds/debian/patches/series libgit2-1.5.1+ds/debian/patches/series --- libgit2-1.5.1+ds/debian/patches/series 2024-02-08 05:31:43.000000000 -0600 +++ libgit2-1.5.1+ds/debian/patches/series 2026-10-08 12:27:57.000000000 -0500 @@ -4,3 +4,7 @@ disable-flaky-stat-tests.patch backports/CVE-2024-24575.patch backports/CVE-2024-24577.patch +backports/CVE-2026-5917.patch +backports/CVE-2026-53584.patch +backports/CVE-2026-53585.patch +backports/CVE-2026-53586.patch
OpenPGP_signature.asc
Description: OpenPGP digital signature
