From d9a03b594d5ad7407925d467e2a2f7c1324817dc Mon Sep 17 00:00:00 2001 From: Tim Soethout Date: Thu, 21 May 2026 11:25:35 +0200 Subject: [PATCH 1/2] fix(completion): correct greedy colon parsing in bash completion script --- autocomplete/bash_autocomplete | 2 +- completion_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/autocomplete/bash_autocomplete b/autocomplete/bash_autocomplete index b7442e10aa..42eb17b8b2 100755 --- a/autocomplete/bash_autocomplete +++ b/autocomplete/bash_autocomplete @@ -66,7 +66,7 @@ __%[1]s_bash_autocomplete() { local description="" if [[ "${line}" == *:* ]]; then - token="${line%%:*}" + token="${line%%%%:*}" description="${line#*:}" fi diff --git a/completion_test.go b/completion_test.go index 2da7f622e2..8d6f6c20aa 100644 --- a/completion_test.go +++ b/completion_test.go @@ -158,6 +158,32 @@ func TestCompletionBashAppendsSpace(t *testing.T) { r.Contains(output, "complete -o bashdefault -o default -F __myapp_bash_autocomplete myapp") } +func TestCompletionBashGreedyColonParsing(t *testing.T) { + // Regression test: the bash completion template uses fmt.Sprintf, so + // literal "%" in the template must be escaped as "%%". The token + // extraction must use the greedy ${line%%:*} (double %%) to split on + // the *first* colon. A single % would use ${line%:*} which splits on + // the *last* colon, breaking descriptions that contain colons + // (e.g. "export:Export configs such as: compose-config"). + + cmd := &Command{ + EnableShellCompletion: true, + } + + r := require.New(t) + + bashRender := shellCompletions["bash"] + r.NotNil(bashRender, "bash completion renderer should exist") + + output, err := bashRender(cmd, "myapp") + r.NoError(err) + + // After fmt.Sprintf, the rendered script must contain ${line%%:*} + // (greedy match) not ${line%:*} (non-greedy match). + r.Contains(output, `${line%%:*}`, "token extraction should use greedy %% to match first colon") + r.NotContains(output, `${line%:*}`, "token extraction must not use non-greedy single % (splits on last colon)") +} + func TestCompletionFishFormat(t *testing.T) { // Regression test for https://github.com/urfave/cli/issues/2285 // Fish completion was broken due to incorrect format specifiers From 22cb796b40ba3d69c6e858e7a13b61020a884cb6 Mon Sep 17 00:00:00 2001 From: Tim Soethout Date: Thu, 28 May 2026 10:52:34 +0200 Subject: [PATCH 2/2] fix(tests): update regression test comment for greedy colon parsing retrigger codeql --- completion_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/completion_test.go b/completion_test.go index 8d6f6c20aa..a608e53021 100644 --- a/completion_test.go +++ b/completion_test.go @@ -159,7 +159,8 @@ func TestCompletionBashAppendsSpace(t *testing.T) { } func TestCompletionBashGreedyColonParsing(t *testing.T) { - // Regression test: the bash completion template uses fmt.Sprintf, so + // Regression test for https://github.com/urfave/cli/issues/2335 + // The bash completion template uses fmt.Sprintf, so // literal "%" in the template must be escaped as "%%". The token // extraction must use the greedy ${line%%:*} (double %%) to split on // the *first* colon. A single % would use ${line%:*} which splits on