{"record":{"id":"068182d284d9d70f","repo":"siyuan-note/siyuan","slug":"invalid-sy-base-name-s-must-end-with-sy","errorCode":null,"errorMessage":"invalid .sy base name [%s]: must end with .sy","messagePattern":"invalid \\.sy base name \\[(.+?)\\]: must end with \\.sy","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/filesys/crypto_hook.go","lineNumber":112,"sourceCode":"\t}()\n\taad, err := SyAAD(boxID, relativePath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn util.DecryptWithAAD(fileKey, data, []byte(aad))\n}\n\n// SyObjectBase 从 box 内相对路径提取稳定文件基名并校验合法性。\n// 接受形如 <rootID>.sy 的基名：扩展名必须是 .sy，且 stem 是合法节点 ID。\n// 非法扩展名或非节点 ID 模式返回错误，避免把任意路径当 AAD 绑定物产生不可解密的数据。\n// 由 filesys、model 历史查看/回滚、import 等所有 .sy 加解密路径共同使用，保证 AAD 一致。\nfunc SyObjectBase(relativePath string) (string, error) {\n\tbase := relativePath\n\tif idx := strings.LastIndexAny(relativePath, \"/\\\\\"); idx >= 0 {\n\t\tbase = relativePath[idx+1:]\n\t}\n\tif !strings.HasSuffix(base, \".sy\") {\n\t\treturn \"\", fmt.Errorf(\"invalid .sy base name [%s]: must end with .sy\", base)\n\t}\n\tstem := strings.TrimSuffix(base, \".sy\")\n\tif !ast.IsNodeIDPattern(stem) {\n\t\treturn \"\", fmt.Errorf(\"invalid .sy base name [%s]: stem is not a node ID\", base)\n\t}\n\treturn base, nil\n}\n\n// SyAAD 构造 .sy 密文的 AAD：siyuan:file:<boxID>:<稳定文件基名>。\n// 父目录不进 AAD——同 box 内文件名不变的移动允许原样 Rename 密文，内容/box/类型/对象 ID 仍受认证。\nfunc SyAAD(boxID, relativePath string) (string, error) {\n\tbase, err := SyObjectBase(relativePath)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn \"siyuan:file:\" + boxID + \":\" + base, nil\n}\n","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/filesys/crypto_hook.go#L94-L130","documentation":"Returned by `filesys.SyObjectBase` when the basename of a `.sy` path does not end with the `.sy` suffix. `SyObjectBase` extracts the stable file basename used to bind encryption AAD (`siyuan:file:<boxID>:<base>`), so the extension is part of the cryptographic identity; an unrecognized extension is rejected rather than silently producing undecryptable data.","triggerScenarios":"Calling `SyObjectBase` or `SyAAD` with a path whose last segment is not a `.sy` file (e.g. `foo.json`, `bar.md`, a directory, or a path with no extension). This commonly happens when a non-`.sy` relative path is routed through the encrypted-file hook.","commonSituations":"Passing an asset or attribute-view JSON path to the `.sy` AAD helper; mismatched extension after a rename/import; calling the encryption hook for a temporary file.","solutions":["Ensure the path argument points at a `<rootID>.sy` file before calling `SyObjectBase`/`SyAAD`.","Route non-`.sy` files through the correct helper (asset/AV encryption uses different AAD construction).","If the path is user-supplied, validate `strings.HasSuffix(base, \".sy\")` upstream."],"exampleFix":"// before\nbase, err := filesys.SyObjectBase(\"20240101000000-abc1234.json\")\n// after\nbase, err := filesys.SyObjectBase(\"20240101000000-abc1234.sy\")","handlingStrategy":"type-guard","validationCode":"// Confirm .sy suffix before calling SyObjectBase/SyAAD:\nbase := p\nif idx := strings.LastIndexAny(p, \"/\\\\\"); idx >= 0 {\n    base = p[idx+1:]\n}\nif !strings.HasSuffix(base, \".sy\") {\n    return \"\", fmt.Errorf(\"not a .sy file: %s\", base)\n}","typeGuard":"func isSyPath(p string) bool {\n    base := p\n    if idx := strings.LastIndexAny(p, \"/\\\\\"); idx >= 0 {\n        base = p[idx+1:]\n    }\n    return strings.HasSuffix(base, \".sy\")\n}","tryCatchPattern":null,"preventionTips":["Route only .sy files through SyObjectBase; use the asset/AV AAD helpers for those types.","Centralize encryption-AAD construction so callers cannot pass the wrong path type.","Unit-test the helper with .json/.md/.sy inputs to lock the contract."],"tags":["encryption","filesystem","validation","aad"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}