返回 DeepSeek-Reasonix
project_scope_paths.go
根目录 / internal / config / project_scope_paths.go
1 package config
2
3 import (
4 "os"
5 "path/filepath"
6 "strings"
7 )
8
9 // configPath resolves against the actual workspace, never a project-declared
10 // workspace_root. Existing link identity is resolved before containment checks.
11 func (c *Config) configPath(ws, raw string) (string, bool) {
12 p := strings.TrimSpace(c.expandSandboxPath(raw))
13 if p == "" || ws == "" || strings.Contains(p, "${") {
14 return "", false
15 }
16 if filepath.VolumeName(p) == "" && !os.IsPathSeparator(p[0]) {
17 p = filepath.Join(ws, p)
18 } else if abs, err := filepath.Abs(p); err == nil {
19 p = abs
20 }
21 p, err := evalSymlinksAllowMissing(p)
22 return p, err == nil
23 }
24
25 func (c *Config) equivalentConfigPath(ws, a, b string) bool {
26 aa, aok := c.configPath(ws, a)
27 bb, bok := c.configPath(ws, b)
28 if !aok || !bok {
29 return false
30 }
31 if aa == bb {
32 return true
33 }
34 ai, ae := os.Stat(aa)
35 bi, be := os.Stat(bb)
36 return ae == nil && be == nil && os.SameFile(ai, bi)
37 }
38
39 func (c *Config) authorizedPath(ws string, allowed []string, candidate string) bool {
40 p, ok := c.configPath(ws, candidate)
41 if !ok {
42 return false
43 }
44 for _, raw := range allowed {
45 root, ok := c.configPath(ws, raw)
46 if ok && configDirectoryCovers(root, p) {
47 return true
48 }
49 }
50 return false
51 }
52
53 // Prefer directory identity on all filesystems, including Windows directories
54 // with case sensitivity enabled. filepath.Rel alone folds Windows case even
55 // when the volume does not. Missing grant roots require exact component names.
56 func configDirectoryCovers(root, path string) bool {
57 if info, err := os.Stat(root); err == nil {
58 return info.IsDir() && sameFileAncestor(root, path)
59 }
60 for dir := path; ; dir = filepath.Dir(dir) {
61 if dir == root {
62 return true
63 }
64 if filepath.Dir(dir) == dir {
65 return false
66 }
67 }
68 }
69
69 lines GO