vxcontrol/pentagi · error
path escapes the flow data directory
Error message
path escapes the flow data directory
What it means
After joining the cleaned path with the flow data dir, ResolveCachedPath re-checks containment with IsWithinDir. This error means the resulting absolute path would escape the per-flow data directory (e.g. via '..' segments in an allowed root), so it is refused to prevent cross-flow or host file access.
Source
Thrown at backend/pkg/flowfiles/files.go:115
func ResolveCachedPath(dataDir string, flowID uint64, reqPath string) (string, error) {
if strings.TrimSpace(reqPath) == "" {
return "", errors.New("path query parameter is required")
}
cleaned := filepath.Clean(filepath.FromSlash(strings.ReplaceAll(reqPath, "\\", "/")))
if filepath.IsAbs(cleaned) {
return "", fmt.Errorf("path must be relative (no leading /)")
}
parts := strings.SplitN(cleaned, string(filepath.Separator), 2)
if parts[0] != UploadsDirName && parts[0] != ContainerDirName && parts[0] != ResourcesDirName {
return "", fmt.Errorf("path must start with '%s', '%s', or '%s'", UploadsDirName, ContainerDirName, ResourcesDirName)
}
flowDataDir := FlowDataDir(dataDir, flowID)
absPath := filepath.Join(flowDataDir, cleaned)
if !IsWithinDir(absPath, flowDataDir) {
return "", fmt.Errorf("path escapes the flow data directory")
}
return absPath, nil
}
func SanitizeFileName(fileName string) (string, error) {
trimmedName := strings.TrimSpace(fileName)
if trimmedName == "" {
return "", fmt.Errorf("file name is required")
}
normalizedName := strings.ReplaceAll(trimmedName, "\\", "/")
cleanName := path.Base(path.Clean("/" + normalizedName))
return validatePathComponent(cleanName)
}
func SanitizeContainerCachePath(containerPath string) (string, error) {View on GitHub (pinned to ea665308ba)
Solutions
- Remove '..' segments; use only List-returned paths
- Keep the path under the chosen root: uploads/, container/, or resources/
- Treat repeated occurrences as a security probe and log/alert on the requester
- Optionally reject any '..' in the raw input before calling the API
Example fix
// before resolveCachedPath(flowID, "uploads/../../shared/file.txt") // after resolveCachedPath(flowID, "uploads/file.txt") // stay inside the flow's cache root
Defensive patterns
Strategy: validation
Validate before calling
func containsDotDot(p string) bool {
for _, seg := range strings.Split(strings.ReplaceAll(p, "\\", "/"), "/") {
if seg == ".." { return true }
}
return false
}
// reject before calling: if containsDotDot(reqPath) { return errors.New("traversal rejected") } Type guard
func isTraversalError(err error) bool { return err != nil && strings.Contains(err.Error(), "path escapes the flow data directory") } Try / catch
abs, err := flowfiles.ResolveCachedPath(dataDir, flowID, reqPath)
if err != nil && strings.Contains(err.Error(), "path escapes the flow data directory") {
log.WithField("path", reqPath).Warn("path traversal attempt blocked")
http.Error(w, "invalid path", http.StatusBadRequest)
return
} Prevention
- Reject any '..' segment in raw input before resolution
- Serve files only through ResolveCachedPath — never join user paths directly
- Log and alert on traversal attempts as potential security probes
- Keep symlinks out of the flow cache directories
When it happens
Trigger: Paths containing '..' segments that survive Clean and step outside the flow dir, e.g. 'uploads/../../other-flow/secret.txt', despite starting with a valid root.
Common situations: Hand-crafted malicious requests attempting path traversal; buggy client code concatenating '../' when building relative paths; symlinks inside the cache pointing outside (if IsWithinDir follows them).
Related errors
- path must be relative (no leading /)
- path must start with '%s', '%s', or '%s'
- path must be relative
- path must not contain parent directory traversal
- Token.CreationDisabled
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/6d5bd7c8eef19d31.
Report an issue: GitHub.