From e46295933053f27a6c355adc715b448b86cb1da5 Mon Sep 17 00:00:00 2001 From: "Sebastian (Tiedtke) Huckleberry" Date: Tue, 17 Dec 2024 12:14:53 -0800 Subject: [PATCH] Unescape shell literal if it's not a placeholder (#717) Prompt messages are unquoted which requires them to be valid shell. Any values not quoted become prompt messages and are exclusively for human consumption. This is why we're removing backslashes from any form of parentheses. Fixes #709. --- internal/command/program_resolver.go | 19 +++++++++++++++++- internal/command/program_resolver_test.go | 24 +++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/internal/command/program_resolver.go b/internal/command/program_resolver.go index 84cc60e7c..c1a33ef77 100644 --- a/internal/command/program_resolver.go +++ b/internal/command/program_resolver.go @@ -254,7 +254,12 @@ func (r *ProgramResolver) findOriginalValue(decl *syntax.DeclClause) (string, bo return true }) - return strings.Join(fragments, " "), isPlaceholder + v := strings.Join(fragments, " ") + if !isPlaceholder { + v = unescapeShellLiteral(v) + } + + return v, isPlaceholder } func (r *ProgramResolver) findEnvValue(name string) (string, bool) { @@ -378,3 +383,15 @@ func (r *ProgramResolver) hasExpr(node syntax.Node) (found bool) { }) return } + +func unescapeShellLiteral(escaped string) string { + replacer := strings.NewReplacer( + `\(`, `(`, + `\)`, `)`, + `\{`, `{`, + `\}`, `}`, + `\[`, `[`, + `\]`, `]`, + ) + return replacer.Replace(escaped) +} diff --git a/internal/command/program_resolver_test.go b/internal/command/program_resolver_test.go index 4b5c2e003..dfc9e5c68 100644 --- a/internal/command/program_resolver_test.go +++ b/internal/command/program_resolver_test.go @@ -132,6 +132,21 @@ func TestProgramResolverResolve(t *testing.T) { }, modifiedProgram: "#\n# TEST_STRING_SGL_QUOTED_VALUE set in managed env store\n# \"export TEST_STRING_SGL_QUOTED_VALUE='value'\"\n\n", }, + { + name: "shell-escaped prompt message", + program: `export TYPE=[Guest type \(hyperv,proxmox,openstack\)]`, + result: &ProgramResolverResult{ + ModifiedProgram: true, + Variables: []ProgramResolverVarResult{ + { + Status: ProgramResolverStatusUnresolvedWithMessage, + Name: "TYPE", + OriginalValue: "[Guest type (hyperv,proxmox,openstack)]", + }, + }, + }, + modifiedProgram: "#\n# TYPE set in managed env store\n# \"export TYPE=[Guest type \\\\(hyperv,proxmox,openstack\\\\)]\"\n\n", + }, { name: "parameter expression", program: `export TEST_PARAM_EXPR=${TEST:7:0}`, @@ -315,3 +330,12 @@ func TestProgramResolverResolve_SensitiveEnvKeys(t *testing.T) { require.EqualValues(t, "#\n# MY_PASSWORD set in managed env store\n# \"export MY_PASSWORD=super-secret\"\n#\n# MY_SECRET set in managed env store\n# \"export MY_SECRET=also-secret\"\n#\n# MY_PLAIN set in managed env store\n# \"export MY_PLAIN=text\"\n\n", buf.String()) }) } + +func TestUnescapeShellLiteral(t *testing.T) { + assert.Equal(t, `echo "Hello World!"`, unescapeShellLiteral(`echo "Hello World!"`)) + assert.Equal(t, `echo "Hello ${name}!"`, unescapeShellLiteral(`echo "Hello ${name}!"`)) + assert.Equal(t, `[Guest type (hyperv,proxmox,openstack)]`, unescapeShellLiteral(`[Guest type \(hyperv,proxmox,openstack\)]`)) + assert.Equal(t, `[IP of waiting server {foo}]`, unescapeShellLiteral(`[IP of waiting server \{foo\}]`)) + assert.Equal(t, `[Guest\ Type]`, unescapeShellLiteral(`[Guest\ Type]`)) + assert.Equal(t, `[Guest Type]`, unescapeShellLiteral(`\[Guest Type\]`)) +}