{"record":{"id":"c96d3a08c6169e0d","repo":"siyuan-note/siyuan","slug":"asset-path-s-does-not-match-data-path-s","errorCode":null,"errorMessage":"asset path [%s] does not match data path [%s]","messagePattern":"asset path \\[(.+?)\\] does not match data path \\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/server/serve.go","lineNumber":994,"sourceCode":"\tif err != nil {\n\t\treturn false\n\t}\n\t_, validatedAbsPath, err := model.ResolveDataAssetPath(filepath.ToSlash(dataRelativePath))\n\treturn err == nil && filepath.Clean(validatedAbsPath) == filepath.Clean(assetAbsPath)\n}\n\nfunc resolveAssetRequestPath(cleanPath, boxID, dataPath string) (string, error) {\n\tif dataPath != \"\" {\n\t\tif boxID != \"\" {\n\t\t\treturn \"\", errors.New(\"box and dataPath cannot be used together\")\n\t\t}\n\t\tdataRelativePath, assetAbsPath, err := model.ResolveDataAssetPath(dataPath)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tassetPath, _, ok := model.AssetPathFromDataRelativePath(dataRelativePath)\n\t\tif !ok || assetPath != cleanPath {\n\t\t\treturn \"\", fmt.Errorf(\"asset path [%s] does not match data path [%s]\", cleanPath, dataPath)\n\t\t}\n\t\treturn assetAbsPath, nil\n\t}\n\tif boxID != \"\" {\n\t\treturn model.GetAssetAbsPathInBox(cleanPath, boxID)\n\t}\n\treturn model.GetAssetAbsPath(cleanPath)\n}\n\nfunc serveAssets(ginServer *gin.Engine) {\n\tginServer.POST(\"/upload\", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, model.Upload)\n\n\tginServer.GET(\"/assets/*path\", model.CheckAuth, func(context *gin.Context) {\n\t\trequestPath := context.Param(\"path\")\n\t\tif \"/\" == requestPath || \"\" == requestPath {\n\t\t\t// 禁止访问根目录 Disable HTTP access to the /assets/ path https://github.com/siyuan-note/siyuan/issues/15257\n\t\t\tcontext.Status(http.StatusForbidden)\n\t\t\treturn","sourceCodeStart":976,"sourceCodeEnd":1012,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/server/serve.go#L976-L1012","documentation":"When an asset request is resolved by dataPath, the handler converts the data-relative path back to the canonical asset path and requires that it equals the requested cleanPath. If they differ (or the conversion fails), the request is rejected. This is a safety check preventing a dataPath parameter from being used to smuggle access to a different file than the URL path names.","triggerScenarios":"resolveAssetRequestPath called with dataPath set where model.AssetPathFromDataRelativePath(dataRelativePath) returns ok=false, or the derived assetPath differs from the cleanPath taken from the URL (kernel/server/serve.go:994). E.g. URL /assets/a.png with dataPath=/etc/passwd or with a dataPath pointing at another asset.","commonSituations":"Hand-crafted or proxied URLs where the path segment and dataPath were generated at different times; path manipulation attempts caught by the guard; assets moved/renamed so the dataPath no longer maps to the URL path.","solutions":["Make the URL path and the `dataPath` parameter refer to the same file (dataPath is workspace-data-relative, e.g. assets/foo.png)","Regenerate the URL if the asset was moved or renamed","If you only know the data-relative path, request the asset directly by that path without a conflicting URL path","Do not attempt to reach files outside the data directory via dataPath — model.ResolveDataAssetPath validates and rejects path traversal separately"],"exampleFix":"// before\nGET /assets/wrong-name.png?dataPath=assets/correct-name.png\n// after\nGET /assets/correct-name.png?dataPath=assets/correct-name.png","handlingStrategy":"validation","validationCode":"// client-side: ensure URL path and dataPath refer to the same file\nconst derived = dataPath.replace(/^assets\\//, \"\");\nif (derived !== name) throw new Error(\"URL path and dataPath must reference the same asset\");","typeGuard":null,"tryCatchPattern":"const res = await fetch(url);\nif (!res.ok) {\n  const msg = await res.text();\n  if (msg.includes(\"does not match data path\")) {\n    // regenerate URL from the canonical dataPath\n  }\n}","preventionTips":["Derive the URL path from dataPath, never independently","Regenerate asset URLs after moving or renaming files","Do not use dataPath to reach files outside assets/ in the data dir"],"tags":["http","assets","path-validation","security","go"],"backgroundTag":"path-traversal-blocked","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}