{"record":{"id":"0542bc3f6e18297f","repo":"siyuan-note/siyuan","slug":"path-escapes-templates-dir-s","errorCode":null,"errorMessage":"path escapes templates dir: %s","messagePattern":"path escapes templates dir: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/cli/cmd/template.go","lineNumber":214,"sourceCode":"\t\treturn nil\n\t},\n}\n\n// resolveTemplateAbs 把模板路径解析为 data/templates 下的绝对路径，拒绝越界。\n// 接受绝对路径或相对 data/templates 的相对路径。\nfunc resolveTemplateAbs(p string) (string, error) {\n\tif p == \"\" {\n\t\treturn \"\", fmt.Errorf(\"--path is required\")\n\t}\n\tabs := p\n\tif !filepath.IsAbs(abs) {\n\t\tabs = filepath.Join(util.DataDir, \"templates\", p)\n\t}\n\tabs = filepath.Clean(abs)\n\ttemplatesBase := filepath.Clean(filepath.Join(util.DataDir, \"templates\"))\n\trel, err := filepath.Rel(templatesBase, abs)\n\tif err != nil || strings.HasPrefix(rel, \"..\") || rel == \"..\" {\n\t\treturn \"\", fmt.Errorf(\"path escapes templates dir: %s\", p)\n\t}\n\treturn abs, nil\n}\n\nfunc init() {\n\ttemplateGetCmd.Flags().String(\"path\", \"\", \"template path (absolute or relative to data/templates)\")\n\ttemplateRemoveCmd.Flags().String(\"path\", \"\", \"template path (absolute or relative to data/templates)\")\n\ttemplateRenderCmd.Flags().String(\"path\", \"\", \"template path (absolute or relative to data/templates)\")\n\ttemplateRenderCmd.Flags().String(\"id\", \"\", \"block ID to render against\")\n\ttemplateSaveAsCmd.Flags().String(\"id\", \"\", \"source document block ID\")\n\ttemplateSaveAsCmd.Flags().String(\"name\", \"\", \"template name without extension\")\n\ttemplateSaveAsCmd.Flags().Bool(\"overwrite\", false, \"overwrite if exists\")\n\ttemplateCreateCmd.Flags().String(\"name\", \"\", \"template name without extension\")\n\ttemplateCreateCmd.Flags().String(\"data\", \"\", \"markdown content\")\n\ttemplateCreateCmd.Flags().String(\"file\", \"\", \"read content from file path (- for stdin)\")\n\ttemplateCreateCmd.Flags().Bool(\"overwrite\", false, \"overwrite if exists\")\n\n\trootCmd.AddCommand(templateCmd)","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/cli/cmd/template.go#L196-L232","documentation":"resolveTemplateAbs normalizes the requested path and verifies via filepath.Rel that it stays inside data/templates. If the cleaned path resolves outside the templates base directory (relative component starting with \"..\"), the error is returned to block path traversal. It protects templates access from reading arbitrary filesystem locations.","triggerScenarios":"Passing --path with traversal segments such as \"../foo\" or \"/abs/path/outside/templates\" that Clean/Rel shows escapes data/templates.","commonSituations":"Typing a path relative to the working directory instead of data/templates; using \"..\" to reach another data subfolder; scripts building paths by joining user input without sanitization.","solutions":["Move the template file into data/templates and reference it relatively.","Use an absolute path that actually lives under data/templates.","Remove \"..\" segments from the path; anchor it at data/templates."],"exampleFix":"// before\ntemplate get --path ../notes/tpl.sy\n// after\ntemplate get --path data/templates/tpl.sy  (or copy tpl.sy into data/templates and use --path tpl.sy)","handlingStrategy":"validation","validationCode":"const templatesBase = \"/path/to/data/templates\";\nconst abs = require(\"path\").resolve(templatesBase, userPath);\nif (!abs.startsWith(templatesBase + require(\"path\").sep)) throw new Error(\"path escapes templates dir\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Place templates under data/templates and reference them relatively.","Never accept \"..\" segments from user input for template paths.","Resolve and verify containment before calling the CLI."],"tags":["cli","path-traversal","security","templates"],"backgroundTag":"path-traversal-blocked","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"}