{"record":{"id":"20dbd839ed5f668f","repo":"siyuan-note/siyuan","slug":"s-is-not-an-asset-path","errorCode":null,"errorMessage":"[%s] is not an asset path","messagePattern":"\\[(.+?)\\] is not an asset path","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/assets.go","lineNumber":1224,"sourceCode":"\t\t\t}\n\t\t}\n\t}\n\tcleanPath = filepath.ToSlash(relativePath)\n\treturn\n}\n\n// GetAssetAbsPathInBox 在指定 box 内解析资源绝对路径，不进行全局遍历。\n// relativePath 必须以 assets/ 前缀开头，boxID 为空且路径没有 box 查询参数时只解析普通/全局资源，不遍历加密 box。\n// 加密 box 直接从 <boxID>/assets/ 查找，不依赖后缀匹配。\nfunc GetAssetAbsPathInBox(relativePath, boxID string) (string, error) {\n\tvar err error\n\trelativePath, boxID, err = assetPathAndBox(relativePath, boxID)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\trelativePath = path.Clean(relativePath)\n\tif relativePath == \".\" || strings.HasPrefix(relativePath, \"../\") || relativePath == \"..\" || path.IsAbs(relativePath) {\n\t\treturn \"\", fmt.Errorf(\"[%s] is not an asset path\", relativePath)\n\t}\n\tif !strings.HasPrefix(relativePath, \"assets/\") {\n\t\treturn \"\", fmt.Errorf(\"[%s] is not an asset path (must start with assets/)\", relativePath)\n\t}\n\tif boxID != \"\" && !ast.IsNodeIDPattern(boxID) {\n\t\treturn \"\", fmt.Errorf(\"[%s] is not a box id\", boxID)\n\t}\n\n\tif boxID == \"\" {\n\t\treturn GetAssetAbsPathWithOpt(relativePath, false)\n\t}\n\n\tp := filepath.Join(util.DataDir, boxID, relativePath)\n\tif gulu.File.IsExist(p) {\n\t\tif !gulu.File.IsSubPath(util.WorkspaceDir, p) {\n\t\t\treturn \"\", fmt.Errorf(\"[%s] is not sub path of workspace\", p)\n\t\t}\n\t\t// 解析符号链接/目录联接，防止软链接跳出资产根目录","sourceCodeStart":1206,"sourceCodeEnd":1242,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/assets.go#L1206-L1242","documentation":"GetAssetAbsPathInBox cleans the input path and rejects anything that is empty, '.', '..', starting with '../', or absolute. Such a path can never denote a notebook asset, so this error is returned before any filesystem access. It is an input-validation guard against path traversal and malformed asset references.","triggerScenarios":"Calling GetAssetAbsPathInBox with an absolute path like \"/etc/passwd\", a traversal path like \"assets/../../secret\", an empty string (cleans to \".\"), or a path that normalizes to \"..\" after query stripping.","commonSituations":"Passing a full filesystem path where a data-relative path is expected; user-supplied or plugin-supplied paths not sanitized; constructing paths by string concatenation that leaves '..' segments; passing an empty variable due to an earlier resolution failure.","solutions":["Pass a data-relative path that starts with assets/, e.g. \"assets/foo.png\" (after path.Clean)","Strip any absolute prefix or leading slashes before calling, or use filepath.Rel(util.DataDir, abs) to derive the relative path","Validate the path yourself before calling: reject empty/absolute/'..' segments early to give a clearer error to your users"],"exampleFix":"// before: absolute path rejected\nmodel.GetAssetAbsPathInBox(\"/home/user/siyuan/data/assets/img.png\", boxID)\n// after: pass data-relative path\nmodel.GetAssetAbsPathInBox(\"assets/img.png\", boxID)","handlingStrategy":"validation","validationCode":"p = path.Clean(strings.TrimSpace(p))\nif p == \"\" || p == \".\" || p == \"..\" || strings.HasPrefix(p, \"../\") || path.IsAbs(p) {\n\treturn fmt.Errorf(\"rejecting non-asset path %q\", p)\n}","typeGuard":"func isSafeRelAssetPath(p string) bool {\n\tp = path.Clean(strings.TrimSpace(p))\n\treturn p != \".\" && p != \"..\" && !strings.HasPrefix(p, \"../\") && !path.IsAbs(p)\n}","tryCatchPattern":"abs, err := model.GetAssetAbsPathInBox(ref, boxID)\nif err != nil && strings.Contains(err.Error(), \"is not an asset path\") {\n\t// convert to data-relative path and retry\n\trel, relErr := filepath.Rel(util.DataDir, rawInput)\n\tif relErr == nil { abs, err = model.GetAssetAbsPathInBox(filepath.ToSlash(rel), boxID) }\n}","preventionTips":["Always pass workspace-data-relative, already-cleaned paths into asset resolvers","Never build paths by string concatenation with user input; use path.Join and path.Clean","Treat absolute filesystem paths as a separate input type and convert them explicitly"],"tags":["validation","path-traversal","asset-resolution"],"backgroundTag":"invalid-argument-value","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}