Reuse native macOS Git credentials
Dependency security audit / rustsec (push) Has been cancelled

This commit is contained in:
hugo committed 2026-08-26 18:53:55 +02:00
1 parent 59cd8951f6
commit b93ae852e0
10 files changed
+327 -36

No files matched your search

@@ -20,6 +20,15 @@ use security_framework::{
},
};
#[cfg(target_os = "macos")]
use security_framework::{
item::{ItemClass, ItemSearchOptions, Limit},
os::macos::{
keychain::SecKeychain,
passwords::{SecAuthenticationType, SecProtocolType},
},
};
use keyring_core::{CredentialStore, Entry};
use super::{SecretLocator, SecretProtection, SecretStoreBackend, SecretStoreError};
@@ -196,6 +205,95 @@ impl SecretStoreBackend for NativeSecretBackend {
.delete_credential()
.map_err(map_error)
}
fn platform_https_git_credential(
&self,
endpoint: &url::Url,
) -> Result<Option<(String, SecretBytes)>, SecretStoreError> {
#[cfg(target_os = "macos")]
{
macos_https_git_credential(endpoint)
}
#[cfg(not(target_os = "macos"))]
{
let _ = endpoint;
Ok(None)
}
}
}
#[cfg(target_os = "macos")]
fn macos_https_git_credential(
endpoint: &url::Url,
) -> Result<Option<(String, SecretBytes)>, SecretStoreError> {
let host = endpoint
.host_str()
.ok_or(SecretStoreError::InvalidReference)?;
let path = endpoint.path();
let mut search = ItemSearchOptions::new();
search
.class(ItemClass::internet_password())
.load_attributes(true)
.limit(Limit::All);
let mut candidates = search
.search()
.map_err(map_security_error)?
.into_iter()
.filter_map(|item| item.simplify_dict())
.filter_map(|attributes| {
let stored_path = attributes.get("path").map_or("", String::as_str);
if attributes.get("srvr").map(String::as_str) == Some(host)
&& attributes.get("ptcl").map(String::as_str) == Some("htps")
&& (stored_path.is_empty() || stored_path == path)
{
attributes
.get("acct")
.map(|account| (account.clone(), stored_path.to_owned()))
} else {
None
}
})
.collect::<Vec<_>>();
candidates.sort_unstable();
candidates.dedup();
let [(account, credential_path)] = candidates.as_slice() else {
return if candidates.is_empty() {
Err(SecretStoreError::Missing)
} else {
Err(SecretStoreError::Corrupted)
};
};
let keychain = SecKeychain::default().map_err(map_security_error)?;
let (password, _) = keychain
.find_internet_password(
host,
None,
account,
credential_path,
endpoint.port(),
SecProtocolType::HTTPS,
SecAuthenticationType::Default,
)
.or_else(|error| {
if endpoint.port().is_some() && error.code() == -25300 {
keychain.find_internet_password(
host,
None,
account,
credential_path,
None,
SecProtocolType::HTTPS,
SecAuthenticationType::Default,
)
} else {
Err(error)
}
})
.map_err(map_security_error)?;
Ok(Some((
account.clone(),
SecretBytes::new(password.as_ref().to_vec()),
)))
}
#[cfg(any(target_os = "ios", target_os = "macos"))]