wavetermdev/waveterm · warning
potential path traversal detected for path %s
Error message
potential path traversal detected for path %s
What it means
ExpandHomeDir expands a leading ~ to the user's home directory, then verifies the result (after Clean/Abs) still resides under homeDir. If filepath.Abs fails or the absolute path escapes the home prefix, it rejects the input as a path traversal attempt (e.g. ~/../etc).
Source
Thrown at tsunami/util/util.go:56
homeVar, err := os.UserHomeDir()
if err != nil {
return "/"
}
return homeVar
}
func ExpandHomeDir(pathStr string) (string, error) {
if pathStr != "~" && !strings.HasPrefix(pathStr, "~/") && (!strings.HasPrefix(pathStr, `~\`) || runtime.GOOS != "windows") {
return filepath.Clean(pathStr), nil
}
homeDir := GetHomeDir()
if pathStr == "~" {
return homeDir, nil
}
expandedPath := filepath.Clean(filepath.Join(homeDir, pathStr[2:]))
absPath, err := filepath.Abs(filepath.Join(homeDir, expandedPath))
if err != nil || !strings.HasPrefix(absPath, homeDir) {
return "", fmt.Errorf("potential path traversal detected for path %s", pathStr)
}
return expandedPath, nil
}
func ExpandHomeDirSafe(pathStr string) string {
path, _ := ExpandHomeDir(pathStr)
return path
}
func ChunkSlice[T any](slice []T, chunkSize int) [][]T {
if len(slice) == 0 {
return nil
}
chunks := make([][]T, 0)
for i := 0; i < len(slice); i += chunkSize {
end := i + chunkSize
if end > len(slice) {
end = len(slice)View on GitHub (pinned to a4447c1563)
Solutions
- Remove the .. traversal from the path; reference files genuinely under the home directory.
- Use ExpandHomeDirSafe if you want the empty-string-on-failure behavior rather than an error, but validate the result.
- If HOME is unset in your environment, set it so os.UserHomeDir returns a real home directory instead of the "/" fallback.
Example fix
// before
p, err := util.ExpandHomeDir("~/../etc/config") // traversal error
// after
p, err := util.ExpandHomeDir("~/.config/myapp/config") Defensive patterns
Strategy: validation
Validate before calling
func isSafeHomePath(p string) bool {
if !strings.HasPrefix(p, "~/") && p != "~" { return true }
rest := strings.TrimPrefix(strings.TrimPrefix(p, "~"), "/")
return rest == "" || !strings.HasPrefix(rest, "..") && !strings.Contains("/"+rest, "/../")
} Try / catch
expanded, err := util.ExpandHomeDir(userPath)
if err != nil {
return fmt.Errorf("refusing unsafe path %q: %w", userPath, err)
} Prevention
- Sanitize user-supplied paths before expansion (strip .. segments)
- Remember GetHomeDir falls back to "/" when HOME is unset — set HOME in service environments
- Prefer ExpandHomeDirSafe only when an empty result is an acceptable signal of rejection
When it happens
Trigger: Calling ExpandHomeDir with a path like "~/../../etc/passwd" or "~/../secrets" where Clean+Join escapes the home directory; also when homeDir itself is odd (GetHomeDir returns "/" on UserHomeDir error) making prefix checks behave unexpectedly.
Common situations: User-supplied config values containing ~/..; sanitizing untrusted path input in tools; environments with no HOME set so homeDir falls back to "/" and any relative-looking expansion trips the prefix check.
Related errors
- path cannot start with ~, ., or ..
- invalid source path: %w
- failed to read secret bindings (ERR-SECRET): %w
- failed to expand path: %w
- failed to stat path: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/1426c5b09949abe0.
Report an issue: GitHub.