fix(auth): stabilize encrypted credential key fallback#28
Conversation
🦋 Changeset detectedLatest commit: 79266e9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses a critical authentication instability where the Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request effectively addresses a race condition in the authentication flow by ensuring a stable fallback for the encrypted credential key, introducing a race-safe OnceLock cache, and prioritizing the local .encryption_key file. However, there are security concerns regarding the handling of file permissions, as the code ignores the result of set_permissions calls on sensitive directories and files, potentially leaving them exposed. Additionally, an OAuth2 access token is passed as a query parameter in the handle_status command, which is against security best practices. My review also includes suggestions to refactor duplicated code and improve the clarity of the race-handling logic, which will enhance the overall maintainability of the code.
…ion warnings - Replace unwrap_or(candidate) with expect() in cache_key closure for clearer OnceLock race invariant: if set() fails, get() is guaranteed to return Some - Emit eprintln! warnings (rather than silently ignoring) when set_permissions fails on the encryption key directory, matching the warning pattern used throughout the codebase (src/auth_commands.rs, helpers/workflows.rs, etc.)
🤖 Bot triage updateFixed in commit 79266e9Addressed Gemini review comments:
Tests: Pre-existing clippy failures on Remaining: CI only shows |
Review comments addressed ✅All Gemini code review comments from this PR have been addressed in the follow-up commit Changes made:
Quality checks:
|
|
/gemini review |
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
1 similar comment
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
jpoehnelt
left a comment
There was a problem hiding this comment.
Solid fix for the keyring race condition and the NoEntry-generates-new-key-every-invocation bug. The OnceLock cache_key closure is an elegant solution to the set-race.
A few notes:
-
File-first, keyring-second ordering — in the
NoEntrybranch, the fix now checks the local.encryption_keyfile before generating a new key, then populates the keyring as a best-effort. This is correct, but it means a machine that previously relied solely on the keyring (no file) and has the keyring entry deleted would silently fall through to generating a new key (losing the old credentials). This is the pre-existing breakage scenario your fix is designed to recover from, so it's acceptable — just worth documenting in a comment. -
#[cfg(unix)]file permissions — the0o600mode on the key file is the right call. The#[cfg(not(unix))]fallback writes without any permission restriction, which is unavoidable on Windows. Consider adding a comment noting this limitation. -
cache_keyclosure capturesKEYby reference — the closure is only ever called locally and doesn't escape, so this is safe. But sinceKEYis a module-levelOnceLock, an explicitlet _ = KEY.set(...)with theis_ok()check makes the intent clearer than the closure. The current closure approach is fine; just noting it as a readability consideration. -
Test coverage — the fix is complex enough to warrant integration-style tests for the
NoEntrypath: (a) no keyring entry, no file → generates and persists; (b) no keyring entry, file exists → reads file, repopulates keyring. These are hard to unit-test without mocking keyring, but a comment pointing to a manual test procedure would help reviewers. -
Changeset — the changeset accurately describes the fix. Marking as
patchis appropriate.
LGTM — the fix is correct and the OnceLock race is properly handled.
Summary
Fixes a reproducible auth flow where:
gws auth loginreports success and writescredentials.encNo credentials providedgws auth statusshowsencryption_valid: falseand decryption failureRoot cause: when keyring returns
NoEntry, we could generate/store a new key in keyring and return early without ensuring a stable local fallback. In environments where keyring behavior is inconsistent across runs, the next process could derive a different key and fail decryption.What changed
credential_store::get_or_create_key():key_fileup front and prefer it when keyring hasNoEntryNoEntry, attempt to load existing.encryption_keyfirst.encryption_key(stable fallback)OnceLock::setloses a raceValidation
cargo test -q credential_storepasses (11/11)Related