Add autofill privacy settings controls

This commit is contained in:
Joe Julian
2026-04-01 17:58:37 -07:00
parent 07c277f425
commit 2e03db85a1
3 changed files with 368 additions and 2 deletions
+190
View File
@@ -2,6 +2,7 @@ package main
import (
"bytes"
"encoding/json"
"errors"
"image"
"io"
@@ -3558,6 +3559,118 @@ func TestUIDenseLayoutPreferencePersists(t *testing.T) {
}
}
func TestUISyncDefaultsPersistInSettings(t *testing.T) {
t.Parallel()
configPath := filepath.Join(t.TempDir(), "settings.json")
first := newUIWithSession("desktop", &session.Manager{})
first.settingsPath = configPath
first.syncDefaultSourceMode = syncSourceRemote
first.syncDefaultDirection = syncDirectionPush
first.saveSettings()
second := newUIWithSession("desktop", &session.Manager{})
second.settingsPath = configPath
second.syncDefaultSourceMode = syncSourceLocal
second.syncDefaultDirection = syncDirectionPull
second.loadSettings()
if got := second.syncDefaultSourceMode; got != syncSourceRemote {
t.Fatalf("syncDefaultSourceMode = %q, want remote", got)
}
if got := second.syncDefaultDirection; got != syncDirectionPush {
t.Fatalf("syncDefaultDirection = %q, want push", got)
}
}
func TestUILoadSettingsFallsBackToLegacySyncDefaultsInUIPreferences(t *testing.T) {
t.Parallel()
dir := t.TempDir()
legacyPath := filepath.Join(dir, "ui-prefs.json")
content, err := json.MarshalIndent(uiPreferences{
SyncSourceDefault: string(syncSourceRemote),
SyncDirectionDefault: string(syncDirectionPush),
}, "", " ")
if err != nil {
t.Fatalf("json.MarshalIndent() error = %v", err)
}
if err := os.WriteFile(legacyPath, content, 0o600); err != nil {
t.Fatalf("os.WriteFile() error = %v", err)
}
reloaded := newUIWithSession("desktop", &session.Manager{})
reloaded.uiPreferencesPath = legacyPath
reloaded.settingsPath = filepath.Join(dir, "settings.json")
reloaded.syncDefaultSourceMode = syncSourceLocal
reloaded.syncDefaultDirection = syncDirectionPull
reloaded.loadSettings()
if got := reloaded.syncDefaultSourceMode; got != syncSourceRemote {
t.Fatalf("syncDefaultSourceMode = %q after legacy load, want remote", got)
}
if got := reloaded.syncDefaultDirection; got != syncDirectionPush {
t.Fatalf("syncDefaultDirection = %q after legacy load, want push", got)
}
}
func TestUIOpenAdvancedSyncDialogUsesSavedSyncDefaults(t *testing.T) {
t.Parallel()
u := newUIWithSession("desktop", &session.Manager{})
u.syncDefaultSourceMode = syncSourceRemote
u.syncDefaultDirection = syncDirectionPush
u.syncSourceMode = syncSourceLocal
u.syncDirection = syncDirectionPull
u.vaultPath.SetText("/vaults/current.kdbx")
u.openAdvancedSyncDialog()
if got := u.syncSourceMode; got != syncSourceRemote {
t.Fatalf("syncSourceMode = %q after open, want remote default", got)
}
if got := u.syncDirection; got != syncDirectionPush {
t.Fatalf("syncDirection = %q after open, want push default", got)
}
if got := u.syncLocalPath.Text(); got != "/vaults/current.kdbx" {
t.Fatalf("syncLocalPath = %q after open, want current vault path", got)
}
}
func TestUISaveSecuritySettingsPersistsSyncDefaults(t *testing.T) {
t.Parallel()
manager := &session.Manager{}
u := newUIWithSession("desktop", manager)
u.masterPassword.SetText("correct horse battery staple")
if err := u.createVaultAction(); err != nil {
t.Fatalf("createVaultAction() error = %v", err)
}
u.securityCipher.SetText(vault.CipherAES256)
u.securityKDF.SetText(vault.KDFAES)
u.loadSettingsDraft()
u.settingsDraft.Sync.SourceDefault = syncSourceRemote
u.settingsDraft.Sync.DirectionDefault = syncDirectionPush
u.settingsPath = filepath.Join(t.TempDir(), "settings.json")
u.uiPreferencesPath = filepath.Join(t.TempDir(), "ui-prefs.json")
if err := u.saveSecuritySettingsAction(); err != nil {
t.Fatalf("saveSecuritySettingsAction() error = %v", err)
}
reloaded := newUIWithSession("desktop", &session.Manager{})
reloaded.settingsPath = u.settingsPath
reloaded.loadSettings()
if got := reloaded.syncDefaultSourceMode; got != syncSourceRemote {
t.Fatalf("reloaded syncDefaultSourceMode = %q, want remote", got)
}
if got := reloaded.syncDefaultDirection; got != syncDirectionPush {
t.Fatalf("reloaded syncDefaultDirection = %q, want push", got)
}
}
func TestUIAccessibilityPreferencesPersist(t *testing.T) {
t.Parallel()
@@ -3657,6 +3770,83 @@ func TestUINotificationPreferencesPersist(t *testing.T) {
}
}
func TestAutofillPrivacyLinesNormalizesEntries(t *testing.T) {
t.Parallel()
got := autofillPrivacyLines(" com.android.chrome \n\ncom.example.app\ncom.android.chrome\n org.keepassgo.browser ")
want := []string{"com.android.chrome", "com.example.app", "org.keepassgo.browser"}
if !slices.Equal(got, want) {
t.Fatalf("autofillPrivacyLines() = %v, want %v", got, want)
}
}
func TestJoinAutofillPrivacyLines(t *testing.T) {
t.Parallel()
got := joinAutofillPrivacyLines([]string{"com.android.chrome", "com.example.app"})
if got != "com.android.chrome\ncom.example.app" {
t.Fatalf("joinAutofillPrivacyLines() = %q, want %q", got, "com.android.chrome\ncom.example.app")
}
}
func TestUIAutofillPrivacyPreferencesPersist(t *testing.T) {
t.Parallel()
configPath := filepath.Join(t.TempDir(), "ui-prefs.json")
first := newUIWithSession("desktop", &session.Manager{})
first.uiPreferencesPath = configPath
first.autofillFirstFillApprovalMode = autofillFirstFillApprovalBlock
first.autofillBrowserAllowlist.SetText("https://accounts.example.com\nhttps://login.example.org\nhttps://accounts.example.com")
first.autofillAppAllowlist.SetText("org.mozilla.firefox\ncom.android.chrome")
first.autofillPackageRules.SetText("com.android.chrome=hostname\norg.keepassgo.browser=view-id")
first.saveUIPreferences()
second := newUIWithSession("desktop", &session.Manager{})
second.uiPreferencesPath = configPath
second.autofillFirstFillApprovalMode = autofillFirstFillApprovalAsk
second.loadUIPreferences()
if got := second.autofillFirstFillApprovalMode; got != autofillFirstFillApprovalBlock {
t.Fatalf("autofillFirstFillApprovalMode = %q, want %q", got, autofillFirstFillApprovalBlock)
}
if got := second.autofillBrowserAllowlist.Text(); got != "https://accounts.example.com\nhttps://login.example.org" {
t.Fatalf("autofillBrowserAllowlist = %q, want normalized browser allowlist", got)
}
if got := second.autofillAppAllowlist.Text(); got != "org.mozilla.firefox\ncom.android.chrome" {
t.Fatalf("autofillAppAllowlist = %q, want preserved allowlist entries", got)
}
if got := second.autofillPackageRules.Text(); got != "com.android.chrome=hostname\norg.keepassgo.browser=view-id" {
t.Fatalf("autofillPackageRules = %q, want persisted package rules", got)
}
}
func TestUILoadUIPreferencesKeepsDefaultAutofillApprovalWhenMissing(t *testing.T) {
t.Parallel()
configPath := filepath.Join(t.TempDir(), "ui-prefs.json")
content, err := json.Marshal(uiPreferences{
GroupControlsHidden: true,
LifecycleAdvancedHidden: true,
HistoryHidden: true,
})
if err != nil {
t.Fatalf("Marshal(uiPreferences) error = %v", err)
}
if err := os.WriteFile(configPath, content, 0o600); err != nil {
t.Fatalf("WriteFile(uiPreferences) error = %v", err)
}
u := newUIWithSession("desktop", &session.Manager{})
u.uiPreferencesPath = configPath
u.autofillFirstFillApprovalMode = autofillFirstFillApprovalAsk
u.loadUIPreferences()
if got := u.autofillFirstFillApprovalMode; got != autofillFirstFillApprovalAsk {
t.Fatalf("autofillFirstFillApprovalMode = %q, want %q when preference missing", got, autofillFirstFillApprovalAsk)
}
}
func TestSelectingRecentRemoteConnectionKeepsPasswordMasked(t *testing.T) {
t.Parallel()