fix(auth): cache keyring probe and rename misleading encrypt/decrypt#106
Merged
tjb-tech merged 1 commit intoHKUDS:mainfrom Apr 11, 2026
Conversation
…helpers Two issues in auth/storage.py: 1. Every call to store_credential / load_credential / clear_provider_credentials probes the system keyring. When no backend is available (WSL, containers, CI), each probe fails and logs a warning. A single `oh setup` invocation triggers 20+ "Keyring load failed" warnings because the availability is never cached. Fix: probe once on first call, cache the result, and log at INFO level instead of WARNING so users see a single quiet message. 2. The public `encrypt` / `decrypt` functions are XOR obfuscation with a key derived from the home-directory path — trivially reversible by anyone with filesystem access. Naming them encrypt/decrypt misleads callers into thinking credentials are protected by real cryptography. Fix: rename to `_obfuscate` / `_deobfuscate` (private), update module docstring to document the security model, and keep `encrypt` / `decrypt` as deprecated aliases for backward compatibility.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
1. Keyring warning spam floods stderr
On systems without a desktop keyring backend (WSL, containers, CI, headless Linux), every single credential operation calls
_keyring_available()→ importskeyring→ tries to use it → fails → logs a warning. A singleoh setup geminiproduces 22 identical warnings that drown out the actual output:That is the full, unedited output of a single command. Every
store_credential/load_credential/clear_provider_credentialscall independently probes the keyring, fails, and prints. This affects every user on WSL, headless Linux, containers, and CI — the most common non-macOS environments.oh auth login geminiproduces a similar wall:2.
encrypt/decryptare XOR obfuscation, not encryptionencrypt()anddecrypt()inauth.storageare XOR with a key deterministically derived fromPath.home()— trivially reversible by anyone with filesystem access. Naming themencrypt/decryptand exporting them in__all__misleads callers (and auditors) into thinking credentials have cryptographic protection. They are not called anywhere in the codebase today, but the public API surface invites misuse.Fix
Keyring caching
_keyring_available()now probes the backend once on first call, caches the result in a module-level flag, and returns the cached value on subsequent callskeyring.get_password()round-trip instead of just checking the import — this catches the common case wherekeyringis installed as a transitive dep but no backend is configuredINFO-level message instead of aWARNINGper callRename obfuscation helpers
encrypt→_obfuscate(private)decrypt→_deobfuscate(private)encrypt/decryptkept as deprecated aliases for backward compatibility__init__.pyexports annotated as deprecatedTest plan
ruff check src tests scriptspasses_obfuscate/_deobfuscateround-trip verifiedencrypt/decryptaliases still work (point to same functions)