package main import ( "errors" "os" "path/filepath" "strconv" "strings" "testing" ) const nobody = 4194400 // secretService fakes busctl answering as gnome-keyring did on 2026-10-04: a session, a login and a // default keyring, the login one unlocked; Lock locks it. func secretService(t *testing.T) string { t.Helper() fakeMachine(t) runtime := filepath.Join(runUserDir, strconv.Itoa(os.Getuid())) if err := os.MkdirAll(runtime, 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(runtime, "bus"), nil, 0o600); err != nil { t.Fatal(err) } bin := fakeBinaries(t, map[string]string{"busctl": `echo "$*" >> "$LOG" locked=false; [ -f "$LOG.locked" ] && locked=true case "$*" in *"get-property org.freedesktop.secrets /org/freedesktop/secrets org.freedesktop.Secret.Service Collections") echo '{"type":"ao","data":["/org/freedesktop/secrets/collection/session","/org/freedesktop/secrets/collection/login","/org/freedesktop/secrets/collection/Default_5fkeyring"]}' ;; *"ReadAlias s default") echo '{"type":"o","data":["/org/freedesktop/secrets/collection/Default_5fkeyring"]}' ;; *"/collection/login org.freedesktop.DBus.Properties GetAll"*) echo '{"type":"a{sv}","data":[{"Items":{"type":"ao","data":["/x/1","/x/2"]},"Label":{"type":"s","data":"Login"},"Locked":{"type":"b","data":'$locked'},"Created":{"type":"t","data":1752488320},"Modified":{"type":"t","data":0}}]}' ;; *"/collection/"*"GetAll"*) echo '{"type":"a{sv}","data":[{"Items":{"type":"ao","data":[]},"Label":{"type":"s","data":"Other"},"Locked":{"type":"b","data":true},"Created":{"type":"t","data":0},"Modified":{"type":"t","data":0}}]}' ;; *"Lock ao 1 /org/freedesktop/secrets/collection/login") touch "$LOG.locked"; echo '{"type":"aoo","data":[["/org/freedesktop/secrets/collection/login"],"/"]}' ;; *) echo "no such call: $*" >&2; exit 1 ;; esac`}) t.Setenv("LOG", filepath.Join(bin, "log")) return bin } func TestCollectionsAreNamesCountsAndLocksNeverItems(t *testing.T) { bin := secretService(t) got, err := Collections() if err != nil { t.Fatal(err) } if len(got.Collections) != 3 { t.Fatalf("%+v", got) } var login, def Collection for _, c := range got.Collections { switch c.ID { case "login": login = c case "Default_5fkeyring": def = c } } if login.Label != "Login" || login.Locked || login.Items != 2 || login.Created == "" || login.Modified != "" || login.Default { t.Fatalf("login: %+v", login) } if !def.Default || !def.Locked { t.Fatalf("default: %+v", def) } asked, _ := os.ReadFile(filepath.Join(bin, "log")) if strings.Contains(string(asked), "GetSecret") || strings.Contains(string(asked), "Item") && strings.Contains(string(asked), "Secret.Item") { t.Fatalf("a secret was asked for:\n%s", asked) } } func TestUnlockedAnswersTheLoginAndTheDefaultKeyring(t *testing.T) { secretService(t) fakeProcess(t, nobody, "gnome-keyring-d") got, err := Unlocked() if err != nil || !got.Daemon || got.Login == nil || got.Login.Locked || got.Default == nil || !got.Default.Default { t.Fatalf("%+v, %v", got, err) } } func TestLockLocksTheLoginKeyringAndRefusesAPathForAnId(t *testing.T) { secretService(t) got, err := Lock("") if err != nil || got.Collection != "login" || !got.Locked { t.Fatalf("%+v, %v", got, err) } for _, bad := range []string{"../service", "login /org/x", "a b"} { if _, err := Lock(bad); err == nil { t.Errorf("%q was accepted", bad) } } } func TestTheAgentsKeysAreFingerprints(t *testing.T) { keys := parseKeys("256 SHA256:x+LmFabc op@laptop (ED25519)\n3072 SHA256:yyy a comment with spaces (RSA)\nThe agent has no identities.\n") if len(keys) != 2 || keys[0] != (Key{Bits: 256, Fingerprint: "SHA256:x+LmFabc", Comment: "op@laptop", Type: "ED25519"}) || keys[1].Comment != "a comment with spaces" || keys[1].Type != "RSA" { t.Fatalf("%+v", keys) } } func TestSSHKeysAsksGcrsAgentAndSaysWhenItDoesNotAnswer(t *testing.T) { secretService(t) bin := fakeBinaries(t, map[string]string{"ssh-add": `echo "$SSH_AUTH_SOCK $*" > "$LOG.ssh"; [ -f "$NOAGENT" ] && { echo "Could not open a connection" >&2; exit 2; }; echo "256 SHA256:abc op (ED25519)"`}) t.Setenv("NOAGENT", filepath.Join(bin, "noagent")) got, err := SSHKeys() if err != nil || len(got.Keys) != 1 || !strings.HasSuffix(got.Agent, "/gcr/ssh") { t.Fatalf("%+v, %v", got, err) } asked, _ := os.ReadFile(os.Getenv("LOG") + ".ssh") if !strings.HasSuffix(strings.TrimSpace(string(asked)), "/gcr/ssh -l -E sha256") { t.Fatalf("asked: %s", asked) } if err := os.WriteFile(filepath.Join(bin, "noagent"), nil, 0o644); err != nil { t.Fatal(err) } if _, err := SSHKeys(); err == nil || !strings.Contains(err.Error(), "gcr-ssh-agent.socket") { t.Fatalf("no agent: %v", err) } } func TestWithoutABusTheToolsSaySo(t *testing.T) { fakeMachine(t) if _, err := Collections(); !errors.Is(err, ErrNoBus) { t.Fatal(err) } if _, err := SSHKeys(); !errors.Is(err, ErrNoBus) { t.Fatal(err) } }