Skip to content

Commit 3a85efe

Browse files
authored
fix: refactor editor command execution to handle file paths with spaces (#248)
1 parent 633aa11 commit 3a85efe

2 files changed

Lines changed: 63 additions & 2 deletions

File tree

cmd/utils/editor.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ func EditYamlInEditor(objectName string, content string) (string, error) {
5151

5252
editor := config.GetEditor()
5353

54-
// #nosec
55-
cmd := exec.Command("sh", "-c", fmt.Sprintf("%s %s", editor, tmpfile.Name()))
54+
cmd := newEditorCommand(editor, tmpfile.Name())
5655
cmd.Stdin = os.Stdin
5756
cmd.Stdout = os.Stdout
5857
err = cmd.Start()
@@ -75,3 +74,10 @@ func EditYamlInEditor(objectName string, content string) (string, error) {
7574

7675
return string(editedContent), nil
7776
}
77+
78+
// newEditorCommand returns a shell command that runs the configured editor with
79+
// filePath as a single argument, even when filePath contains spaces.
80+
func newEditorCommand(editor, filePath string) *exec.Cmd {
81+
// #nosec G204 -- editor value is user-controlled CLI configuration.
82+
return exec.Command("sh", "-c", fmt.Sprintf(`%s "$1"`, editor), "sem-editor", filePath)
83+
}

cmd/utils/editor_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package utils
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
7+
"testing"
8+
)
9+
10+
func Test__newEditorCommand__PathWithSpacesIsSingleArgument(t *testing.T) {
11+
tmpDir := t.TempDir()
12+
13+
logPath := filepath.Join(tmpDir, "editor-args.log")
14+
scriptPath := filepath.Join(tmpDir, "capture-args.sh")
15+
filePath := filepath.Join(tmpDir, "Notifications-my notification.yml")
16+
17+
script := `#!/bin/sh
18+
log_path="$1"
19+
shift
20+
printf '%s\n' "$#" > "$log_path"
21+
for arg in "$@"; do
22+
printf '%s\n' "$arg" >> "$log_path"
23+
done
24+
`
25+
26+
if err := os.WriteFile(scriptPath, []byte(script), 0o755); err != nil {
27+
t.Fatalf("failed to create capture script: %v", err)
28+
}
29+
30+
editor := scriptPath + " " + logPath
31+
cmd := newEditorCommand(editor, filePath)
32+
33+
output, err := cmd.CombinedOutput()
34+
if err != nil {
35+
t.Fatalf("editor command failed: %v (output: %s)", err, string(output))
36+
}
37+
38+
logData, err := os.ReadFile(logPath)
39+
if err != nil {
40+
t.Fatalf("failed to read captured args: %v", err)
41+
}
42+
43+
lines := strings.Split(strings.TrimSpace(string(logData)), "\n")
44+
if len(lines) < 2 {
45+
t.Fatalf("expected at least 2 lines in capture output, got %d", len(lines))
46+
}
47+
48+
if lines[0] != "1" {
49+
t.Fatalf("expected editor script to receive exactly one file path argument, got %q", lines[0])
50+
}
51+
52+
if lines[1] != filePath {
53+
t.Fatalf("expected file path %q, got %q", filePath, lines[1])
54+
}
55+
}

0 commit comments

Comments
 (0)