{"record":{"id":"ec0b35c6326aa3ab","repo":"siyuan-note/siyuan","slug":"invalid-content-template-path","errorCode":null,"errorMessage":"invalid content template path","messagePattern":"invalid content template path","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/template.go","lineNumber":1220,"sourceCode":"\t\tif \"name\" == key || \"alias\" == key || \"bookmark\" == key || \"memo\" == key || \"icon\" == key ||\n\t\t\tstrings.HasPrefix(key, \"custom-\") {\n\t\t\ttree.Root.SetIALAttr(key, value)\n\t\t}\n\t}\n\ttree.Root.SetIALAttr(\"updated\", util.CurrentTimeSecondsStr())\n\tif err = indexWriteTreeUpsertQueue(tree); nil != err {\n\t\treturn err\n\t}\n\tav.BatchUpsertBlockRel(tree.Root.ChildrenByType(ast.NodeAttributeView))\n\treturn nil\n}\n\nfunc resolveDocContentTemplatePath(templatePath string) (string, error) {\n\ttemplatePath = strings.TrimPrefix(filepath.ToSlash(strings.TrimSpace(templatePath)), \"/\")\n\tcleanPath := filepath.Clean(filepath.FromSlash(templatePath))\n\tif \"\" == cleanPath || \".\" == cleanPath || filepath.IsAbs(cleanPath) || \"..\" == cleanPath ||\n\t\tstrings.HasPrefix(cleanPath, \"..\"+string(os.PathSeparator)) {\n\t\treturn \"\", errors.New(\"invalid content template path\")\n\t}\n\ttemplateRoot := filepath.Join(util.DataDir, \"templates\")\n\tabsPath := filepath.Join(templateRoot, cleanPath)\n\tif !gulu.File.IsSubPath(templateRoot, absPath) {\n\t\treturn \"\", errors.New(\"content template path is outside templates directory\")\n\t}\n\tif !filelock.IsExist(absPath) {\n\t\treturn \"\", fmt.Errorf(\"content template [%s] not found\", templatePath)\n\t}\n\trealRoot, err := filepath.EvalSymlinks(templateRoot)\n\tif nil != err {\n\t\treturn \"\", err\n\t}\n\trealPath, err := filepath.EvalSymlinks(absPath)\n\tif nil != err {\n\t\treturn \"\", err\n\t}\n\tinfo, err := os.Stat(realPath)","sourceCodeStart":1202,"sourceCodeEnd":1238,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/template.go#L1202-L1238","documentation":"resolveDocContentTemplatePath validates a template path supplied for applying a document content template. It rejects paths that are empty, resolve to '.', are absolute, or escape the templates root via '..' components. This is a path-safety guard ensuring only relative paths inside <data>/templates/ can be used.","triggerScenarios":"Calling applyDocContentTemplate (e.g. via the templates API) with a templatePath that is \"\", \".\", an absolute path like \"/etc/passwd\" or \"C:/tmp/t.md\", or a path starting with \"../\" after trimming a leading slash.","commonSituations":"Passing a full filesystem path instead of the template's name-relative path; concatenating user home dirs into the path; a client sending an empty template field when the user did not pick a template.","solutions":["Pass only the template's path relative to <data>/templates/, e.g. \"basic.md\" or \"sub/tpl.md\".","Strip any leading slash or workspace prefix from the path before calling; use the path exactly as listed under Templates in the UI.","If the caller received the path from user input, reject/trim empty and absolute values before invoking the API."],"exampleFix":"// before\napplyDocContentTemplate(boxID, \"/home/user/data/templates/tpl.md\", ...)\n// after\napplyDocContentTemplate(boxID, \"tpl.md\", ...)","handlingStrategy":"validation","validationCode":"function isValidTemplatePath(p) {\n  if (typeof p !== \"string\") return false;\n  let t = p.trim().replace(/^\\//, \"\");\n  if (!t || t === \".\" || t === \"..\") return false;\n  return !/^(\\/|[A-Za-z]:[\\\\/])/.test(p) && !t.split(\"/\").includes(\"..\");\n}\nif (!isValidTemplatePath(templatePath)) throw new Error(\"invalid template path\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always store and pass template paths relative to the workspace templates directory","Never construct template paths from absolute filesystem locations","Normalize user input with path normalization before sending to the API"],"tags":["path-validation","templates","go"],"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"}