From 3066442c1b6e1db21e5a8d75ebfb491ce2427e1a Mon Sep 17 00:00:00 2001 From: Joe Julian Date: Sun, 29 Mar 2026 17:27:19 -0700 Subject: [PATCH] Reset password peek across view changes --- main.go | 13 ++++++++++++- main_test.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ui_editor.go | 1 + ui_forms.go | 12 ++++++++---- 4 files changed, 75 insertions(+), 5 deletions(-) diff --git a/main.go b/main.go index 5d84d85..5516f31 100644 --- a/main.go +++ b/main.go @@ -386,20 +386,27 @@ func (u *ui) selectedAttachmentNames() []string { } func (u *ui) showEntriesSection() { + u.resetPasswordPeek() u.state.ShowSection(appstate.SectionEntries) u.filter() } func (u *ui) showTemplatesSection() { + u.resetPasswordPeek() u.state.ShowSection(appstate.SectionTemplates) u.filter() } func (u *ui) showRecycleBinSection() { + u.resetPasswordPeek() u.state.ShowSection(appstate.SectionRecycleBin) u.filter() } +func (u *ui) resetPasswordPeek() { + u.showPassword = false +} + func (u *ui) childGroups() []string { u.state.SetSearchQuery(u.search.Text()) groups, err := u.state.ChildGroups() @@ -506,6 +513,7 @@ func (u *ui) createVaultAction() error { u.vaultPath.SetText(u.saveAsTargetPath()) u.noteRecentVault(u.saveAsTargetPath()) } + u.resetPasswordPeek() u.currentPath = append([]string(nil), u.state.CurrentPath...) u.editingEntry = false u.filter() @@ -526,6 +534,7 @@ func (u *ui) openVaultAction() error { return err } u.noteRecentVault(path) + u.resetPasswordPeek() u.currentPath = append([]string(nil), u.state.CurrentPath...) u.restoreRecentVaultGroup(path) u.editingEntry = false @@ -573,6 +582,7 @@ func (u *ui) openRemoteAction() error { u.remotePassword.Text(), u.rememberRemoteAuth.Value, ) + u.resetPasswordPeek() u.enterHiddenVaultRoot() u.editingEntry = false u.filter() @@ -585,7 +595,7 @@ func (u *ui) lockAction() error { return err } u.currentPath = append([]string(nil), u.state.CurrentPath...) - u.showPassword = false + u.resetPasswordPeek() u.editingEntry = false u.filter() return nil @@ -600,6 +610,7 @@ func (u *ui) unlockAction() error { if err := u.state.Unlock(key); err != nil { return err } + u.resetPasswordPeek() u.currentPath = append([]string(nil), u.state.CurrentPath...) u.editingEntry = false u.filter() diff --git a/main_test.go b/main_test.go index 73d63d1..f1f7703 100644 --- a/main_test.go +++ b/main_test.go @@ -2097,6 +2097,60 @@ func TestEnterOnRemoteLifecycleScreenDefaultsToOpenRemoteVault(t *testing.T) { } } +func TestMasterPasswordPeekResetsAfterOpeningVault(t *testing.T) { + t.Parallel() + + path := filepath.Join(t.TempDir(), "vault.kdbx") + var encoded bytes.Buffer + if err := vault.SaveKDBX(&encoded, vault.Model{ + Entries: []vault.Entry{ + {ID: "vault-console", Title: "Vault Console", Password: "token-1", Path: []string{"Root", "Internet"}}, + }, + }, "correct horse battery staple"); err != nil { + t.Fatalf("SaveKDBX() error = %v", err) + } + if err := os.WriteFile(path, encoded.Bytes(), 0o600); err != nil { + t.Fatalf("WriteFile(vault.kdbx) error = %v", err) + } + + u := newUIWithSession("desktop", &session.Manager{}) + u.masterPassword.SetText("correct horse battery staple") + u.vaultPath.SetText(path) + u.showPassword = true + + if err := u.openVaultAction(); err != nil { + t.Fatalf("openVaultAction() error = %v", err) + } + if u.showPassword { + t.Fatal("showPassword = true after openVaultAction(), want false") + } +} + +func TestPasswordPeekResetsWhenChangingSelectedEntry(t *testing.T) { + t.Parallel() + + u := newUIWithModel("desktop", vault.Model{ + Entries: []vault.Entry{ + {ID: "vault-console", Title: "Vault Console", Password: "token-1", Path: []string{"Root", "Internet"}}, + {ID: "bellagio", Title: "Bellagio", Password: "token-2", Path: []string{"Root", "Internet"}}, + }, + }) + u.showEntriesSection() + u.state.NavigateToPath([]string{"Root", "Internet"}) + u.filter() + + u.state.SelectedEntryID = "vault-console" + u.loadSelectedEntryIntoEditor() + u.showPassword = true + + u.state.SelectedEntryID = "bellagio" + u.loadSelectedEntryIntoEditor() + + if u.showPassword { + t.Fatal("showPassword = true after selecting a different entry, want false") + } +} + func TestEnterOnLockedScreenDefaultsToUnlockVault(t *testing.T) { t.Parallel() diff --git a/ui_editor.go b/ui_editor.go index 2a1ba6f..f5cbab8 100644 --- a/ui_editor.go +++ b/ui_editor.go @@ -40,6 +40,7 @@ func (u *ui) attachmentInput() (string, []byte, error) { } func (u *ui) loadSelectedEntryIntoEditor() { + u.resetPasswordPeek() u.selectedHistoryIndex = -1 u.historyIndex.SetText("") diff --git a/ui_forms.go b/ui_forms.go index 6352b95..00d144e 100644 --- a/ui_forms.go +++ b/ui_forms.go @@ -26,7 +26,9 @@ func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions { ) }), layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout), - layout.Rigid(labeledEditorHelp(u.theme, "Master Password", "Leave blank if this vault is protected by key file only.", &u.masterPassword, true)), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return u.masterPasswordField(gtx, "Leave blank if this vault is protected by key file only.") + }), layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout), layout.Rigid(selectorEditorHelp(u.theme, "Key File", "Optional path to a KeePass-compatible key file.", &u.keyFilePath, &u.pickKeyFile, "Choose File", false)), layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout), @@ -490,7 +492,9 @@ func selectorEditorHelp(th *material.Theme, label, help string, editor *widget.E func (u *ui) unlockPanel(gtx layout.Context) layout.Dimensions { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, - layout.Rigid(u.unlockPasswordField), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return u.masterPasswordField(gtx, "Used alone or together with a key file to unlock the vault.") + }), layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout), layout.Rigid(selectorEditorHelp(u.theme, "Key File", "Optional path to a KeePass-compatible key file.", &u.keyFilePath, &u.pickKeyFile, "Choose File", false)), layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout), @@ -500,7 +504,7 @@ func (u *ui) unlockPanel(gtx layout.Context) layout.Dimensions { ) } -func (u *ui) unlockPasswordField(gtx layout.Context) layout.Dimensions { +func (u *ui) masterPasswordField(gtx layout.Context, help string) layout.Dimensions { icon := u.eyeIcon desc := "Show master password" mask := rune('•') @@ -542,7 +546,7 @@ func (u *ui) unlockPasswordField(gtx layout.Context) layout.Dimensions { }), layout.Rigid(layout.Spacer{Height: unit.Dp(2)}.Layout), layout.Rigid(func(gtx layout.Context) layout.Dimensions { - lbl := material.Label(u.theme, unit.Sp(11), "Used alone or together with a key file to unlock the vault.") + lbl := material.Label(u.theme, unit.Sp(11), help) lbl.Color = mutedColor return lbl.Layout(gtx) }),