{"record":{"id":"1426c5b09949abe0","repo":"wavetermdev/waveterm","slug":"potential-path-traversal-detected-for-path-s","errorCode":null,"errorMessage":"potential path traversal detected for path %s","messagePattern":"potential path traversal detected for path (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"tsunami/util/util.go","lineNumber":56,"sourceCode":"\thomeVar, err := os.UserHomeDir()\n\tif err != nil {\n\t\treturn \"/\"\n\t}\n\treturn homeVar\n}\n\nfunc ExpandHomeDir(pathStr string) (string, error) {\n\tif pathStr != \"~\" && !strings.HasPrefix(pathStr, \"~/\") && (!strings.HasPrefix(pathStr, `~\\`) || runtime.GOOS != \"windows\") {\n\t\treturn filepath.Clean(pathStr), nil\n\t}\n\thomeDir := GetHomeDir()\n\tif pathStr == \"~\" {\n\t\treturn homeDir, nil\n\t}\n\texpandedPath := filepath.Clean(filepath.Join(homeDir, pathStr[2:]))\n\tabsPath, err := filepath.Abs(filepath.Join(homeDir, expandedPath))\n\tif err != nil || !strings.HasPrefix(absPath, homeDir) {\n\t\treturn \"\", fmt.Errorf(\"potential path traversal detected for path %s\", pathStr)\n\t}\n\treturn expandedPath, nil\n}\n\nfunc ExpandHomeDirSafe(pathStr string) string {\n\tpath, _ := ExpandHomeDir(pathStr)\n\treturn path\n}\n\nfunc ChunkSlice[T any](slice []T, chunkSize int) [][]T {\n\tif len(slice) == 0 {\n\t\treturn nil\n\t}\n\tchunks := make([][]T, 0)\n\tfor i := 0; i < len(slice); i += chunkSize {\n\t\tend := i + chunkSize\n\t\tif end > len(slice) {\n\t\t\tend = len(slice)","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/tsunami/util/util.go#L38-L74","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\np, err := util.ExpandHomeDir(\"~/../etc/config\") // traversal error\n// after\np, err := util.ExpandHomeDir(\"~/.config/myapp/config\")","handlingStrategy":"validation","validationCode":"func isSafeHomePath(p string) bool {\n    if !strings.HasPrefix(p, \"~/\") && p != \"~\" { return true }\n    rest := strings.TrimPrefix(strings.TrimPrefix(p, \"~\"), \"/\")\n    return rest == \"\" || !strings.HasPrefix(rest, \"..\") && !strings.Contains(\"/\"+rest, \"/../\")\n}","typeGuard":null,"tryCatchPattern":"expanded, err := util.ExpandHomeDir(userPath)\nif err != nil {\n    return fmt.Errorf(\"refusing unsafe path %q: %w\", userPath, err)\n}","preventionTips":["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"],"tags":["path","security","path-traversal","go"],"backgroundTag":"path-traversal-detected","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}