{"record":{"id":"7328361e79eb9f37","repo":"siyuan-note/siyuan","slug":"path-s-must-not-contain","errorCode":null,"errorMessage":"path [%s] must not contain '..'","messagePattern":"path \\[(.+?)\\] must not contain '\\.\\.'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/filesys/tree.go","lineNumber":161,"sourceCode":"\treturn\n}\n\n// ValidateBoxRelativePath 校验 box 内相对路径是否安全。\n// 拒绝 ..、绝对路径，确保最终路径位于 <DataDir>/<boxID> 内。\n// 允许路径以 / 开头（如 /20230101/xxx.sy），会自动标准化再去掉前导斜杠。\n// 根路径（\"/\" 或 \"\"）合法，返回空字符串。\nfunc ValidateBoxRelativePath(boxID, p string) (string, error) {\n\tp = filepath.ToSlash(p)\n\t// 记录原始路径用于 IsSubPath 校验\n\torigP := p\n\t// 标准化：去掉前导 /\n\tp = strings.TrimPrefix(p, \"/\")\n\t// 根路径直接放行（box 根目录本身是合法路径）\n\tif p == \"\" {\n\t\treturn p, nil\n\t}\n\tif strings.HasPrefix(p, \"..\") || strings.Contains(p, \"/../\") || strings.HasSuffix(p, \"/..\") || p == \"..\" || p == \".\" {\n\t\treturn \"\", fmt.Errorf(\"path [%s] must not contain '..'\", origP)\n\t}\n\tresolved := filepath.Join(util.DataDir, boxID, origP)\n\tboxRoot := filepath.Join(util.DataDir, boxID)\n\tif !gulu.File.IsSubPath(boxRoot, resolved) {\n\t\treturn \"\", fmt.Errorf(\"path [%s] escapes box directory\", origP)\n\t}\n\treturn p, nil\n}\n\nfunc LoadTreeWithFix(boxID, p string, luteEngine *lute.Lute) (ret *parse.Tree, needFix bool, err error) {\n\tif _, err = ValidateBoxRelativePath(boxID, p); err != nil {\n\t\tlogging.LogErrorf(\"invalid tree path [%s] for box [%s]: %s\", p, boxID, err)\n\t\treturn\n\t}\n\n\tdek, encrypted, releaseCryptoLease, leaseErr := acquireCryptoLease(boxID)\n\tif leaseErr != nil {\n\t\terr = leaseErr","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/filesys/tree.go#L143-L179","documentation":"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.","triggerScenarios":"Passing a path like `../secret`, `a/../../b`, `..`, `.` or trailing `/..` as a box-relative document path to tree load/save APIs (`LoadTree`, `LoadTreeWithFix`, etc.).","commonSituations":"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.","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."],"exampleFix":"// before\n_, err := filesys.ValidateBoxRelativePath(box, \"../other-box/doc.sy\")\n// after\n_, err := filesys.ValidateBoxRelativePath(box, \"20240101000000-abcdef1234567/sub.sy\")","handlingStrategy":"validation","validationCode":"// Reject parent traversal before calling ValidateBoxRelativePath:\nslashed := filepath.ToSlash(p)\nif strings.HasPrefix(slashed, \"..\") || strings.Contains(slashed, \"/../\") ||\n    strings.HasSuffix(slashed, \"/..\") || slashed == \"..\" || slashed == \".\" {\n    return \"\", fmt.Errorf(\"path [%s] must not contain '..'\", p)\n}","typeGuard":"func hasTraversal(p string) bool {\n    s := filepath.ToSlash(p)\n    return strings.HasPrefix(s, \"..\") || strings.Contains(s, \"/../\") ||\n        strings.HasSuffix(s, \"/..\") || s == \"..\" || s == \".\"\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["security","path-traversal","filesystem","validation"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}