Conversation
…rate from keytar. Update .gitignore for Rust artifacts, adjust package.json for new build scripts, and modify workflows for native module compilation. Enhance state versioning to support migration of credentials from keytar to desktop_core.
…Script configurations and package dependencies. Refactor native binding logic and enhance error handling. Remove legacy keytar references and adjust package-lock.json for new native module structure.
…removing unnecessary semicolons. Update build scripts in package.json to exclude JavaScript during native module compilation.
…ule directly in the renderer process, removing the need for the credential storage listener. Update TypeScript definitions and enhance error handling in password management functions. Adjust Cargo dependencies and versions for improved compatibility.
|
Claude finished @JaredScar's task in 5m 0s —— View job Code Review Summary - PR #1012: PM-12436 replace node keytarOverviewThis PR replaces the deprecated Findings
Additional Observations
What Looks Good
|
|
|
Great job! No new security vulnerabilities introduced in this pull request |
| isAvailable: () => nativeBinding.isAvailable(), | ||
| migrateKeytarPassword: (service, account) => | ||
| nativeBinding.migrateKeytarPassword(service, account), | ||
| PASSWORD_NOT_FOUND: "Password not found.", |
There was a problem hiding this comment.
PASSWORD_NOT_FOUND is hardcoded as "Password not found." in this JavaScript wrapper, but the actual error message originates from desktop_core::password functions, propagated through napi::Error::from_reason(e.to_string()) in lib.rs. If desktop_core returns a different error string (e.g., "password not found", "No password found", or a platform-specific message), the comparison e.message === passwords.PASSWORD_NOT_FOUND in both NativeSecureStorageService and ElectronRendererSecureStorageService will never match.
The consequence is that get() will throw an error instead of returning null for missing keys, and remove() will throw instead of silently succeeding. This would break credential lookups for any key not yet stored and could cause startup failures.
Consider one of:
-
Export the constant from the Rust side so the string is defined in one place:
#[napi] pub const PASSWORD_NOT_FOUND: &str = "Password not found.";
Then reference it in
index.jsvianativeBinding.PASSWORD_NOT_FOUND. -
Use a structured error approach (error codes instead of message string comparison) so the contract is not dependent on exact text matching.
| const credentialKeys = [ | ||
| SecureStorageKeys.ldap, | ||
| SecureStorageKeys.gsuite, | ||
| SecureStorageKeys.azure, | ||
| SecureStorageKeys.entra, | ||
| SecureStorageKeys.okta, | ||
| SecureStorageKeys.oneLogin, | ||
| ]; |
There was a problem hiding this comment.
migrateStateFrom1To2 method (lines 168-178) stores all SecureStorageKeys values in secure storage with a ${userId}_ prefix. This means the following keys may also exist in the Windows credential store in the old keytar UTF-8 format and would not be migrated:
${userId}_directoryConfig_(fromSecureStorageKeys.directoryConfigPrefix)${userId}_syncConfig(fromSecureStorageKeys.sync)${userId}_directoryType(fromSecureStorageKeys.directoryType)${userId}_organizationId(fromSecureStorageKeys.organizationId)
Reading these unmigrated credentials through desktop_core on Windows would produce garbled UTF-16 output, the same corruption this migration is designed to prevent.
| const credentialKeys = [ | |
| SecureStorageKeys.ldap, | |
| SecureStorageKeys.gsuite, | |
| SecureStorageKeys.azure, | |
| SecureStorageKeys.entra, | |
| SecureStorageKeys.okta, | |
| SecureStorageKeys.oneLogin, | |
| ]; | |
| const credentialKeys = [ | |
| SecureStorageKeys.ldap, | |
| SecureStorageKeys.gsuite, | |
| SecureStorageKeys.azure, | |
| SecureStorageKeys.entra, | |
| SecureStorageKeys.okta, | |
| SecureStorageKeys.oneLogin, | |
| SecureStorageKeys.directoryConfigPrefix, | |
| SecureStorageKeys.sync, | |
| SecureStorageKeys.directoryType, | |
| SecureStorageKeys.organizationId, | |
| ]; |
|
|
||
| import { passwords } from "dc-native"; | ||
|
|
||
| const APPLICATION_NAME = "Bitwarden Directory Connector"; |
There was a problem hiding this comment.
🎨 SUGGESTED: This service name is now hardcoded in three separate locations:
- Here in
ElectronRendererSecureStorageService - In
stateMigration.service.tsline 190 (const serviceName = "Bitwarden Directory Connector") - In
bwdc.tsline 63 (const applicationName = "Bitwarden Directory Connector")
If any of these drift out of sync, credentials written by one path would be invisible to another. Consider extracting this to a shared constant (e.g., in a constants file) to make the coupling explicit and prevent future inconsistencies.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1012 +/- ##
==========================================
- Coverage 15.11% 15.08% -0.04%
==========================================
Files 67 67
Lines 2798 2804 +6
Branches 483 480 -3
==========================================
Hits 423 423
- Misses 2271 2277 +6
Partials 104 104 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|





🎟️ Tracking
https://bitwarden.atlassian.net/browse/PM-12436
📔 Objective
Rid of node keytar from Directory Connector