{"record":{"id":"20c7c4535ac6172d","repo":"AlistGo/alist","slug":"share-path-out-of-range","errorCode":null,"errorMessage":"share path out of range","messagePattern":"share path out of range","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"server/handles/share.go","lineNumber":370,"sourceCode":"\t}\n\treturn true\n}\n\nfunc shouldTrackShareContentAccess(c *gin.Context) bool {\n\treturn c.Request.Method != http.MethodHead\n}\n\nfunc resolveShareTarget(share *model.Share, rawRelPath string) (string, string, error) {\n\tcleanRelPath := utils.FixAndCleanPath(rawRelPath)\n\tif !share.IsDir && cleanRelPath != \"/\" {\n\t\treturn \"\", \"\", fmt.Errorf(\"file share does not support nested path\")\n\t}\n\tif cleanRelPath == \"/\" {\n\t\treturn share.RootPath, \"/\", nil\n\t}\n\ttarget := utils.FixAndCleanPath(stdpath.Join(share.RootPath, cleanRelPath))\n\tif !utils.IsSubPath(share.RootPath, target) {\n\t\treturn \"\", \"\", fmt.Errorf(\"share path out of range\")\n\t}\n\treturn target, cleanRelPath, nil\n}\n\nfunc resolveShareWildcardTarget(share *model.Share, rawPath string) (string, string, error) {\n\tpath, err := url.PathUnescape(rawPath)\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\treturn resolveShareTarget(share, strings.TrimPrefix(path, \"/\"))\n}\n\nfunc buildPublicShareAssetURL(c *gin.Context, prefix, shareID, relPath, token string, preview bool) string {\n\tbase := common.GetApiUrl(c.Request) + prefix + shareID\n\tcleanPath := utils.FixAndCleanPath(relPath)\n\tif cleanPath != \"/\" {\n\t\tbase += utils.EncodePath(cleanPath, true)\n\t}","sourceCodeStart":352,"sourceCodeEnd":388,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/server/handles/share.go#L352-L388","documentation":"Returned by resolveShareTarget (server/handles/share.go:370) when the cleaned, joined target path falls outside share.RootPath according to utils.IsSubPath. This is the share engine's path-traversal guard: after FixAndCleanPath and stdpath.Join, any residue of \"..\" that would escape the shared root produces this error instead of a filesystem read.","triggerScenarios":"GET /s/<shareID>/../../etc/passwd style URLs; URL-encoded traversal (%2e%2e%2f) that survives cleaning; crafted wildcard share paths after url.PathUnescape in resolveShareWildcardTarget — this fires purely on lexical path analysis, not on symlink targets.","commonSituations":"Security scanners probing share links; mis-encoded paths from clients; hand-built relative paths mixing absolute and relative segments.","solutions":["Request only paths that lexically sit inside the share root","URL-encode path segments and never hand-build ../ sequences","If you legitimately need a sibling path, create a share rooted at the common parent"],"exampleFix":"// before\nrel := \"../../secret.txt\"\n// after\nrel := \"docs/secret.txt\" // relative to the share root","handlingStrategy":"validation","validationCode":"func insideShareRoot(root, rel string) bool {\n  joined := path.Clean(path.Join(root, rel))\n  return joined == root || strings.HasPrefix(joined, root+\"/\")\n}","typeGuard":null,"tryCatchPattern":"// treat as a security signal, not a retry candidate:\nif err != nil && strings.Contains(err.Error(), \"out of range\") { log.Warnf(\"traversal attempt: %v\", rawPath) }","preventionTips":["Always URL-encode path segments","Never hand-concatenate user input into share paths","Log and alert on this error — it indicates probing"],"tags":["share","security","path-traversal"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}