Skip to content

Commit ec7ae35

Browse files
committed
Merge branch 'disallow-control-characters-in-sideband-channel'
This addresses: - CVE-2024-52005: Insufficient neutralization of ANSI escape sequences in sideband payload can be used to mislead Git users into believing that certain remote-generated messages actually originate from Git. Signed-off-by: Johannes Schindelin <[email protected]>
2 parents 5d50f4e + c7049c2 commit ec7ae35

File tree

4 files changed

+124
-2
lines changed

4 files changed

+124
-2
lines changed

Documentation/config.txt

+2
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,8 @@ include::config/sequencer.txt[]
517517

518518
include::config/showbranch.txt[]
519519

520+
include::config/sideband.txt[]
521+
520522
include::config/sparse.txt[]
521523

522524
include::config/splitindex.txt[]

Documentation/config/sideband.txt

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
sideband.allowControlCharacters::
2+
By default, control characters that are delivered via the sideband
3+
are masked, except ANSI color sequences. This prevents potentially
4+
unwanted ANSI escape sequences from being sent to the terminal. Use
5+
this config setting to override this behavior:
6+
+
7+
--
8+
color::
9+
Allow ANSI color sequences, line feeds and horizontal tabs,
10+
but mask all other control characters. This is the default.
11+
false::
12+
Mask all control characters other than line feeds and
13+
horizontal tabs.
14+
true::
15+
Allow all control characters to be sent to the terminal.
16+
--

sideband.c

+76-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ static struct keyword_entry keywords[] = {
2323
{ "error", GIT_COLOR_BOLD_RED },
2424
};
2525

26+
static enum {
27+
ALLOW_NO_CONTROL_CHARACTERS = 0,
28+
ALLOW_ALL_CONTROL_CHARACTERS = 1,
29+
ALLOW_ANSI_COLOR_SEQUENCES = 2
30+
} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;
31+
2632
/* Returns a color setting (GIT_COLOR_NEVER, etc). */
2733
static int use_sideband_colors(void)
2834
{
@@ -36,6 +42,25 @@ static int use_sideband_colors(void)
3642
if (use_sideband_colors_cached >= 0)
3743
return use_sideband_colors_cached;
3844

45+
switch (git_config_get_maybe_bool("sideband.allowcontrolcharacters", &i)) {
46+
case 0: /* Boolean value */
47+
allow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :
48+
ALLOW_NO_CONTROL_CHARACTERS;
49+
break;
50+
case -1: /* non-Boolean value */
51+
if (git_config_get_string("sideband.allowcontrolcharacters",
52+
&value))
53+
; /* huh? `get_maybe_bool()` returned -1 */
54+
else if (!strcmp(value, "color"))
55+
allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;
56+
else
57+
warning(_("unrecognized value for `sideband."
58+
"allowControlCharacters`: '%s'"), value);
59+
break;
60+
default:
61+
break; /* not configured */
62+
}
63+
3964
if (!git_config_get_string(key, &value)) {
4065
use_sideband_colors_cached = git_config_colorbool(key, value);
4166
} else if (!git_config_get_string("color.ui", &value)) {
@@ -64,6 +89,55 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref
6489
list_config_item(list, prefix, keywords[i].keyword);
6590
}
6691

92+
static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int n)
93+
{
94+
int i;
95+
96+
/*
97+
* Valid ANSI color sequences are of the form
98+
*
99+
* ESC [ [<n> [; <n>]*] m
100+
*/
101+
102+
if (allow_control_characters != ALLOW_ANSI_COLOR_SEQUENCES ||
103+
n < 3 || src[0] != '\x1b' || src[1] != '[')
104+
return 0;
105+
106+
for (i = 2; i < n; i++) {
107+
if (src[i] == 'm') {
108+
strbuf_add(dest, src, i + 1);
109+
return i;
110+
}
111+
if (!isdigit(src[i]) && src[i] != ';')
112+
break;
113+
}
114+
115+
return 0;
116+
}
117+
118+
static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)
119+
{
120+
int i;
121+
122+
if (allow_control_characters == ALLOW_ALL_CONTROL_CHARACTERS) {
123+
strbuf_add(dest, src, n);
124+
return;
125+
}
126+
127+
strbuf_grow(dest, n);
128+
for (; n && *src; src++, n--) {
129+
if (!iscntrl(*src) || *src == '\t' || *src == '\n')
130+
strbuf_addch(dest, *src);
131+
else if ((i = handle_ansi_color_sequence(dest, src, n))) {
132+
src += i;
133+
n -= i;
134+
} else {
135+
strbuf_addch(dest, '^');
136+
strbuf_addch(dest, 0x40 + *src);
137+
}
138+
}
139+
}
140+
67141
/*
68142
* Optionally highlight one keyword in remote output if it appears at the start
69143
* of the line. This should be called for a single line only, which is
@@ -79,7 +153,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
79153
int i;
80154

81155
if (!want_color_stderr(use_sideband_colors())) {
82-
strbuf_add(dest, src, n);
156+
strbuf_add_sanitized(dest, src, n);
83157
return;
84158
}
85159

@@ -112,7 +186,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
112186
}
113187
}
114188

115-
strbuf_add(dest, src, n);
189+
strbuf_add_sanitized(dest, src, n);
116190
}
117191

118192

t/t5409-colorize-remote-messages.sh

+30
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,34 @@ test_expect_success 'fallback to color.ui' '
9898
grep "<BOLD;RED>error<RESET>: error" decoded
9999
'
100100

101+
test_expect_success 'disallow (color) control sequences in sideband' '
102+
write_script .git/color-me-surprised <<-\EOF &&
103+
printf "error: Have you \\033[31mread\\033[m this?\\a\\n" >&2
104+
exec "$@"
105+
EOF
106+
test_config_global uploadPack.packObjectshook ./color-me-surprised &&
107+
test_commit need-at-least-one-commit &&
108+
109+
git clone --no-local . throw-away 2>stderr &&
110+
test_decode_color <stderr >decoded &&
111+
test_grep RED decoded &&
112+
test_grep "\\^G" stderr &&
113+
tr -dc "\\007" <stderr >actual &&
114+
test_must_be_empty actual &&
115+
116+
rm -rf throw-away &&
117+
git -c sideband.allowControlCharacters=false \
118+
clone --no-local . throw-away 2>stderr &&
119+
test_decode_color <stderr >decoded &&
120+
test_grep ! RED decoded &&
121+
test_grep "\\^G" stderr &&
122+
123+
rm -rf throw-away &&
124+
git -c sideband.allowControlCharacters clone --no-local . throw-away 2>stderr &&
125+
test_decode_color <stderr >decoded &&
126+
test_grep RED decoded &&
127+
tr -dc "\\007" <stderr >actual &&
128+
test_file_not_empty actual
129+
'
130+
101131
test_done

0 commit comments

Comments
 (0)