{"record":{"id":"de0a29d0ab7cec1d","repo":"siyuan-note/siyuan","slug":"invalid-template-path","errorCode":null,"errorMessage":"invalid template path","messagePattern":"invalid template path","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/template_manage.go","lineNumber":61,"sourceCode":"\tPath     string `json:\"path\"`\n\tTarget   string `json:\"target\"`\n\tContent  string `json:\"content\"`\n\tRevision string `json:\"revision\"`\n}\n\ntype TemplateFileEntry struct {\n\tPath      string `json:\"path\"`\n\tIsDir     bool   `json:\"isDir\"`\n\tIsPackage bool   `json:\"isPackage,omitempty\"`\n}\n\n// 访问已有模板只校验目录边界，不对文件名进行清理或改写。\nfunc validateTemplateRelativePath(p string, allowRoot bool) error {\n\tif p == \"\" && allowRoot {\n\t\treturn nil\n\t}\n\tif p == \"\" || !fs.ValidPath(p) || strings.ContainsAny(p, \"\\\\:\\x00\") {\n\t\treturn errors.New(\"invalid template path\")\n\t}\n\tfor _, part := range strings.Split(p, \"/\") {\n\t\tif strings.HasPrefix(part, \".\") {\n\t\t\treturn errors.New(\"hidden template paths are reserved\")\n\t\t}\n\t}\n\treturn nil\n}\n\n// 新名称保持跨平台可用，已有父目录沿用原名。\nfunc validateNewTemplateName(p string) error {\n\tpart := path.Base(p)\n\tdevice := strings.ToUpper(strings.SplitN(part, \".\", 2)[0])\n\tif device == \"CON\" || device == \"PRN\" || device == \"AUX\" || device == \"NUL\" || (len(device) == 4 && (strings.HasPrefix(device, \"COM\") || strings.HasPrefix(device, \"LPT\")) && device[3] >= '1' && device[3] <= '9') {\n\t\treturn errors.New(\"reserved template file name\")\n\t}\n\tif strings.HasPrefix(part, \".\") || strings.TrimSpace(part) != part || strings.HasSuffix(part, \".\") || strings.ContainsAny(part, \"\\\\:<>\\\"|?*\") || strings.ContainsFunc(part, unicode.IsControl) {\n\t\treturn errors.New(\"invalid template path component\")","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/template_manage.go#L43-L79","documentation":"validateTemplateRelativePath rejected the given template relative path because it is empty when a root path is not allowed, is not a valid fs.ValidPath, or contains forbidden characters (backslash, colon, NUL). This guards against malformed and unsafe paths before any file access.","triggerScenarios":"DocSaveAsTemplateInDirectory or checkTemplateFilePath calls validateTemplateRelativePath with a path containing \"\\\\\", \":\", or \"\\x00\", or a path failing fs.ValidPath (e.g. leading/trailing slash, \".\", \"..\", empty segments).","commonSituations":"Windows-style separators (\\\\) passed on any platform; absolute paths passed where a relative path is required; user-supplied paths containing colons (e.g. drive letters) or NUL bytes.","solutions":["Convert the path to slash-separated relative form (strip volume/drive prefixes and leading separators)","Clean the path with path.Clean / fs.ValidPath semantics before calling","Reject or sanitize user input before constructing the template path"],"exampleFix":"// before\nerr := validateTemplateRelativePath(\"C:\\\\templates\\\\foo.md\", false)\n// after\np := path.Clean(strings.ReplaceAll(userPath, \"\\\\\", \"/\"))\np = strings.TrimPrefix(p, \"/\")\nerr := validateTemplateRelativePath(p, false)\n","handlingStrategy":"validation","validationCode":"func safeTemplatePath(p string) bool {\n    return p != \"\" && fs.ValidPath(p) && !strings.ContainsAny(p, \"\\\\:\\x00\")\n}","typeGuard":null,"tryCatchPattern":"if err := validateTemplateRelativePath(p, false); err != nil {\n    return fmt.Errorf(\"rejecting bad template path %q: %w\", p, err)\n}","preventionTips":["Always pass slash-separated relative paths, never absolute or Windows paths","Run path.Clean and strip leading separators before calling","Reject user input containing backslash, colon, or NUL"],"tags":["template","path","validation"],"backgroundTag":"invalid-argument-format","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}