{"record":{"id":"1bd41561658b0e5a","repo":"chenhg5/cc-connect","slug":"path-escapes-workspace-base-directory","errorCode":null,"errorMessage":"path escapes workspace base directory","messagePattern":"path escapes workspace base directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/engine.go","lineNumber":16996,"sourceCode":"\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"cannot resolve home directory: %w\", err)\n\t\t}\n\t\tdirPath = filepath.Join(home, dirPath[2:])\n\t} else if !filepath.IsAbs(dirPath) {\n\t\tdirPath = filepath.Join(baseDir, dirPath)\n\t}\n\tcleaned := filepath.Clean(dirPath)\n\tresolved, err := filepath.EvalSymlinks(cleaned)\n\tif err != nil {\n\t\tresolved = cleaned\n\t}\n\tif baseDir != \"\" && !filepath.IsAbs(target) && !strings.HasPrefix(target, \"~\") {\n\t\tcleanBase := filepath.Clean(baseDir)\n\t\tif evalBase, err := filepath.EvalSymlinks(cleanBase); err == nil {\n\t\t\tcleanBase = evalBase\n\t\t}\n\t\tif !strings.HasPrefix(resolved, cleanBase+string(filepath.Separator)) && resolved != cleanBase {\n\t\t\treturn \"\", fmt.Errorf(\"path escapes workspace base directory\")\n\t\t}\n\t}\n\treturn resolved, nil\n}\n\n// looksLikeLocalDir returns true if the string looks like a local directory\n// path (absolute path, home-relative, dot-relative, or a bare name that\n// doesn't look like a URL). Slash commands like /dir are not local dirs.\nfunc looksLikeLocalDir(s string) bool {\n\tif s == \"\" {\n\t\treturn false\n\t}\n\tif strings.Contains(s, \"://\") || strings.Contains(s, \"@\") {\n\t\treturn false\n\t}\n\tif strings.HasPrefix(s, \"~/\") || s == \"~\" || strings.HasPrefix(s, \"./\") || strings.HasPrefix(s, \"../\") {\n\t\treturn true\n\t}","sourceCodeStart":16978,"sourceCodeEnd":17014,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/engine.go#L16978-L17014","documentation":"resolveLocalDirPath rejects resolved paths that fall outside the configured workspace baseDir. After cleaning and symlink-evaluating both the resolved target and the base directory, any path that is not the baseDir itself or beneath it is refused. This is a deliberate security guard against ../ traversal escaping the workspace sandbox.","triggerScenarios":"Calling resolveLocalDirPath with a relative target such as \"../other/repo\" (or any path whose cleaned resolution lands outside baseDir) while baseDir is non-empty, target is relative, and target does not start with '~'.","commonSituations":"Configured workspace directory accidentally contains \"..\" segments; symlink inside the workspace pointing to an outside directory resolved via EvalSymlinks; user typo'd a sibling directory path; hostile input from a chat message used as a directory target.","solutions":["Use a path inside baseDir, or change baseDir in config.toml to the parent directory you actually need","Remove '..' segments from the target and specify it relative to baseDir","Use an absolute path and adjust baseDir policy if absolute targets are intended","Remove or re-point symlinks that resolve outside the workspace"],"exampleFix":"// before: escapes baseDir (~/workspaces)\ntarget = \"../other-repo\"\n// after: stays inside baseDir\ntarget = \"other-repo\"","handlingStrategy":"validation","validationCode":"func insideBase(target, baseDir string) bool {\n    abs, err := filepath.Abs(target)\n    if err != nil { return false }\n    base, err := filepath.Abs(baseDir)\n    if err != nil { return false }\n    rel, err := filepath.Rel(base, abs)\n    return err == nil && !strings.HasPrefix(rel, \"..\")\n}\n// call before resolveLocalDirPath","typeGuard":null,"tryCatchPattern":"resolved, err := resolveLocalDirPath(userTarget, baseDir)\nif err != nil {\n    if strings.Contains(err.Error(), \"escapes workspace base directory\") {\n        return fmt.Errorf(\"refusing path outside workspace: %s\", userTarget)\n    }\n    return err\n}","preventionTips":["Sanitize user-supplied directory strings for '..' before passing them in","Keep workspace configs free of ../ segments","Audit symlinks inside the workspace periodically","Treat chat-originated paths as untrusted input and validate against baseDir"],"tags":["path-traversal","security","workspace-sandbox"],"backgroundTag":"path-traversal-blocked","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}