siyuan-note/siyuan · error
path [%s] must not contain '..'
Error message
path [%s] must not contain '..'
What it means
Returned by `filesys.ValidateBoxRelativePath` when the box-relative path contains a parent-traversal segment. The check rejects any path that starts with `..`, contains `/../`, ends with `/..`, or equals `..`/`.`. This is a lexical guard run before path joining, defending notebook data from directory-traversal input.
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 251596fc0d)
Solutions
- Provide a path strictly inside the box, e.g. `20240101.../subdoc.sy`, with no `..` segments.
- Sanitize input with `filepath.Clean` and reject results that leave the box root.
- If the path came from a client, treat this error as a rejected malicious request and log it.
Example fix
// before _, err := filesys.ValidateBoxRelativePath(box, "../other-box/doc.sy") // after _, err := filesys.ValidateBoxRelativePath(box, "20240101000000-abcdef1234567/sub.sy")
Defensive patterns
Strategy: validation
Validate before calling
// Reject parent traversal before calling ValidateBoxRelativePath:
slashed := filepath.ToSlash(p)
if strings.HasPrefix(slashed, "..") || strings.Contains(slashed, "/../") ||
strings.HasSuffix(slashed, "/..") || slashed == ".." || slashed == "." {
return "", fmt.Errorf("path [%s] must not contain '..'", p)
} Type guard
func hasTraversal(p string) bool {
s := filepath.ToSlash(p)
return strings.HasPrefix(s, "..") || strings.Contains(s, "/../") ||
strings.HasSuffix(s, "/..") || s == ".." || s == "."
} Prevention
- Never feed raw client input into box-relative path APIs.
- Build doc paths from IDs server-side, not from request strings.
- Log and rate-limit inputs that trip this check to spot probing.
When it happens
Trigger: Passing a path like `../secret`, `a/../../b`, `..`, `.` or trailing `/..` as a box-relative document path to tree load/save APIs (`LoadTree`, `LoadTreeWithFix`, etc.).
Common situations: User input or sync/import data containing relative segments; tooling that builds paths by concatenating untrusted strings; a malformed client request naming a doc path.
Related errors
- path [%s] escapes box directory
- path is not a child of assets directory: %s
- import path is not sub path of import dir
- path escapes templates dir: %s
- asset path must be relative to data directory: %s
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/7328361e79eb9f37.
Report an issue: GitHub.