{"record":{"id":"f543dd6293461ecb","repo":"siyuan-note/siyuan","slug":"password-must-not-be-empty","errorCode":null,"errorMessage":"password must not be empty","messagePattern":"password must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/crypto.go","lineNumber":952,"sourceCode":"\treturn boxConf.Encrypted, nil\n}\n\n// cachedDEKs 缓存已解锁加密笔记本的 DEK，按 boxID 索引。\n// KEK 不全局缓存（\"严格每笔记本单独解锁\"语义）：UnlockBox 临时派生 KEK 解出 DEK 后即丢弃 KEK，\n// 仅保留 per-box DEK 供后续读写加解密。\nvar (\n\tcachedDEKs     = map[string][]byte{}\n\tcachedDEKsLock sync.RWMutex\n)\n\n// boxLastAccess 记录每个加密笔记本最近一次真实用户交互或显式保活时间（unix 纳秒），供自动锁定 cron 使用。\n// key: boxID, value: *atomic.Int64。UnlockBox 成功时初始化，Unmount 时清理。\nvar boxLastAccess sync.Map\n\n// EnableEncryptedNotebookWithSync 在启用前先完成同步；同步恢复了既有配置时只校验原主密码。\nfunc EnableEncryptedNotebookWithSync(password string) error {\n\tif len(password) == 0 {\n\t\treturn errors.New(\"password must not be empty\")\n\t}\n\tif err := SyncDataBeforeEnableEncryptedNotebook(); err != nil {\n\t\treturn err\n\t}\n\n\t// 同步可能已经从其他设备恢复了完整配置。此时校验用户输入的是原主密码，不能再创建新的密钥体系。\n\tif NotebookCryptoEnabled() {\n\t\tnotebookCryptoMu.Lock()\n\t\tdefer notebookCryptoMu.Unlock()\n\t\tkek, err := deriveKEK(password)\n\t\tif kek != nil {\n\t\t\tzeroAndClear(kek)\n\t\t}\n\t\treturn err\n\t}\n\treturn EnableEncryptedNotebook(password)\n}\n","sourceCodeStart":934,"sourceCodeEnd":970,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/crypto.go#L934-L970","documentation":"EnableEncryptedNotebookWithSync rejects an empty password immediately (len(password) == 0) before any synchronization or key generation. This is the entry-point guard for the sync-then-enable flow; passing an empty password would otherwise produce a meaningless sync round and risk creating a key derived from an empty secret. The message is the hardcoded 'password must not be empty' (not a localized string).","triggerScenarios":"EnableEncryptedNotebookWithSync is called with an empty byte string. Typical when the UI submits the enable form without a password entry, or a programmatic caller passes an unset variable.","commonSituations":"User clicks 'enable encryption' without entering a password; frontend validation bypassed; automation passing an empty env variable; password field cleared before submit.","solutions":["Validate non-empty password in the UI before invoking the enable flow.","Programmatically guard: `if password == \"\" { return errors.New(\"password required\") }`.","Ensure the password input is bound to the actual form field, not an unset default."],"exampleFix":"// before\nerr := model.EnableEncryptedNotebookWithSync(password)\n\n// after\nif password == \"\" {\n    return errors.New(\"password required\")\n}\nerr := model.EnableEncryptedNotebookWithSync(password)","handlingStrategy":"validation","validationCode":"// Guard before calling the sync-then-enable entry point.\nif len(password) == 0 {\n    return errors.New(\"password required\")\n}\nerr := model.EnableEncryptedNotebookWithSync(password)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate non-empty password in the UI before submitting the enable form.","Bind the password input directly to the request payload, not an unset default.","Add a unit test asserting the empty-password guard fires."],"tags":["crypto","encryption","validation","password"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}