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 := false

View on GitHub (pinned to a4447c1563)

Solutions

  1. Expand ~ client-side before sending (resolve to an absolute path)
  2. Convert the path to absolute within the allowed shared root
  3. Remove ./ prefixes and resolve .. segments before calling
  4. 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

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


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/539c44a72be3b7cc. Report an issue: GitHub.