chenhg5/cc-connect · error
path escapes workspace base directory
Error message
path escapes workspace base directory
What it means
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.
Source
Thrown at core/engine.go:16996
if err != nil {
return "", fmt.Errorf("cannot resolve home directory: %w", err)
}
dirPath = filepath.Join(home, dirPath[2:])
} else if !filepath.IsAbs(dirPath) {
dirPath = filepath.Join(baseDir, dirPath)
}
cleaned := filepath.Clean(dirPath)
resolved, err := filepath.EvalSymlinks(cleaned)
if err != nil {
resolved = cleaned
}
if baseDir != "" && !filepath.IsAbs(target) && !strings.HasPrefix(target, "~") {
cleanBase := filepath.Clean(baseDir)
if evalBase, err := filepath.EvalSymlinks(cleanBase); err == nil {
cleanBase = evalBase
}
if !strings.HasPrefix(resolved, cleanBase+string(filepath.Separator)) && resolved != cleanBase {
return "", fmt.Errorf("path escapes workspace base directory")
}
}
return resolved, nil
}
// looksLikeLocalDir returns true if the string looks like a local directory
// path (absolute path, home-relative, dot-relative, or a bare name that
// doesn't look like a URL). Slash commands like /dir are not local dirs.
func looksLikeLocalDir(s string) bool {
if s == "" {
return false
}
if strings.Contains(s, "://") || strings.Contains(s, "@") {
return false
}
if strings.HasPrefix(s, "~/") || s == "~" || strings.HasPrefix(s, "./") || strings.HasPrefix(s, "../") {
return true
}View on GitHub (pinned to 4000b2338a)
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
Example fix
// before: escapes baseDir (~/workspaces) target = "../other-repo" // after: stays inside baseDir target = "other-repo"
Defensive patterns
Strategy: validation
Validate before calling
func insideBase(target, baseDir string) bool {
abs, err := filepath.Abs(target)
if err != nil { return false }
base, err := filepath.Abs(baseDir)
if err != nil { return false }
rel, err := filepath.Rel(base, abs)
return err == nil && !strings.HasPrefix(rel, "..")
}
// call before resolveLocalDirPath Try / catch
resolved, err := resolveLocalDirPath(userTarget, baseDir)
if err != nil {
if strings.Contains(err.Error(), "escapes workspace base directory") {
return fmt.Errorf("refusing path outside workspace: %s", userTarget)
}
return err
} Prevention
- 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
When it happens
Trigger: 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 '~'.
Common situations: 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.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- project %q: target user %q can run passwordless sudo. The ru
- project %q audit: %s
- target user %q can run passwordless sudo; isolation is meani
- chmod unit file: %w
- remote image host resolved to blocked IP %s
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/1bd41561658b0e5a.
Report an issue: GitHub.