mirror of
https://github.com/openai/codex.git
synced 2026-11-11 04:41:10 +00:00
Keep credentials out of app-server logs (#39993)
## Why App-server logs can be persisted or included in submitted diagnostics, so credentials used by model providers, authentication refreshes, and attestation requests must not appear in diagnostic output. ## What changed - Add `RedactedString`, which preserves serialization and string access while replacing debug output with `<redacted>`. - Use it for model-provider bearer tokens, header and query values, authentication command arguments, and attestation tokens. - Avoid logging JSON-RPC error payloads and parser or authentication errors that may echo credentials; retain safe context such as error codes and categories. ## Testing - Add an app-server regression test that exercises provider credentials, refreshed authentication tokens, and attestation tokens, then verifies none appear in persisted SQLite or submitted diagnostic logs. GitOrigin-RevId: 8c50408adf94d93847658b1320682cf3b637d2cc
This commit is contained in:
1 parent
45a3edc02a
commit
79b7606803
36 files changed
+446
-108
No files matched your search
@@ -7,6 +7,7 @@ use codex_model_provider_info::ModelProviderInfo;
|
||||
use codex_model_provider_info::WireApi;
|
||||
use codex_protocol::config_types::ModelProviderAuthInfo;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_redacted_string::RedactedString;
|
||||
|
||||
use super::SessionThreadConfig;
|
||||
use super::ThreadConfigContext;
|
||||
@@ -174,15 +175,15 @@ fn model_provider_from_proto(
|
||||
base_url: provider.base_url,
|
||||
env_key: provider.env_key,
|
||||
env_key_instructions: provider.env_key_instructions,
|
||||
experimental_bearer_token: provider.experimental_bearer_token,
|
||||
experimental_bearer_token: provider.experimental_bearer_token.map(Into::into),
|
||||
auth: provider
|
||||
.auth
|
||||
.map(model_provider_auth_from_proto)
|
||||
.transpose()?,
|
||||
aws: None,
|
||||
wire_api,
|
||||
query_params: provider.query_params.map(|map| map.values),
|
||||
http_headers: provider.http_headers.map(|map| map.values),
|
||||
query_params: provider.query_params.map(redacted_string_map),
|
||||
http_headers: provider.http_headers.map(redacted_string_map),
|
||||
env_http_headers: provider.env_http_headers.map(|map| map.values),
|
||||
request_max_retries: provider.request_max_retries,
|
||||
stream_max_retries: provider.stream_max_retries,
|
||||
@@ -227,12 +228,12 @@ fn model_provider_to_proto(
|
||||
base_url,
|
||||
env_key,
|
||||
env_key_instructions,
|
||||
experimental_bearer_token,
|
||||
experimental_bearer_token: experimental_bearer_token.map(RedactedString::into_inner),
|
||||
auth: auth.map(model_provider_auth_to_proto),
|
||||
wire_api: proto_wire_api(wire_api).into(),
|
||||
query_params: query_params.map(proto_string_map),
|
||||
http_headers: http_headers.map(proto_string_map),
|
||||
env_http_headers: env_http_headers.map(proto_string_map),
|
||||
env_http_headers: env_http_headers.map(|values| proto::StringMap { values }),
|
||||
request_max_retries,
|
||||
stream_max_retries,
|
||||
stream_idle_timeout_ms,
|
||||
@@ -257,7 +258,7 @@ fn model_provider_auth_from_proto(
|
||||
|
||||
Ok(ModelProviderAuthInfo {
|
||||
command: auth.command,
|
||||
args: auth.args,
|
||||
args: auth.args.into_iter().map(RedactedString::from).collect(),
|
||||
timeout_ms,
|
||||
refresh_interval_ms: auth.refresh_interval_ms,
|
||||
cwd,
|
||||
@@ -276,16 +277,28 @@ fn model_provider_auth_to_proto(auth: ModelProviderAuthInfo) -> proto::ModelProv
|
||||
|
||||
proto::ModelProviderAuthInfo {
|
||||
command,
|
||||
args,
|
||||
args: args.into_iter().map(RedactedString::into_inner).collect(),
|
||||
timeout_ms: timeout_ms.get(),
|
||||
refresh_interval_ms,
|
||||
cwd: cwd.to_string_lossy().into_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn redacted_string_map(map: proto::StringMap) -> HashMap<String, RedactedString> {
|
||||
map.values
|
||||
.into_iter()
|
||||
.map(|(name, value)| (name, value.into()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn proto_string_map(values: HashMap<String, String>) -> proto::StringMap {
|
||||
proto::StringMap { values }
|
||||
fn proto_string_map(values: HashMap<String, RedactedString>) -> proto::StringMap {
|
||||
proto::StringMap {
|
||||
values: values
|
||||
.into_iter()
|
||||
.map(|(name, value)| (name, value.into_inner()))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -422,7 +435,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn model_provider_proto_roundtrips_through_domain_type() {
|
||||
let expected = expected_provider();
|
||||
let mut expected = expected_provider();
|
||||
expected.auth = None;
|
||||
expected.experimental_bearer_token = Some("synthetic-provider-token".into());
|
||||
let proto = model_provider_to_proto("local", expected.clone());
|
||||
assert!(proto.supports_standalone_web_search);
|
||||
let (id, actual) = model_provider_from_proto(proto).expect("model provider from proto");
|
||||
@@ -531,7 +546,7 @@ mod tests {
|
||||
experimental_bearer_token: None,
|
||||
auth: Some(ModelProviderAuthInfo {
|
||||
command: "token-helper".to_string(),
|
||||
args: vec!["--json".to_string()],
|
||||
args: vec!["--json".into()],
|
||||
timeout_ms: NonZeroU64::new(5_000).expect("non-zero timeout"),
|
||||
refresh_interval_ms: 300_000,
|
||||
cwd: workspace_dir(),
|
||||
@@ -539,12 +554,9 @@ mod tests {
|
||||
wire_api: WireApi::Responses,
|
||||
query_params: Some(HashMap::from([(
|
||||
"api-version".to_string(),
|
||||
"2026-04-16".to_string(),
|
||||
)])),
|
||||
http_headers: Some(HashMap::from([(
|
||||
"X-Test".to_string(),
|
||||
"enabled".to_string(),
|
||||
"2026-04-16".into(),
|
||||
)])),
|
||||
http_headers: Some(HashMap::from([("X-Test".to_string(), "enabled".into())])),
|
||||
env_http_headers: Some(HashMap::from([(
|
||||
"X-Env".to_string(),
|
||||
"LOCAL_HEADER".to_string(),
|
||||
|
||||
Reference in new issue
Block a user