{"record":{"id":"29d27bf6a2027573","repo":"siyuan-note/siyuan","slug":"new-password-must-not-be-empty","errorCode":null,"errorMessage":"new password must not be empty","messagePattern":"new password must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/crypto.go","lineNumber":1710,"sourceCode":"func ClearDEK(boxID string) {\n\tLockBox(boxID)\n}\n\n// ChangeMasterPassword 改主密码：用旧密码校验后，用新密码派生新 KEK，\n// 重新加密 verifier，并把所有加密笔记本的 WrappedDEK 用新 KEK 重新包络后写回各自的 BoxConf。\n//\n// 使用两阶段提交确保崩溃后可恢复：\n//\n//\tPhase 0: 预计算所有新 WrappedDEK（内存）\n//\tPhase 1: 写入 migration manifest\n//\tPhase 2: 切换全局 verifier\n//\tPhase 3: 写入各 box conf + backup\n//\tPhase 4: 清除 manifest\n//\n// 注意：必须在所有加密笔记本都已 Unmount 的状态下调用（DEK 不在内存），否则新旧 KEK 切换会让缓存与磁盘不一致。\nfunc ChangeMasterPassword(oldPassword, newPassword string) error {\n\tif len(newPassword) == 0 {\n\t\treturn errors.New(\"new password must not be empty\")\n\t}\n\n\tnotebookCryptoMu.Lock()\n\tdefer notebookCryptoMu.Unlock()\n\n\t// 改密期间不能有已 Mount 的加密笔记本（DEK 在内存），否则新旧 KEK 切换会让缓存与磁盘不一致\n\tcachedDEKsLock.RLock()\n\tdekCount := len(cachedDEKs)\n\tcachedDEKsLock.RUnlock()\n\tif dekCount > 0 {\n\t\treturn errors.New(\"cannot change master password while encrypted notebooks are unlocked (DEKs in memory), lock them first\")\n\t}\n\n\toldKEK, err := deriveKEK(oldPassword)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer zeroAndClear(oldKEK)","sourceCodeStart":1692,"sourceCodeEnd":1728,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/crypto.go#L1692-L1728","documentation":"ChangeMasterPassword rejects an empty new password. A master password is the root of the KEK derivation for encrypted notebooks, so a zero-length value would produce an unusable/weakened key hierarchy and is refused before any re-wrap work begins.","triggerScenarios":"Calling model.ChangeMasterPassword(oldPassword, \"\") from a UI/API path that did not validate the new password field, e.g. user submitted a blank form or a script passed an empty string.","commonSituations":"Frontend form submitted without client-side validation; automation scripts setting a password variable that is empty; localization/UI bugs clearing the field.","solutions":["Validate the new password is non-empty (and meets strength requirements) in the caller before invoking ChangeMasterPassword","Return a user-facing message prompting entry of a new password","Check for whitespace-only inputs too, since the kernel only rejects zero length"],"exampleFix":"// before\nawait changeMasterPassword(oldPw, newPwInput.value)\n// after\nif (!newPwInput.value.trim()) throw new Error(window.siyuan.languages.passwordRequired)\nawait changeMasterPassword(oldPw, newPwInput.value)","handlingStrategy":"validation","validationCode":"if len(newPassword) === 0 { throw new Error(\"new password is required\") }\nawait changeMasterPassword(oldPassword, newPassword)","typeGuard":null,"tryCatchPattern":"try { await changeMasterPassword(oldPw, newPw) } catch (e) { if (e.message.includes(\"new password must not be empty\")) { showError(\"请输入新密码\") } }","preventionTips":["Validate password fields (non-empty, strength) in the UI before submission","Trim-check inputs to reject whitespace-only values","Guard automation scripts against empty password variables"],"tags":["password","validation","encryption"],"backgroundTag":"empty-required-field","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"}