1
0
mirror of https://github.com/tw93/Mole.git synced 2026-03-26 10:15:06 +00:00

security: fix CodeQL command injection and path traversal alerts

- Add validatePath() helper to check path safety before external commands
- Validate paths in delete.go (moveToTrash), scanner.go (mdfind, du),
  and main.go (open command)
- Remove overly restrictive character whitelist that rejected valid
  macOS paths (Chinese, emoji, $, ;, etc.)
- Unify path validation logic across all three files

Fixes CodeQL alerts:
- Command injection in osascript (delete.go)
- Command injection in mdfind/du (scanner.go)
- Path traversal in open command (main.go)
This commit is contained in:
Tw93
2026-03-14 08:24:08 +08:00
parent f6acfa774c
commit 951e395ab7
3 changed files with 67 additions and 24 deletions

View File

@@ -775,18 +775,14 @@ func (m model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
}
for path := range m.largeMultiSelected {
go func(p string) {
ctx, cancel := context.WithTimeout(context.Background(), openCommandTimeout)
defer cancel()
_ = exec.CommandContext(ctx, "open", p).Run()
_ = safeOpen(p, false)
}(path)
}
m.status = fmt.Sprintf("Opening %d items...", count)
} else {
selected := m.largeFiles[m.largeSelected]
go func(path string) {
ctx, cancel := context.WithTimeout(context.Background(), openCommandTimeout)
defer cancel()
_ = exec.CommandContext(ctx, "open", path).Run()
_ = safeOpen(path, false)
}(selected.Path)
m.status = fmt.Sprintf("Opening %s...", selected.Name)
}
@@ -800,18 +796,14 @@ func (m model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
}
for path := range m.multiSelected {
go func(p string) {
ctx, cancel := context.WithTimeout(context.Background(), openCommandTimeout)
defer cancel()
_ = exec.CommandContext(ctx, "open", p).Run()
_ = safeOpen(p, false)
}(path)
}
m.status = fmt.Sprintf("Opening %d items...", count)
} else {
selected := m.entries[m.selected]
go func(path string) {
ctx, cancel := context.WithTimeout(context.Background(), openCommandTimeout)
defer cancel()
_ = exec.CommandContext(ctx, "open", path).Run()
_ = safeOpen(path, false)
}(selected.Path)
m.status = fmt.Sprintf("Opening %s...", selected.Name)
}
@@ -829,18 +821,14 @@ func (m model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
}
for path := range m.largeMultiSelected {
go func(p string) {
ctx, cancel := context.WithTimeout(context.Background(), openCommandTimeout)
defer cancel()
_ = exec.CommandContext(ctx, "open", "-R", p).Run()
_ = safeOpen(p, true)
}(path)
}
m.status = fmt.Sprintf("Showing %d items in Finder...", count)
} else {
selected := m.largeFiles[m.largeSelected]
go func(path string) {
ctx, cancel := context.WithTimeout(context.Background(), openCommandTimeout)
defer cancel()
_ = exec.CommandContext(ctx, "open", "-R", path).Run()
_ = safeOpen(path, true)
}(selected.Path)
m.status = fmt.Sprintf("Showing %s in Finder...", selected.Name)
}
@@ -854,18 +842,14 @@ func (m model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
}
for path := range m.multiSelected {
go func(p string) {
ctx, cancel := context.WithTimeout(context.Background(), openCommandTimeout)
defer cancel()
_ = exec.CommandContext(ctx, "open", "-R", p).Run()
_ = safeOpen(p, true)
}(path)
}
m.status = fmt.Sprintf("Showing %d items in Finder...", count)
} else {
selected := m.entries[m.selected]
go func(path string) {
ctx, cancel := context.WithTimeout(context.Background(), openCommandTimeout)
defer cancel()
_ = exec.CommandContext(ctx, "open", "-R", path).Run()
_ = safeOpen(path, true)
}(selected.Path)
m.status = fmt.Sprintf("Showing %s in Finder...", selected.Name)
}
@@ -1172,3 +1156,17 @@ func scanOverviewPathCmd(path string, index int) tea.Cmd {
}
}
}
// safeOpen executes 'open' command with path validation.
func safeOpen(path string, reveal bool) error {
if err := validatePath(path); err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), openCommandTimeout)
defer cancel()
args := []string{path}
if reveal {
args = []string{"-R", path}
}
return exec.CommandContext(ctx, "open", args...).Run()
}