Open
fix CVE-2026-5917 #64
Open
1AAdd on 4 days ago
Total: 2 files changed+199-1
@@ -0,0 +1,194 @@
1+From 35dcd89e842098e3de88ec80d8b432d46ea6902c Mon Sep 17 00:00:00 2001
2+From: litenglong <litenglong@kylinos.cn>
3+Date: Wed, 4 Nov 2026 15:43:46 +0800
4+Subject: [PATCH] CVE-2026-5917: Escape repo path in gen_proto()
5+ 
6+Takes inspiration from 346f28b89d6aae0c3ea0b9f0b0bc0f0afa177fab
7+ 
8+Conflict: upstream modifies ssh_libssh2.c and uses git_str API, but this
9+branch uses ssh.c with the older git_buf API and git_buf_text_* namespace.
10+Applied the same fix manually with adaptation to preserve percent-decoding:
11+git_buf_decode_percent(request, repo, strlen(repo)) ->
12+decode to temp buffer, then git_buf_text_puts_escaped(request, decoded, "'!", "'\\", "'").
13+ 
14+Additional changes:
15+- Upgrade git_buf_text_puts_escaped() from 4-parameter to 5-parameter
16+ implementation (esc_with split into esc_prefix and esc_suffix, both
17+ NULL-safe)
18+- Update all existing call sites to pass NULL as esc_suffix:
19+ * src/buf_text.h: git_buf_text_puts_escape_regex() inline wrapper
20+ * tests/core/buffer.c: test_core_buffer__puts_escaped() test cases
21+- Remove unused `int len` variable from gen_proto()
22+- Preserve percent-decoding: decode to temp git_buf first, then escape
23+ the decoded result for shell safety
24+- Add test cases for esc_suffix non-NULL (shell quote escaping)
25+ 
26+Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
27+Signed-off-by: litenglong <litenglong@kylinos.cn>
28+Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
29+---
30+ src/buf_text.c | 24 ++++++++++++++++++------
31+ src/buf_text.h | 8 +++++---
32+ src/transports/ssh.c | 17 ++++++++++++-----
33+ tests/core/buffer.c | 14 +++++++++++---
34+ 4 files changed, 46 insertions(+), 17 deletions(-)
35+ 
36+diff --git a/src/buf_text.c b/src/buf_text.c
37+index 306980b..824dc38 100644
38+--- a/src/buf_text.c
39++++ b/src/buf_text.c
40+@@ -10,10 +10,13 @@ int git_buf_text_puts_escaped(
41+ git_buf *buf,
42+ const char *string,
43+ const char *esc_chars,
44+- const char *esc_with)
45++ const char *esc_prefix,
46++ const char *esc_suffix)
47+ {
48+ const char *scan;
49+- size_t total = 0, esc_len = strlen(esc_with), count, alloclen;
50++ size_t total = 0, count, alloclen;
51++ size_t esc_prefix_len = esc_prefix ? strlen(esc_prefix) : 0;
52++ size_t esc_suffix_len = esc_suffix ? strlen(esc_suffix) : 0;
53+
54+ if (!string)
55+ return 0;
56+@@ -25,7 +28,7 @@ int git_buf_text_puts_escaped(
57+ scan += count;
58+ /* count run of escaped characters */
59+ count = strspn(scan, esc_chars);
60+- total += count * (esc_len + 1);
61++ total += count * (esc_prefix_len + esc_suffix_len + 1);
62+ scan += count;
63+ }
64+
65+@@ -41,13 +44,22 @@ int git_buf_text_puts_escaped(
66+ buf->size += count;
67+
68+ for (count = strspn(scan, esc_chars); count > 0; --count) {
69+- /* copy escape sequence */
70+- memmove(buf->ptr + buf->size, esc_with, esc_len);
71+- buf->size += esc_len;
72++ /* copy escape prefix sequence */
73++ if (esc_prefix) {
74++ memmove(buf->ptr + buf->size, esc_prefix, esc_prefix_len);
75++ buf->size += esc_prefix_len;
76++ }
77++
78+ /* copy character to be escaped */
79+ buf->ptr[buf->size] = *scan;
80+ buf->size++;
81+ scan++;
82++
83++ /* copy escape suffix sequence */
84++ if (esc_suffix) {
85++ memmove(buf->ptr + buf->size, esc_suffix, esc_suffix_len);
86++ buf->size += esc_suffix_len;
87++ }
88+ }
89+ }
90+
91+diff --git a/src/buf_text.h b/src/buf_text.h
92+index 726b0ae..e826c0a 100644
93+--- a/src/buf_text.h
94++++ b/src/buf_text.h
95+@@ -33,21 +33,23 @@ typedef struct {
96+ * @param buf Buffer to append data to
97+ * @param string String to escape and append
98+ * @param esc_chars Characters to be escaped
99+- * @param esc_with String to insert in from of each found character
100++ * @param esc_prefix String to insert before each found character (or NULL)
101++ * @param esc_suffix String to insert after each found character (or NULL)
102+ * @return 0 on success, <0 on failure (probably allocation problem)
103+ */
104+ extern int git_buf_text_puts_escaped(
105+ git_buf *buf,
106+ const char *string,
107+ const char *esc_chars,
108+- const char *esc_with);
109++ const char *esc_prefix,
110++ const char *esc_suffix);
111+
112+ /**
113+ * Append string escaping characters that are regex special
114+ */
115+ GIT_INLINE(int) git_buf_text_puts_escape_regex(git_buf *buf, const char *string)
116+ {
117+- return git_buf_text_puts_escaped(buf, string, "^.[]$()|*+?{}\\", "\\");
118++ return git_buf_text_puts_escaped(buf, string, "^.[]$()|*+?{}\\", "\\", NULL);
119+ }
120+
121+ /**
122+diff --git a/src/transports/ssh.c b/src/transports/ssh.c
123+index 8098dda..3062c12 100644
124+--- a/src/transports/ssh.c
125++++ b/src/transports/ssh.c
126+@@ -65,7 +65,6 @@ static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
127+ static int gen_proto(git_buf *request, const char *cmd, const char *url)
128+ {
129+ const char *repo;
130+- int len;
131+ size_t i;
132+
133+ for (i = 0; i < ARRAY_SIZE(ssh_prefixes); ++i) {
134+@@ -89,12 +88,20 @@ done:
135+ return -1;
136+ }
137+
138+- len = strlen(cmd) + 1 /* Space */ + 1 /* Quote */ + strlen(repo) + 1 /* Quote */ + 1;
139+-
140+- git_buf_grow(request, len);
141+ git_buf_puts(request, cmd);
142+ git_buf_puts(request, " '");
143+- git_buf_decode_percent(request, repo, strlen(repo));
144++
145++ /* Decode percent-encoding to temp buffer, then escape for shell */
146++ {
147++ git_buf decoded = GIT_BUF_INIT;
148++ if (git_buf_decode_percent(&decoded, repo, strlen(repo)) < 0) {
149++ git_buf_free(&decoded);
150++ return -1;
151++ }
152++ git_buf_text_puts_escaped(request, decoded.ptr, "'!", "'\\", "'");
153++ git_buf_free(&decoded);
154++ }
155++
156+ git_buf_puts(request, "'");
157+
158+ if (git_buf_oom(request))
159+diff --git a/tests/core/buffer.c b/tests/core/buffer.c
160+index c4308cb..e4555bc 100644
161+--- a/tests/core/buffer.c
162++++ b/tests/core/buffer.c
163+@@ -709,17 +709,25 @@ void test_core_buffer__puts_escaped(void)
164+ git_buf a = GIT_BUF_INIT;
165+
166+ git_buf_clear(&a);
167+- cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "", ""));
168++ cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "", "", NULL));
169+ cl_assert_equal_s("this is a test", a.ptr);
170+
171+ git_buf_clear(&a);
172+- cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "t", "\\"));
173++ cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "t", "\\", NULL));
174+ cl_assert_equal_s("\\this is a \\tes\\t", a.ptr);
175+
176+ git_buf_clear(&a);
177+- cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "i ", "__"));
178++ cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "i ", "__", NULL));
179+ cl_assert_equal_s("th__is__ __is__ a__ test", a.ptr);
180+
181++ git_buf_clear(&a);
182++ cl_git_pass(git_buf_text_puts_escaped(&a, "foo'bar!baz", "'!", "'\\", "'"));
183++ cl_assert_equal_s("foo'\\''bar'\\!'baz", a.ptr);
184++
185++ git_buf_clear(&a);
186++ cl_git_pass(git_buf_text_puts_escaped(&a, "no-match", "'!", "'\\", "'"));
187++ cl_assert_equal_s("no-match", a.ptr);
188++
189+ git_buf_clear(&a);
190+ cl_git_pass(git_buf_text_puts_escape_regex(&a, "^match\\s*[A-Z]+.*"));
191+ cl_assert_equal_s("\\^match\\\\s\\*\\[A-Z\\]\\+\\.\\*", a.ptr);
192+--
193+2.25.1
194+ 
@@ -1,6 +1,6 @@
1Name: libgit21Name: libgit2
2Version: 0.27.82Version: 0.27.8
3-Release: 83+Release: 9
4Summary: portable, pure C implementation of the Git core methods4Summary: portable, pure C implementation of the Git core methods
5License: GPLv2 with exceptions5License: GPLv2 with exceptions
6URL: https://libgit2.org6URL: https://libgit2.org
@@ -13,6 +13,7 @@ Patch0004: Remove-error-prone-redundant-test.patch
13Patch0005: CVE-2023-22742.patch13Patch0005: CVE-2023-22742.patch
14# https://github.com/libgit2/libgit2/commit/eb4c1716cd92bf56f2770653a915d5fc01eab8f314# https://github.com/libgit2/libgit2/commit/eb4c1716cd92bf56f2770653a915d5fc01eab8f3
15Patch0006: CVE-2024-24577.patch15Patch0006: CVE-2024-24577.patch
16+Patch0007: CVE-2026-5917-Escape-repo-path-in-gen_proto.patch
16 17 
17BuildRequires: gcc cmake >= 2.8.11 ninja-build http-parser-devel libcurl-devel18BuildRequires: gcc cmake >= 2.8.11 ninja-build http-parser-devel libcurl-devel
18BuildRequires: libssh2-devel openssl-devel python3 zlib-devel19BuildRequires: libssh2-devel openssl-devel python3 zlib-devel
@@ -61,6 +62,9 @@ sed -i '/ADD_TEST(online/s/^/#/' tests/CMakeLists.txt
61%{_includedir}/git2*62%{_includedir}/git2*
62 63 
63%changelog64%changelog
65+* Wed Nov 4 2026 litenglong <litenglong@kylinos.cn> - 0.27.8-9
66+- Fix CVE-2026-5917
67+ 
64* Thu Feb 08 2024 yaoxin <yao_xin001@hoperun.com> - 0.27.8-868* Thu Feb 08 2024 yaoxin <yao_xin001@hoperun.com> - 0.27.8-8
65- Fix CVE-2024-2457769- Fix CVE-2024-24577
66 70