{"record":{"id":"0387450a19b3c111","repo":"siyuan-note/siyuan","slug":"encrypted-sy-s-base-id-s-root-id-s","errorCode":null,"errorMessage":"encrypted .sy [%s]: base id [%s] != root id [%s]","messagePattern":"encrypted \\.sy \\[(.+?)\\]: base id \\[(.+?)\\] != root id \\[(.+?)\\]","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"kernel/filesys/tree.go","lineNumber":661,"sourceCode":"\t\treturn\n\t}\n\n\tif treenode.UpgradeSpec(ret) {\n\t\tneedFix = true\n\t}\n\n\t// v3.5.1 https://github.com/siyuan-note/siyuan/pull/16657 引入的问题，属性值未转义\n\t// v3.5.2 https://github.com/siyuan-note/siyuan/issues/16686 进行了修复，并加了订正逻辑 https://github.com/siyuan-note/siyuan/pull/16712\n\t// https://github.com/siyuan-note/siyuan/security/advisories/GHSA-ff66-236v-p4fg XSS 漏洞：\"title\": \"&amp;\\\" onmouseenter=\\\"require('child_process').exec('calc')\"\n\tif escapeAttributeValues(ret) {\n\t\tneedFix = true\n\t}\n\n\tif pathID := util.GetTreeID(p); pathID != ret.Root.ID {\n\t\tif encrypted {\n\t\t\t// 加密 .sy：基名 ID（pathID）必须与解密后的根块 ID 一致。不一致说明密文被替换、\n\t\t\t// 文件名被篡改或 AAD 认证被绕过，不得静默修正——fail-closed，符合加密笔记本威胁模型。\n\t\t\terr = fmt.Errorf(\"encrypted .sy [%s]: base id [%s] != root id [%s]\", p, pathID, ret.Root.ID)\n\t\t\tlogging.LogErrorf(\"%s\", err)\n\t\t\treturn\n\t\t}\n\t\tneedFix = true\n\t\tlogging.LogInfof(\"reset tree id from [%s] to [%s]\", ret.Root.ID, pathID)\n\t\tret.Root.ID = pathID\n\t\tret.ID = pathID\n\t\tret.Root.SetIALAttr(\"id\", ret.ID)\n\t}\n\tif treenode.FixInvalidListChildren(ret.Root) {\n\t\tneedFix = true\n\t}\n\tif treenode.NormalizeTabs(ret.Root) {\n\t\tneedFix = true\n\t}\n\n\tif !needFix {\n\t\treturn jsonData, false, nil","sourceCodeStart":643,"sourceCodeEnd":679,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/filesys/tree.go#L643-L679","documentation":"For encrypted notebooks, fixTreeJSONData enforces fail-closed identity: the base-name ID of the .sy path must equal the root block ID of the decrypted content. A mismatch proves the ciphertext was swapped, the file renamed, or AAD authentication bypassed, so it is never silently auto-fixed (unencrypted trees get their ID reset instead).","triggerScenarios":"LoadTreeWithFix on an encrypted notebook where a .sy file was renamed, its ciphertext replaced with another document's, or where the decryption produced content whose Root.ID differs from the path-derived ID.","commonSituations":"Copying .sy files between notebooks and renaming them in an encrypted notebook, tampering attempts, or restoring encrypted files from a partial/mixed backup.","solutions":["Rename the .sy file back to the ID matching its root block ID (root.ID.sy).","Restore the original encrypted file from backup or sync history; do not hand-edit ciphertext.","If the mismatch is intentional, re-encrypt: export the document and re-import it into the encrypted notebook."],"exampleFix":"// before: notebook/20240101-newname.sy decrypts to root.ID 20240101-oldname\n// after: mv 20240101-newname.sy 20240101-oldname.sy","handlingStrategy":"validation","validationCode":"pathID := util.GetTreeID(p)\n// before writing into an encrypted notebook: ensure filename == root.ID + \".sy\"","typeGuard":"func encryptedIdentityOK(pathID, rootID string) bool { return pathID == rootID }","tryCatchPattern":"if _, err := filesys.LoadTreeWithFix(box, p, lute); err != nil && strings.Contains(err.Error(), \"base id\") {\n    // fail closed: do NOT auto-fix; restore the original encrypted file\n}","preventionTips":["Never rename or swap .sy files inside encrypted notebooks","Restore encrypted notebooks only from complete, consistent backups","Re-import (export/import) documents instead of moving files between encrypted boxes"],"tags":["encryption","integrity","identity-check"],"backgroundTag":"checksum-mismatch","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"}