siyuan-note/siyuan · error
path [ ] must not contain '..
Error message
path [%s] must not contain '..'
What it means
ValidateBoxRelativePath sanitizes notebook-relative document paths. It rejects any path containing ".." (leading, embedded, trailing, or exactly ".") because such segments could escape the notebook's data directory. This is a safety check run before any tree read or write.
Solutions
- Remove ".." segments and pass a clean path rooted at the notebook, e.g. "/folder/doc.sy".
- Resolve the path yourself against the notebook root and verify it stays inside before calling.
- Sanitize/validate user input before building the path argument.
Example fix
// before
LoadTreeWithFix("20240101120000-abc", "../other/doc.sy", lute)
// after
LoadTreeWithFix("20240101120000-abc", "/other/doc.sy", lute) Defensive patterns
Strategy: validation
Validate before calling
function safeBoxPath(p) {
if (p.includes("..") || p === "." || !p.startsWith("/")) throw new Error("unsafe box path");
return p;
} Try / catch
if _, err := filesys.ValidateBoxRelativePath(boxID, p); err != nil {
// reject the request before any read/write
} Prevention
- Sanitize user-supplied paths and strip ".." segments early.
- Always express document paths relative to the notebook with a leading slash.
- Add a containment check at your API boundary before calling kernel functions.
When it happens
Trigger: Calling ReadDocHPath, LoadTreeWithFix, prepareWriteTree, etc. with a path like "../other/doc.sy", "a/../b.sy", "a/b/..", ".", or a raw "..".
Common situations: User-supplied paths passed straight into the API; scripts built from untrusted input; path-joining bugs that introduce ".." segments; Windows/Unix separator handling producing normalized ".." after conversion.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- path [ ] escapes box directory
- asset path escapes data directory
- asset path escapes data directory
- export path is outside export directory
- history path [ ] is not in workspace
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/7328361e79eb9f37.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/filesys/tree.go:161
return
}
// ValidateBoxRelativePath 校验 box 内相对路径是否安全。
// 拒绝 ..、绝对路径,确保最终路径位于 <DataDir>/<boxID> 内。
// 允许路径以 / 开头(如 /20230101/xxx.sy),会自动标准化再去掉前导斜杠。
// 根路径("/" 或 "")合法,返回空字符串。
func ValidateBoxRelativePath(boxID, p string) (string, error) {
p = filepath.ToSlash(p)
// 记录原始路径用于 IsSubPath 校验
origP := p
// 标准化:去掉前导 /
p = strings.TrimPrefix(p, "/")
// 根路径直接放行(box 根目录本身是合法路径)
if p == "" {
return p, nil
}
if strings.HasPrefix(p, "..") || strings.Contains(p, "/../") || strings.HasSuffix(p, "/..") || p == ".." || p == "." {
return "", fmt.Errorf("path [%s] must not contain '..'", origP)
}
resolved := filepath.Join(util.DataDir, boxID, origP)
boxRoot := filepath.Join(util.DataDir, boxID)
if !gulu.File.IsSubPath(boxRoot, resolved) {
return "", fmt.Errorf("path [%s] escapes box directory", origP)
}
return p, nil
}
func LoadTreeWithFix(boxID, p string, luteEngine *lute.Lute) (ret *parse.Tree, needFix bool, err error) {
if _, err = ValidateBoxRelativePath(boxID, p); err != nil {
logging.LogErrorf("invalid tree path [%s] for box [%s]: %s", p, boxID, err)
return
}
dek, encrypted, releaseCryptoLease, leaseErr := acquireCryptoLease(boxID)
if leaseErr != nil {
err = leaseErrView on GitHub (pinned to 9f775e8a12)