{"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":1685,"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":1667,"sourceCodeEnd":1703,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/crypto.go#L1667-L1703","documentation":"Thrown by ChangeMasterPassword when newPassword is empty. This is the first validation before any crypto work begins. An empty master password would produce a deterministic KEK and is explicitly rejected as a security invariant.","triggerScenarios":"Called via the HTTP API changeMasterPassword (POST /api/notebook/changeMasterPassword) with an empty newPassword field in the JSON body. Also reachable from mobile/harmony bindings that call ChangeMasterPassword directly.","commonSituations":"Frontend form validation bug that submits an empty new-password field. API consumer sends {\"oldPassword\":\"...\",\"newPassword\":\"\"} due to a serialization error. A test harness or script calls ChangeMasterPassword with an empty string.","solutions":["Provide a non-empty newPassword in the API request body.","Add frontend validation to reject empty new-password before submitting the API call.","If calling programmatically, validate the password is non-empty before invoking ChangeMasterPassword."],"exampleFix":"// before\nmodel.ChangeMasterPassword(oldPassword, \"\")\n// after\nif newPassword == \"\" {\n    return errors.New(\"new password must not be empty\")\n}\nmodel.ChangeMasterPassword(oldPassword, newPassword)","handlingStrategy":"validation","validationCode":"// Validate before calling ChangeMasterPassword:\nif newPassword == \"\" {\n    return errors.New(\"new password must not be empty\")\n}\nmodel.ChangeMasterPassword(oldPassword, newPassword)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Add frontend validation to require a non-empty new password.","Validate API request payloads before calling model functions.","In test scripts, always provide a non-empty password."],"tags":["encryption","validation","password","go"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}