fix(secrets): keep the unit tests off the OS keyring

Asking for real keyring backends made the four round-trip tests write a genuine
login-Keychain item, and macOS binds an item's ACL to the exact binary that
created it. So every run after the first, with any source edit in between, was
a new binary against an existing item, and the OS blocked all four tests on an
authorization dialog that a headless run can never answer. Reproduced: four
tests hanging past 60s, where a fresh keychain passes in 0.39s.

The comment claiming the sandboxed tempdir guaranteed a fresh state was the
thing that hid it. try_open_keyring ignores storage_dir entirely and looks the
entry up by service name alone, so the tempdir bought nothing.

Backend::kdf_only pins them to the path they were always documented as taking.
What they assert, wrap/unwrap round-trips and tamper detection, is identical on
either backend, so nothing is lost. survives_a_restart.rs still exercises the
real backend, which is where it belongs: persistence across processes is what
that test is about.

Also names Linux's backend here instead of inheriting it. qbz-credentials
enables async-secret-service and Cargo unifies features, which holds for every
shipping build but makes this crate's correctness depend on a sibling: built on
its own, cargo tree showed keyring with no secret-service, i.e. the same silent
mock this PR exists to remove. Verified: secret-service v4.0.0 on Linux,
security-framework on macOS, and still zero keyring on iOS and Android.
This commit is contained in:
Afonso Jorge Ramos committed 2026-08-12 03:21:26 +02:00
1 parent 99ba4aa48e
commit 7814d4d2d5
3 files changed
+51 -14

No files matched your search

+18 -6
View File
@@ -43,10 +43,22 @@ tempfile = "3"
# the next. `Backend::new` still reports `BackendKind::Keyring`, so the logs
# say nothing is wrong.
#
# Linux is absent from this list because it is already covered: qbz-credentials
# enables `async-secret-service` and Cargo unifies features across the graph.
# Naming `sync-secret-service` here would be a build failure rather than belt
# and braces — keyring's lib.rs has a `compile_error!` for enabling both the
# sync and async halves of a store at once.
# Linux names its backend here too, rather than relying on qbz-credentials
# enabling `async-secret-service` and Cargo unifying features across the graph.
# That does hold for any build containing qbz-credentials, which today is every
# shipping one, but it makes this crate's correctness depend on a sibling: build
# qbz-secrets on its own and `cargo tree` shows keyring with no secret-service,
# which is the silent mock described above. A crate meant to be reusable should
# not be one dependency-graph change away from storing secrets in a hashmap.
#
# `async-secret-service` and not `sync-secret-service`: keyring's lib.rs has a
# `compile_error!` for enabling both halves of a store, and qbz-credentials
# already picked the async one. `crypto-rust` is not optional, secret-service
# does not build without a crypto backend selected.
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
keyring = { version = "3", features = ["apple-native", "windows-native"] }
keyring = { version = "3", features = [
"apple-native",
"windows-native",
"async-secret-service",
"crypto-rust",
] }