{"record":{"id":"0406d11f9797372a","repo":"siyuan-note/siyuan","slug":"path-belongs-to-encrypted-notebook-s-s-0406d1","errorCode":null,"errorMessage":"path belongs to encrypted notebook [%s]: %s","messagePattern":"path belongs to encrypted notebook \\[(.+?)\\]: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/tools/file.go","lineNumber":107,"sourceCode":"\nfunc resolvePath(rel string) (string, error) {\n\trel = filepath.Clean(strings.ReplaceAll(rel, \"/\", string(os.PathSeparator)))\n\tabs := filepath.Join(util.WorkspaceDir, rel)\n\tif err := authorizePath(abs, rel); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn abs, nil\n}\n\n// authorizePath 校验单个最终路径是否允许访问，display 仅用于错误信息：顶层调用传工作区相对路径，\n// 递归遍历、目录拷贝和压缩包解压传最终路径本身。\nfunc authorizePath(abs, display string) error {\n\tif !gulu.File.IsSubPath(util.WorkspaceDir, abs) {\n\t\treturn fmt.Errorf(\"path escapes workspace: %s\", display)\n\t}\n\t// 拒绝加密笔记本目录：MCP 文件工具不能读写加密 box 下的文件（防止密文泄漏或明文破坏加密格式）\n\tif boxID, encrypted := rejectEncryptedPath(abs); encrypted {\n\t\treturn fmt.Errorf(\"path belongs to encrypted notebook [%s]: %s\", boxID, display)\n\t}\n\t// 防止 symlink 逃逸工作区：解析符号链接后再次检查\n\tif resolved := util.ResolveLongestExistingParent(abs); resolved != abs && !gulu.File.IsSubPath(util.WorkspaceDir, resolved) {\n\t\treturn fmt.Errorf(\"symlink escapes workspace: %s\", display)\n\t}\n\t// 禁止访问敏感文件（conf/conf.json、data/snippets/conf.json、data/templates、data/.siyuan/publishAccess.json），\n\t// 与 HTTP 文件 API 共用同一黑名单（见 kernel/util/path_guard.go 的 IsForbiddenAbsPath）\n\tif util.IsForbiddenAbsPath(abs) {\n\t\treturn fmt.Errorf(\"access to sensitive workspace file is forbidden: %s\", display)\n\t}\n\treturn nil\n}\n\n// authorizeFinalPath 对即将打开或创建的最终路径做授权。resolvePath 只覆盖调用方给出的路径，\n// 容器路径合法不代表其后代合法：递归遍历、复制、解压、删除、重命名都必须对每一个后代路径再次调用本函数。\nfunc authorizeFinalPath(abs string) error {\n\treturn authorizePath(abs, abs)\n}","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/mcp/tools/file.go#L89-L125","documentation":"authorizePath (kernel/mcp/tools/file.go) additionally rejects paths that resolve inside an encrypted notebook (box) directory, returning the owning box ID. MCP file tools cannot read or write under encrypted boxes because that would leak ciphertext semantics or corrupt the encrypted format; use the block-level MCP tools after unlocking instead.","triggerScenarios":"Any MCP file read/write/copy/archive-extract whose final path resolves under data/<boxID>/ where <boxID> is an encrypted notebook, e.g. writing an asset into data/202401...-/ directly.","commonSituations":"Scripts composing data/<boxID>/ paths manually from a notebook ID; copying directories that contain encrypted boxes; extracting archives that embed encrypted-box paths.","solutions":["Target a non-encrypted notebook's directory instead","Use the block MCP tools (after unlocking the notebook) rather than raw file access for content inside encrypted boxes","Pre-check the box's encryption state via model.IsEncryptedBox/isBoxUnlocked-equivalent APIs before building the path","Exclude encrypted box directories from directory copies and archive contents before the operation"],"exampleFix":"// before\nawait call(\"write_file\", {path: \"data/20240101120000-abc/file.txt\"}) // encrypted box\n// after\nawait call(\"write_file\", {path: \"data/assets/file.txt\"}) // non-encrypted location","handlingStrategy":"validation","validationCode":"function isEncryptedBoxPath(p, encryptedBoxIds) {\n  return encryptedBoxIds.some(id => p === `data/${id}` || p.startsWith(`data/${id}/`));\n}","typeGuard":null,"tryCatchPattern":"try { await call(\"write_file\", {path}) } catch (e) { if (String(e).startsWith(\"path belongs to encrypted notebook\")) { console.error(\"use block tools for encrypted boxes, path:\", path); } throw e; }","preventionTips":["Keep an up-to-date list of encrypted box IDs and exclude their directories","Use block-level MCP tools (after unlock) for encrypted content","Filter encrypted boxes out of directory copies and archives"],"tags":["mcp","security","encryption","filesystem"],"backgroundTag":"path-traversal-blocked","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}