{"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":710,"sourceCode":"\t\ttree.Root.AppendChild(child)\n\t}\n\ttemplateIALs := parse.IAL2Map(templateTree.Root.KramdownIAL)\n\tfor key, value := range templateIALs {\n\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\treturn indexWriteTreeUpsertQueue(tree)\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":692,"sourceCodeEnd":728,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/template.go#L692-L728","documentation":"Returned by resolveDocContentTemplatePath (template.go:710) when the supplied template path is structurally invalid: empty, '.', '..', an absolute path, or begins with '..' + os.PathSeparator. This is the first-layer guard before any filesystem resolution, rejecting obviously unsafe or meaningless paths.","triggerScenarios":"A document's content-template frontmatter / setting specifies a path that is empty, a lone dot, an absolute path (e.g. '/etc/passwd' or 'C:\\x'), or begins with parent traversal ('../'). Triggered when SiYuan resolves which template file to use for rendering a doc's initial content.","commonSituations":"A doc config saves an empty template path; a user manually edits the template path to an absolute OS path; a migration/import left a '.' placeholder; a path was constructed by joining user input that resolved to '..'.","solutions":["Set the document's content template path to a relative path under templates/ (e.g. 'diary.md').","Strip leading slashes and '..' segments before saving the path.","If the path is intentionally empty (no template), clear the setting rather than passing '.' or '/'."],"exampleFix":"// before — absolute/empty path rejected\nresolveDocContentTemplatePath(\"/home/user/tpl.md\")\nresolveDocContentTemplatePath(\"\")\n\n// after — relative path under templates/\nresolveDocContentTemplatePath(\"diary.md\")","handlingStrategy":"validation","validationCode":"import \"path/filepath\"\nimport \"strings\"\n\nfunc isValidContentTemplatePath(p string) bool {\n    p = strings.TrimSpace(filepath.ToSlash(p))\n    p = strings.TrimPrefix(p, \"/\")\n    if p == \"\" || p == \".\" || p == \"..\" || filepath.IsAbs(p) || strings.HasPrefix(p, \"../\") {\n        return false\n    }\n    return true\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Store content template paths as relative paths under templates/ only.","Reject empty, dot, absolute, and '..'-prefixed paths in the UI before saving.","Treat the content-template path field as untrusted input."],"tags":["template","path-traversal","validation","security"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}