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 = leaseErr

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Provide a path strictly inside the box, e.g. `20240101.../subdoc.sy`, with no `..` segments.
  2. Sanitize input with `filepath.Clean` and reject results that leave the box root.
  3. 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

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


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/7328361e79eb9f37. Report an issue: GitHub.