wavetermdev/waveterm · error
path cannot start with ~, ., or ..
Error message
path cannot start with ~, ., or ..
What it means
CleanPathPrefix sanitizes a remote file path for safe sharing. After stripping a leading separator it rejects paths that begin with ~, ., or .. because such prefixes can escape the intended shared root or reference ambiguous home-relative locations.
Source
Thrown at pkg/remote/fileshare/fsutil/fsutil.go:48
}
lastSlash := strings.LastIndex(hostAndPath, fspath.Separator)
if lastSlash <= 0 {
return ""
}
return hostAndPath[:lastSlash+1]
}
// CleanPathPrefix corrects paths for prefix filesystems (i.e. ones that don't have directories)
func CleanPathPrefix(path string) (string, error) {
if path == "" {
return "", nil
}
if strings.HasPrefix(path, fspath.Separator) {
path = path[1:]
}
if strings.HasPrefix(path, "~") || strings.HasPrefix(path, ".") || strings.HasPrefix(path, "..") {
return "", fmt.Errorf("path cannot start with ~, ., or ..")
}
var newParts []string
for _, part := range strings.Split(path, fspath.Separator) {
if part == ".." {
if len(newParts) > 0 {
newParts = newParts[:len(newParts)-1]
}
} else if part != "." {
newParts = append(newParts, part)
}
}
return fspath.Join(newParts...), nil
}
func ReadFileStream(ctx context.Context, readCh <-chan wshrpc.RespOrErrorUnion[wshrpc.FileData], fileInfoCallback func(finfo wshrpc.FileInfo), dirCallback func(entries []*wshrpc.FileInfo) error, fileCallback func(data io.Reader) error) error {
var fileData *wshrpc.FileData
firstPk := true
isDir := falseView on GitHub (pinned to a4447c1563)
Solutions
- Expand ~ client-side before sending (resolve to an absolute path)
- Convert the path to absolute within the allowed shared root
- Remove ./ prefixes and resolve .. segments before calling
- Reject or re-prompt the user for a valid absolute path
Example fix
// before
p, err := fsutil.CleanPathPrefix("~/notes.txt") // error
// after
abs, _ := filepath.Abs(filepath.Join(homeDir, "notes.txt"))
p, err := fsutil.CleanPathPrefix(strings.TrimPrefix(abs, "/")) Defensive patterns
Strategy: validation
Validate before calling
func validSharePath(p string) bool {
base := filepath.Base(p)
return base != "" && !strings.HasPrefix(base, "~") && !strings.HasPrefix(base, ".")
} Type guard
func isCleanSharePath(p string) bool {
return !strings.HasPrefix(p, "~") && !strings.HasPrefix(p, "./") && !strings.HasPrefix(p, "../")
} Try / catch
p, err := fsutil.CleanPathPrefix(raw)
if err != nil && strings.Contains(err.Error(), "cannot start with") {
return fmt.Errorf("please provide an absolute path inside the shared root")
} Prevention
- Always send absolute paths within the shared root
- Expand ~ on the client before sending
- Sanitize user-supplied paths and reject traversal patterns
When it happens
Trigger: Calling CleanPathPrefix (used in fileshare path handling) with a path like "~/file.txt", ".hidden", "../etc/passwd", or "..foo".
Common situations: Client sends a home-relative path assuming ~ expansion; relative paths constructed with "./" prefixes; path traversal attempt in a shared-file feature; user pastes a shell-style path into a file-share input.
Related errors
- potential path traversal detected for path %s
- failed to read secret bindings (ERR-SECRET): %w
- failed to expand path: %w
- failed to stat path: %w
- path is not a directory
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/539c44a72be3b7cc.
Report an issue: GitHub.