返回 DeepSeek-Reasonix
open_local_path.go
根目录 / desktop / open_local_path.go
1 package main
2
3 import (
4 "fmt"
5 "os"
6 "path/filepath"
7 "strings"
8 )
9
10 // normalizeLocalOpenPath validates and normalizes a user-clicked local path
11 // before it is handed to the OS opener. It accepts either a plain absolute
12 // path (D:\a\b.md or D:/a/b.md) or a file:/// URL produced by the markdown
13 // linkifier; the frontend decodes percent-escapes before calling
14 // OpenLocalPath, so no URL decoding happens here.
15 func normalizeLocalOpenPath(path string) (string, error) {
16 path = strings.TrimSpace(path)
17 if path == "" {
18 return "", os.ErrInvalid
19 }
20 path = strings.TrimPrefix(path, "file:///")
21 // Normalize forward slashes (file URLs, slash-form UNC "//nas/share")
22 // to the platform-native separators the opener expects.
23 path = filepath.FromSlash(path)
24 if !filepath.IsAbs(path) {
25 return "", fmt.Errorf("path is not absolute: %q", path)
26 }
27 return path, nil
28 }
29
30 // openTargetAllowed reports whether a resolved path may be handed to the OS
31 // "open" verb. Directories and documents open normally; executable targets
32 // are refused because OpenLocalPath is fed by AI-generated chat content —
33 // a prompt-injected or hallucinated ".bat" path must not run on click.
34 // openWorkspacePath itself stays untouched: it is also used by
35 // RevealWorkspacePathForTab with trusted workspace inputs.
36 var executableOpenSuffixes = map[string]bool{
37 ".bat": true, ".cmd": true, ".com": true, ".exe": true,
38 ".ps1": true, ".vbs": true, ".jse": true, ".js": true,
39 ".lnk": true, ".url": true, ".scr": true, ".msi": true,
40 ".reg": true, ".pif": true, ".hta": true, ".wsf": true,
41 }
42
43 func openTargetAllowed(path string, isDir bool) bool {
44 if isDir {
45 return true
46 }
47 return !executableOpenSuffixes[strings.ToLower(filepath.Ext(path))]
48 }
49
50 // OpenLocalPath opens an arbitrary local absolute path (file or directory)
51 // with the OS default application. It backs clicking a local path rendered in
52 // chat markdown (issue #7426) — Windows drive paths, UNC paths and file:///
53 // URLs included.
54 func (a *App) OpenLocalPath(path string) error {
55 path, err := normalizeLocalOpenPath(path)
56 if err != nil {
57 return err
58 }
59 info, err := os.Stat(path)
60 if err != nil {
61 return err
62 }
63 if !openTargetAllowed(path, info.IsDir()) {
64 return fmt.Errorf("refusing to open executable target %q", path)
65 }
66 return openWorkspacePath(path)
67 }
68
68 lines GO