Open
fix CVE-2026-5917 #65
Open
1AAdd on 4 days ago
Total: 2 files changed+203-1
@@ -0,0 +1,198 @@
1+From 7f7f0263e943c1d1af81dd52863b3ef92a5e79bc Mon Sep 17 00:00:00 2001
2+From: litenglong <litenglong@kylinos.cn>
3+Date: Wed, 4 Nov 2026 14:35:04 +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 still uses ssh.c for the libssh2 backend with the older git_buf API.
10+Applied the same fix manually with additional adaptation to preserve
11+percent-decoding behavior:
12+git_buf_decode_percent(request, repo, strlen(repo)) ->
13+decode to temp buffer, then git_buf_puts_escaped(request, decoded, "'!", "'\\", "'").
14+ 
15+Additional changes:
16+- Upgrade git_buf_puts_escaped() from 4-parameter to 5-parameter
17+ implementation to match upstream API (esc_with split into esc_prefix
18+ and esc_suffix, both NULL-safe)
19+- Update all existing call sites to pass NULL as esc_suffix:
20+ * src/buffer.h: git_buf_puts_escape_regex() inline wrapper
21+ * tests/core/buffer.c: test_core_buffer__puts_escaped() test cases
22+- src/transports/ssh.c uses 5-parameter form with prefix+char+suffix
23+ shell escaping pattern
24+- Remove unused `int len` variable from gen_proto()
25+- Preserve percent-decoding: decode to temp git_buf first, then escape
26+ the decoded result for shell safety. This maintains backward
27+ compatibility for URLs containing %XX-encoded characters.
28+- Add test cases for esc_suffix non-NULL (shell quote escaping)
29+ 
30+Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
31+Signed-off-by: litenglong <litenglong@kylinos.cn>
32+Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
33+---
34+ src/buffer.c | 24 ++++++++++++++++++------
35+ src/buffer.h | 8 +++++---
36+ src/transports/ssh.c | 17 ++++++++++++-----
37+ tests/core/buffer.c | 14 +++++++++++---
38+ 4 files changed, 46 insertions(+), 17 deletions(-)
39+ 
40+diff --git a/src/buffer.c b/src/buffer.c
41+index fe087ea..033766b 100644
42+--- a/src/buffer.c
43++++ b/src/buffer.c
44+@@ -1059,10 +1059,13 @@ int git_buf_puts_escaped(
45+ git_buf *buf,
46+ const char *string,
47+ const char *esc_chars,
48+- const char *esc_with)
49++ const char *esc_prefix,
50++ const char *esc_suffix)
51+ {
52+ const char *scan;
53+- size_t total = 0, esc_len = strlen(esc_with), count, alloclen;
54++ size_t total = 0, count, alloclen;
55++ size_t esc_prefix_len = esc_prefix ? strlen(esc_prefix) : 0;
56++ size_t esc_suffix_len = esc_suffix ? strlen(esc_suffix) : 0;
57+
58+ if (!string)
59+ return 0;
60+@@ -1074,7 +1077,7 @@ int git_buf_puts_escaped(
61+ scan += count;
62+ /* count run of escaped characters */
63+ count = strspn(scan, esc_chars);
64+- total += count * (esc_len + 1);
65++ total += count * (esc_prefix_len + esc_suffix_len + 1);
66+ scan += count;
67+ }
68+
69+@@ -1090,13 +1093,22 @@ int git_buf_puts_escaped(
70+ buf->size += count;
71+
72+ for (count = strspn(scan, esc_chars); count > 0; --count) {
73+- /* copy escape sequence */
74+- memmove(buf->ptr + buf->size, esc_with, esc_len);
75+- buf->size += esc_len;
76++ /* copy escape prefix sequence */
77++ if (esc_prefix) {
78++ memmove(buf->ptr + buf->size, esc_prefix, esc_prefix_len);
79++ buf->size += esc_prefix_len;
80++ }
81++
82+ /* copy character to be escaped */
83+ buf->ptr[buf->size] = *scan;
84+ buf->size++;
85+ scan++;
86++
87++ /* copy escape suffix sequence */
88++ if (esc_suffix) {
89++ memmove(buf->ptr + buf->size, esc_suffix, esc_suffix_len);
90++ buf->size += esc_suffix_len;
91++ }
92+ }
93+ }
94+
95+diff --git a/src/buffer.h b/src/buffer.h
96+index 75930e2..6fd8985 100644
97+--- a/src/buffer.h
98++++ b/src/buffer.h
99+@@ -281,21 +281,23 @@ int git_buf_splice(
100+ * @param buf Buffer to append data to
101+ * @param string String to escape and append
102+ * @param esc_chars Characters to be escaped
103+- * @param esc_with String to insert in from of each found character
104++ * @param esc_prefix String to insert before each found character (or NULL)
105++ * @param esc_suffix String to insert after each found character (or NULL)
106+ * @return 0 on success, <0 on failure (probably allocation problem)
107+ */
108+ extern int git_buf_puts_escaped(
109+ git_buf *buf,
110+ const char *string,
111+ const char *esc_chars,
112+- const char *esc_with);
113++ const char *esc_prefix,
114++ const char *esc_suffix);
115+
116+ /**
117+ * Append string escaping characters that are regex special
118+ */
119+ GIT_INLINE(int) git_buf_puts_escape_regex(git_buf *buf, const char *string)
120+ {
121+- return git_buf_puts_escaped(buf, string, "^.[]$()|*+?{}\\", "\\");
122++ return git_buf_puts_escaped(buf, string, "^.[]$()|*+?{}\\", "\\", NULL);
123+ }
124+
125+ /**
126+diff --git a/src/transports/ssh.c b/src/transports/ssh.c
127+index 86cfcc9..dea8af8 100644
128+--- a/src/transports/ssh.c
129++++ b/src/transports/ssh.c
130+@@ -68,7 +68,6 @@ static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
131+ static int gen_proto(git_buf *request, const char *cmd, const char *url)
132+ {
133+ const char *repo;
134+- int len;
135+ size_t i;
136+
137+ for (i = 0; i < ARRAY_SIZE(ssh_prefixes); ++i) {
138+@@ -92,12 +91,20 @@ done:
139+ return -1;
140+ }
141+
142+- len = strlen(cmd) + 1 /* Space */ + 1 /* Quote */ + strlen(repo) + 1 /* Quote */ + 1;
143+-
144+- git_buf_grow(request, len);
145+ git_buf_puts(request, cmd);
146+ git_buf_puts(request, " '");
147+- git_buf_decode_percent(request, repo, strlen(repo));
148++
149++ /* Decode percent-encoding to temp buffer, then escape for shell */
150++ {
151++ git_buf decoded = GIT_BUF_INIT;
152++ if (git_buf_decode_percent(&decoded, repo, strlen(repo)) < 0) {
153++ git_buf_dispose(&decoded);
154++ return -1;
155++ }
156++ git_buf_puts_escaped(request, decoded.ptr, "'!", "'\\", "'");
157++ git_buf_dispose(&decoded);
158++ }
159++
160+ git_buf_puts(request, "'");
161+
162+ if (git_buf_oom(request))
163+diff --git a/tests/core/buffer.c b/tests/core/buffer.c
164+index 2af4a87..d3ec2d2 100644
165+--- a/tests/core/buffer.c
166++++ b/tests/core/buffer.c
167+@@ -693,17 +693,25 @@ void test_core_buffer__puts_escaped(void)
168+ git_buf a = GIT_BUF_INIT;
169+
170+ git_buf_clear(&a);
171+- cl_git_pass(git_buf_puts_escaped(&a, "this is a test", "", ""));
172++ cl_git_pass(git_buf_puts_escaped(&a, "this is a test", "", "", NULL));
173+ cl_assert_equal_s("this is a test", a.ptr);
174+
175+ git_buf_clear(&a);
176+- cl_git_pass(git_buf_puts_escaped(&a, "this is a test", "t", "\\"));
177++ cl_git_pass(git_buf_puts_escaped(&a, "this is a test", "t", "\\", NULL));
178+ cl_assert_equal_s("\\this is a \\tes\\t", a.ptr);
179+
180+ git_buf_clear(&a);
181+- cl_git_pass(git_buf_puts_escaped(&a, "this is a test", "i ", "__"));
182++ cl_git_pass(git_buf_puts_escaped(&a, "this is a test", "i ", "__", NULL));
183+ cl_assert_equal_s("th__is__ __is__ a__ test", a.ptr);
184+
185++ git_buf_clear(&a);
186++ cl_git_pass(git_buf_puts_escaped(&a, "foo'bar!baz", "'!", "'\\", "'"));
187++ cl_assert_equal_s("foo'\\''bar'\\!'baz", a.ptr);
188++
189++ git_buf_clear(&a);
190++ cl_git_pass(git_buf_puts_escaped(&a, "no-match", "'!", "'\\", "'"));
191++ cl_assert_equal_s("no-match", a.ptr);
192++
193+ git_buf_clear(&a);
194+ cl_git_pass(git_buf_puts_escape_regex(&a, "^match\\s*[A-Z]+.*"));
195+ cl_assert_equal_s("\\^match\\\\s\\*\\[A-Z\\]\\+\\.\\*", a.ptr);
196+--
197+2.25.1
198+ 
@@ -1,6 +1,6 @@
1Name: libgit21Name: libgit2
2Version: 1.3.22Version: 1.3.2
3-Release: 33+Release: 4
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
@@ -8,6 +8,7 @@ Source0: https://github.com/libgit2/libgit2/archive/v%{version}.tar.gz
8Patch1: CVE-2023-22742.patch8Patch1: CVE-2023-22742.patch
9# https://github.com/libgit2/libgit2/commit/eb4c1716cd92bf56f2770653a915d5fc01eab8f39# https://github.com/libgit2/libgit2/commit/eb4c1716cd92bf56f2770653a915d5fc01eab8f3
10Patch2: CVE-2024-24577.patch10Patch2: CVE-2024-24577.patch
11+Patch3: CVE-2026-5917-Escape-repo-path-in-gen_proto.patch
11 12 
12BuildRequires: gcc cmake >= 2.8.11 ninja-build http-parser-devel libcurl-devel13BuildRequires: gcc cmake >= 2.8.11 ninja-build http-parser-devel libcurl-devel
13BuildRequires: libssh2-devel openssl-devel python3 zlib-devel14BuildRequires: libssh2-devel openssl-devel python3 zlib-devel
@@ -59,6 +60,9 @@ rm -vr deps
59%{_includedir}/git2*60%{_includedir}/git2*
60 61 
61%changelog62%changelog
63+* Wed Nov 4 2026 litenglong <litenglong@kylinos.cn> - 1.3.2-4
64+- Fix CVE-2026-5917
65+ 
62* Wed Feb 07 2024 yaoxin <yao_xin001@hoperun.com> - 1.3.2-366* Wed Feb 07 2024 yaoxin <yao_xin001@hoperun.com> - 1.3.2-3
63- Fix CVE-2024-2457767- Fix CVE-2024-24577
64 68