siyuan-note/siyuan · error
path escapes templates dir: %s
Error message
path escapes templates dir: %s
What it means
Returned by `resolveTemplateAbs` after `filepath.Rel(templatesBase, abs)` yields a relative path starting with `..` (or equal to `..`). This is a security guard preventing path traversal: the resolved absolute path must stay inside `data/templates/`, otherwise an attacker-controlled `--path` could read or write arbitrary files.
Source
Thrown at kernel/cli/cmd/template.go:202
return nil
},
}
// resolveTemplateAbs 把模板路径解析为 data/templates 下的绝对路径,拒绝越界。
// 接受绝对路径或相对 data/templates 的相对路径。
func resolveTemplateAbs(p string) (string, error) {
if p == "" {
return "", fmt.Errorf("--path is required")
}
abs := p
if !filepath.IsAbs(abs) {
abs = filepath.Join(util.DataDir, "templates", p)
}
abs = filepath.Clean(abs)
templatesBase := filepath.Clean(filepath.Join(util.DataDir, "templates"))
rel, err := filepath.Rel(templatesBase, abs)
if err != nil || strings.HasPrefix(rel, "..") || rel == ".." {
return "", fmt.Errorf("path escapes templates dir: %s", p)
}
return abs, nil
}
func init() {
templateGetCmd.Flags().String("path", "", "template path (absolute or relative to data/templates)")
templateRemoveCmd.Flags().String("path", "", "template path (absolute or relative to data/templates)")
templateRenderCmd.Flags().String("path", "", "template path (absolute or relative to data/templates)")
templateRenderCmd.Flags().String("id", "", "block ID to render against")
templateSaveAsCmd.Flags().String("id", "", "source document block ID")
templateSaveAsCmd.Flags().String("name", "", "template name without extension")
templateSaveAsCmd.Flags().Bool("overwrite", false, "overwrite if exists")
templateCreateCmd.Flags().String("name", "", "template name without extension")
templateCreateCmd.Flags().String("data", "", "markdown content")
templateCreateCmd.Flags().String("file", "", "read content from file path (- for stdin)")
templateCreateCmd.Flags().Bool("overwrite", false, "overwrite if exists")
rootCmd.AddCommand(templateCmd)View on GitHub (pinned to 251596fc0d)
Solutions
- Use a plain template name or a path strictly under `data/templates/`, e.g. `--path sub/foo.md`.
- If you genuinely need a file from elsewhere, copy it into `data/templates/` first, then reference it by its in-dir path.
- Sanitize untrusted input by stripping leading `/` and any `..` segments before passing to the CLI.
Example fix
// before siyuan template get --path ../../shared/note.md // after cp ../../shared/note.md data/templates/shared-note.md && siyuan template get --path shared-note.md
Defensive patterns
Strategy: validation
Validate before calling
// Pre-sanitize before calling resolveTemplateAbs-equivalent logic:
clean := filepath.Clean(p)
rel, err := filepath.Rel(templatesBase, clean)
if err != nil || strings.HasPrefix(rel, "..") || rel == ".." {
return fmt.Errorf("path escapes templates dir: %s", p)
} Prevention
- Never concatenate untrusted input into --path.
- Prefer plain template names over filesystem paths.
- Copy external files into data/templates/ first, then reference them in-dir.
When it happens
Trigger: Passing `--path ../../../etc/passwd`, `--path /etc/passwd` (absolute path outside the templates dir), or any value that, after `filepath.Clean`, resolves above `data/templates`. Symlinks inside the dir are not dereferenced by this check, but lexical escapes are blocked.
Common situations: User typos with leading `../`; tooling that concatenates untrusted input into `--path`; absolute paths intended for a different working directory.
Related errors
- template path is outside templates directory
- invalid content template path
- content template path is outside templates directory
- --id is required
- --name is required
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/0542bc3f6e18297f.
Report an issue: GitHub.