siyuan-note/siyuan · error
template path is outside templates directory
Error message
template path is outside templates directory
What it means
Returned by RemoveTemplate (template.go:97) when the resolved absolute path is not a sub-path of <data>/templates/. This is a security guard preventing arbitrary file deletion. The check uses gulu.File.IsSubPath after filepath.Clean, comparing against the canonical templates root.
Source
Thrown at kernel/model/template.go:97
buf.Grow(4096)
err = tpl.Execute(buf, nil)
if err != nil {
return "", fmt.Errorf(Conf.Language(44), err.Error())
}
ret = buf.String()
return
}
// RemoveTemplate 删除模板文件,路径必须限定在 <data>/templates/ 目录内,防止任意文件被删除
func RemoveTemplate(p string) (err error) {
abs := p
if !filepath.IsAbs(abs) {
abs = filepath.Join(util.DataDir, "templates", p)
}
abs = filepath.Clean(abs)
templatesRoot := filepath.Clean(filepath.Join(util.DataDir, "templates"))
if !gulu.File.IsSubPath(templatesRoot, abs) {
return errors.New("template path is outside templates directory")
}
err = filelock.Remove(abs)
if err != nil {
logging.LogErrorf("remove template failed: %s", err)
}
return
}
// getTemplateReadmePaths 返回模板包 README 的相对包根路径集合:恒含 README.md,并合并 template.json 的 readme 字段(大小写敏感)。
func getTemplateReadmePaths(templateDir string) map[string]struct{} {
paths := map[string]struct{}{"README.md": {}}
pkg, err := bazaar.ParsePackageJSON(filepath.Join(templateDir, "template.json"))
if err != nil {
return paths
}
for _, v := range pkg.Readme {
v = strings.TrimSpace(v)
if "" != v {View on GitHub (pinned to 251596fc0d)
Solutions
- Ensure the path passed to RemoveTemplate is a simple relative filename under templates/ with no '..' segments.
- Sanitize input: reject paths containing '..' or starting with '/' before calling the API.
- If a legitimate template deletion fails, verify the template file actually lives directly under <data>/templates/ and not in a nested data subdirectory.
Example fix
// before — traversal escapes templates root
RemoveTemplate("../../config/conf.json")
// after — relative name within templates dir
RemoveTemplate("my-template.md") Defensive patterns
Strategy: validation
Validate before calling
import "strings"
import "path/filepath"
func safeTemplateName(p string) bool {
p = filepath.ToSlash(filepath.Clean(p))
if filepath.IsAbs(p) || strings.Contains(p, "../") || strings.HasPrefix(p, "/") {
return false
}
return true
} Prevention
- Always pass a plain relative filename (e.g. 'tpl.md') to RemoveTemplate, never user-supplied raw paths.
- Sanitize any plugin-supplied path by rejecting '..' segments before calling the API.
- Treat RemoveTemplate as a destructive op — confirm the resolved path is under <data>/templates/ client-side first.
When it happens
Trigger: The API is called with a 'p' argument containing '..' traversal segments that escape the templates directory after cleaning, e.g. '../../etc/important_file', or an absolute path pointing outside <data>/templates/. Triggered by the remove-template HTTP/CLI endpoint when a malformed or malicious path is supplied.
Common situations: A plugin or external script calls the template-remove API with a user-supplied path that is not sanitized; a path crafted to test directory traversal; a relative path that, after filepath.Join with util.DataDir/templates, still resolves above the templates root due to leading '..'.
Related errors
- path escapes templates dir: %s
- invalid content template path
- content template path is outside templates directory
- path [%s] must not contain '..'
- path [%s] escapes box directory
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/dcb4a452baeec3de.
Report an issue: GitHub.