mirror of
https://github.com/openai/codex.git
synced 2026-11-14 21:40:39 +00:00
Release MCP OAuth read locks before keyring access (#61401)
## Why Encrypted MCP OAuth reads hold a shared file lock while accessing the keyring, blocking writers until the keyring lookup and decryption finish. ## What changed - Add `LocalSecretsSnapshot` to capture ciphertext under the file lock, then release shared read locks before keyring access and decryption. Keep exclusive locks across conditional credential updates. - Reuse the loaded passphrase when updating existing secrets, avoiding a second keyring lookup. Return an error when an existing encrypted file has no keyring passphrase instead of generating a replacement. - Update login storage error fixtures to use directory reads that fail consistently across platforms. ## Testing Add regression coverage for releasing read locks during keyring access, updating secrets with one keyring lookup, rejecting missing passphrases on cached reads, and reading captured snapshots after later writes. GitOrigin-RevId: add8f60dba52f71c8f8bc10ae8b8ae2f3379ccac
This commit is contained in:
@@ -11,8 +11,9 @@ use tracing_subscriber::EnvFilter;
|
||||
#[test]
|
||||
fn login_default_filter_records_sanitized_storage_fallback() -> anyhow::Result<()> {
|
||||
let home = tempdir()?;
|
||||
// Fail before accessing the real keyring, then allow the plaintext fallback to succeed.
|
||||
std::fs::write(home.path().join("secrets"), "not a directory")?;
|
||||
let secrets_path = home.path().join("secrets").join("codex_auth.age");
|
||||
// Reading a directory fails before accessing the real keyring on every platform.
|
||||
std::fs::create_dir_all(&secrets_path)?;
|
||||
let log_path = home.path().join("login.log");
|
||||
let subscriber = tracing_subscriber::fmt()
|
||||
.without_time()
|
||||
@@ -36,7 +37,7 @@ fn login_default_filter_records_sanitized_storage_fallback() -> anyhow::Result<(
|
||||
failure.contains("failed to save auth to keyring, falling back to file storage"),
|
||||
"{logs}"
|
||||
);
|
||||
assert!(failure.contains("failed to create secrets dir"), "{logs}");
|
||||
assert!(failure.contains("failed to read secrets file"), "{logs}");
|
||||
assert!(
|
||||
logs.contains("credential storage file fallback completed"),
|
||||
"{logs}"
|
||||
@@ -46,15 +47,19 @@ fn login_default_filter_records_sanitized_storage_fallback() -> anyhow::Result<(
|
||||
assert!(!logs.contains("private-api-key"));
|
||||
let completion = logs.lines().last().unwrap();
|
||||
assert!(!completion.contains(home.path().to_str().unwrap()));
|
||||
let cause = std::fs::create_dir_all(home.path().join("secrets"))
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
let cause = std::fs::read(&secrets_path).unwrap_err().to_string();
|
||||
// Windows reports access denied for directory reads; Unix reports a directory error.
|
||||
insta::assert_snapshot!(
|
||||
logs.replace(
|
||||
home.path().join("secrets").to_str().unwrap(),
|
||||
"[SECRETS_PATH]"
|
||||
)
|
||||
.replace(&cause, "[OS_ERROR]")
|
||||
logs.replace(secrets_path.to_str().unwrap(), "[SECRETS_PATH]")
|
||||
.replace(&cause, "[OS_ERROR]")
|
||||
.replace(
|
||||
"secure_error=\"access_denied\"",
|
||||
"secure_error=\"[OS_ERROR_CATEGORY]\""
|
||||
)
|
||||
.replace(
|
||||
"secure_error=\"other\"",
|
||||
"secure_error=\"[OS_ERROR_CATEGORY]\""
|
||||
)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -62,11 +67,9 @@ fn login_default_filter_records_sanitized_storage_fallback() -> anyhow::Result<(
|
||||
#[test]
|
||||
fn login_storage_error_displays_context_and_cause() -> anyhow::Result<()> {
|
||||
let home = tempdir()?;
|
||||
let secrets_path = home.path().join("secrets");
|
||||
std::fs::write(&secrets_path, "not a directory")?;
|
||||
let cause = std::fs::create_dir_all(&secrets_path)
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
let secrets_path = home.path().join("secrets").join("codex_auth.age");
|
||||
std::fs::create_dir_all(&secrets_path)?;
|
||||
let cause = std::fs::read(&secrets_path).unwrap_err().to_string();
|
||||
let error = login_with_api_key(
|
||||
home.path(),
|
||||
"private-api-key",
|
||||
|
||||
+3
-4
@@ -1,7 +1,6 @@
|
||||
---
|
||||
source: cli/src/login_storage_tests.rs
|
||||
assertion_line: 47
|
||||
expression: "logs.replace(home.path().join(\"secrets\").to_str().unwrap(),\n\"[SECRETS_PATH]\").replace(&cause, \"[OS_ERROR]\")"
|
||||
expression: "logs.replace(secrets_path.to_str().unwrap(),\n\"[SECRETS_PATH]\").replace(&cause,\n\"[OS_ERROR]\").replace(\"secure_error=\\\"access_denied\\\"\",\n\"secure_error=\\\"[OS_ERROR_CATEGORY]\\\"\").replace(\"secure_error=\\\"other\\\"\",\n\"secure_error=\\\"[OS_ERROR_CATEGORY]\\\"\")"
|
||||
---
|
||||
WARN codex_login::auth::storage: failed to save auth to keyring, falling back to file storage: failed to write OAuth tokens to encrypted auth storage: failed to create secrets dir [SECRETS_PATH]: [OS_ERROR]
|
||||
WARN codex_otel::auth_storage: credential storage file fallback completed credential_kind="codex" store_mode="auto" selected_store="secrets" actual_store="file" operation="save" secure_outcome="error" outcome="success" fallback_reason="secure_error" secure_error="other" storage_phase="policy" originator="none"
|
||||
WARN codex_login::auth::storage: failed to save auth to keyring, falling back to file storage: failed to write OAuth tokens to encrypted auth storage: failed to read secrets file at [SECRETS_PATH]: [OS_ERROR]
|
||||
WARN codex_otel::auth_storage: credential storage file fallback completed credential_kind="codex" store_mode="auto" selected_store="secrets" actual_store="file" operation="save" secure_outcome="error" outcome="success" fallback_reason="secure_error" secure_error="[OS_ERROR_CATEGORY]" storage_phase="policy" originator="none"
|
||||
|
||||
+1
-2
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: cli/src/login_storage_tests.rs
|
||||
assertion_line: 58
|
||||
expression: "message.replace(secrets_path.to_str().unwrap(),\n\"[SECRETS_PATH]\").replace(&cause, \"[OS_ERROR]\")"
|
||||
---
|
||||
failed to write OAuth tokens to encrypted auth storage: failed to create secrets dir [SECRETS_PATH]: [OS_ERROR]
|
||||
failed to write OAuth tokens to encrypted auth storage: failed to read secrets file at [SECRETS_PATH]: [OS_ERROR]
|
||||
|
||||
Reference in New Issue
Block a user