Skip to content

Commit 6c64db9

Browse files
mcastorinabryanbeverlycursoragent
authored
Expand tilde manually in TUI (#4827)
* Fix command injection vulnerability in TUI The TUI was building a command string from user input via string concatenation and passing it to `sh -c` through syscall.Exec. This allowed shell metacharacters in any TUI input field (git URI, file path, tokens, etc.) to be interpreted as shell commands. Replace the `sh -c` invocation with a direct syscall.Exec of the trufflehog binary, passing arguments as a proper argv array. This eliminates shell interpretation entirely. Co-authored-by: Cursor <cursoragent@cursor.com> * Add tilde expansion for TUI args after removing shell layer Since sh -c was removed to fix command injection, ~/foo paths entered in the TUI are no longer expanded by a shell. This adds a narrow expandTilde helper that replaces a leading ~ with os.UserHomeDir() before exec, restoring path resolution without reintroducing any shell interpretation. Guards against empty $HOME to prevent ~/foo silently resolving to /foo. Made-with: Cursor --------- Co-authored-by: Bryan Beverly <bryan.beverly@trufflesec.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent afd5336 commit 6c64db9

File tree

1 file changed

+30
-3
lines changed

1 file changed

+30
-3
lines changed

main.go

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,28 @@ var (
280280
usingTUI = false
281281
)
282282

283+
// expandTilde replaces a leading ~ in each argument with the user's home
284+
// directory. This compensates for bypassing the shell (which would normally
285+
// perform this expansion) while still avoiding shell metacharacter injection.
286+
func expandTilde(args []string) []string {
287+
home, err := os.UserHomeDir()
288+
if err != nil || home == "" {
289+
return args
290+
}
291+
out := make([]string, len(args))
292+
for i, arg := range args {
293+
switch {
294+
case arg == "~":
295+
out[i] = home
296+
case strings.HasPrefix(arg, "~/"):
297+
out[i] = home + arg[1:]
298+
default:
299+
out[i] = arg
300+
}
301+
}
302+
return out
303+
}
304+
283305
func init() {
284306
_, _ = maxprocs.Set()
285307

@@ -308,12 +330,17 @@ func init() {
308330
os.Exit(0)
309331
}
310332

311-
binary, err := exec.LookPath("sh")
333+
// The TUI passes user input as literal strings. Since we no longer
334+
// invoke a shell, we must expand ~ ourselves so paths like ~/foo
335+
// resolve correctly.
336+
args = expandTilde(args)
337+
338+
binary, err := exec.LookPath(os.Args[0])
312339
if err == nil {
313340
// On success, this call will never return. On failure, fallthrough
314341
// to overwriting os.Args.
315-
cmd := strings.Join(append(os.Args[:1], args...), " ")
316-
_ = syscall.Exec(binary, []string{"sh", "-c", cmd}, append(os.Environ(), "TUI_PARENT=true"))
342+
execArgs := append([]string{binary}, args...)
343+
_ = syscall.Exec(binary, execArgs, append(os.Environ(), "TUI_PARENT=true"))
317344
}
318345

319346
// Overwrite the Args slice so overseer works properly.

0 commit comments

Comments
 (0)