{"record":{"id":"a729a93efdbd1d43","repo":"vxcontrol/pentagi","slug":"path-must-not-contain-parent-directory-traversal","errorCode":null,"errorMessage":"path must not contain parent directory traversal","messagePattern":"path must not contain parent directory traversal","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/resources/resources.go","lineNumber":151,"sourceCode":"//   - cleans the path (removes .., double slashes, etc.)\n//   - rejects absolute paths, dot-only components, and paths that exceed MaxPathLength\n//   - returns an error for the empty path\nfunc SanitizeResourcePath(p string) (string, error) {\n\ttrimmed := strings.TrimSpace(p)\n\tif trimmed == \"\" {\n\t\treturn \"\", fmt.Errorf(\"path must not be empty\")\n\t}\n\tif len(trimmed) > MaxPathLength {\n\t\treturn \"\", fmt.Errorf(\"path exceeds maximum allowed length of %d characters\", MaxPathLength)\n\t}\n\n\tnormalized := strings.ReplaceAll(trimmed, \"\\\\\", \"/\")\n\tif strings.HasPrefix(normalized, \"/\") {\n\t\treturn \"\", fmt.Errorf(\"path must be relative\")\n\t}\n\tfor _, part := range strings.Split(normalized, \"/\") {\n\t\tif part == \"..\" {\n\t\t\treturn \"\", fmt.Errorf(\"path must not contain parent directory traversal\")\n\t\t}\n\t}\n\tcleaned := path.Clean(\"/\" + normalized)\n\t// Remove the leading \"/\" we added for Clean, making the path relative.\n\trel := strings.TrimPrefix(cleaned, \"/\")\n\tif rel == \"\" || rel == \".\" {\n\t\treturn \"\", fmt.Errorf(\"invalid path\")\n\t}\n\n\t// Validate every path component.\n\tparts := strings.Split(rel, \"/\")\n\tfor _, part := range parts {\n\t\tif err := validatePathComponent(part); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\treturn rel, nil","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/resources/resources.go#L133-L169","documentation":"SanitizeResourcePath rejects any path containing a '..' segment after normalizing backslashes to '/'. Parent-directory traversal would allow a crafted resource name to escape the storage root. The check runs before path.Clean so even obfuscated-but-plain '..' segments are caught.","triggerScenarios":"Calling SanitizeResourcePath (or AddResourceFromFlow, ZipResources, SanitizeResourceDir which delegate to it) with values like \"../../etc/passwd\", \"a/../../b\", or any user-controlled filename containing a literal '..' path segment.","commonSituations":"Untrusted filenames coming from ZIP archives or HTTP uploads being stored directly; clients attempting to escape the resources directory; archive-extraction code (Zip Slip) feeding raw entry names into the API.","solutions":["Reject or sanitize the input upstream: strip or refuse any '..' segments before calling the API","Use path.Base or the library's SanitizeResourceFileName for bare filenames","Log the rejected input — it usually indicates a malicious or buggy client","If traversal is legitimately required, resolve the destination yourself and verify it stays within the root"],"exampleFix":"// before\nname, err := resources.SanitizeResourcePath(userInput) // \"../../etc/passwd\"\n// after\nif strings.Contains(userInput, \"..\") {\n    return fmt.Errorf(\"rejected suspicious path %q\", userInput)\n}\nname, err := resources.SanitizeResourcePath(userInput)","handlingStrategy":"validation","validationCode":"func hasTraversal(p string) bool {\n\tnorm := strings.ReplaceAll(p, \"\\\\\", \"/\")\n\tfor _, part := range strings.Split(norm, \"/\") {\n\t\tif part == \"..\" {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}","typeGuard":null,"tryCatchPattern":"name, err := resources.SanitizeResourcePath(userInput)\nif err != nil {\n\tif strings.Contains(err.Error(), \"parent directory traversal\") {\n\t\tlog.Warn(\"path traversal attempt blocked\", \"input\", userInput)\n\t\treturn status.Errorf(codes.InvalidArgument, \"invalid resource path\")\n\t}\n\treturn err\n}","preventionTips":["Treat any '..' in user input as an attack signal and reject it before calling the library","For ZIP extraction, sanitize every entry name with the library before writing (Zip Slip defense)","Never build paths by string concatenation of user input with a base directory","Add automated tests with traversal payloads (../../etc/passwd, a\\..\\..\\x) to your upload handlers"],"tags":["path-traversal","security","zip-slip","input-validation"],"backgroundTag":"path-traversal-detected","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}