Redact auth tokens from app-server response logs (#39141)

## Why

App-server response diagnostics can include access tokens returned by the ChatGPT auth refresh flow.

## What changed

- Log successfully parsed server responses instead of raw JSON-RPC response payloads.
- Redact `access_token` from the `Debug` representation of `ChatgptAuthTokensRefreshResponse`.
- Avoid formatting callback send errors that can retain the original response payload.

GitOrigin-RevId: fd47485b38ed89527b25937c759b3273581f6ae6
This commit is contained in:
Adam Perry @ OpenAI
2026-08-18 05:15:44 +00:00
committed by copyberry
parent 5ee6baee2f
commit 9a254ba1fa
3 changed files with 20 additions and 9 deletions

View File

@@ -13,6 +13,7 @@ use codex_protocol::protocol::SpendControlLimitSnapshot as CoreSpendControlLimit
use serde::Deserialize;
use serde::Serialize;
use std::collections::HashMap;
use std::fmt;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(tag = "type", rename_all = "camelCase")]
@@ -277,7 +278,7 @@ pub struct ChatgptAuthTokensRefreshParams {
pub previous_account_id: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ChatgptAuthTokensRefreshResponse {
@@ -286,6 +287,16 @@ pub struct ChatgptAuthTokensRefreshResponse {
pub chatgpt_plan_type: Option<String>,
}
impl fmt::Debug for ChatgptAuthTokensRefreshResponse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ChatgptAuthTokensRefreshResponse")
.field("access_token", &"<redacted>")
.field("chatgpt_account_id", &self.chatgpt_account_id)
.field("chatgpt_plan_type", &self.chatgpt_plan_type)
.finish()
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]

View File

@@ -815,7 +815,6 @@ impl MessageProcessor {
/// Handle a standalone JSON-RPC response originating from the peer.
pub(crate) async fn process_response(&self, response: JSONRPCResponse) {
tracing::info!("<- response: {:?}", response);
let JSONRPCResponse { id, result, .. } = response;
self.outgoing.notify_client_response(id, result).await
}

View File

@@ -386,14 +386,15 @@ impl OutgoingMessageSender {
match entry {
Some((id, entry)) => {
let completed_at_ms = now_unix_timestamp_ms();
if let Ok(response) = entry.request.response_from_result(result.clone())
&& !matches!(response, ServerResponse::PermissionsRequestApproval { .. })
{
self.analytics_events_client
.track_server_response(completed_at_ms, response);
if let Ok(response) = entry.request.response_from_result(result.clone()) {
tracing::info!("<- response: {response:?}");
if !matches!(response, ServerResponse::PermissionsRequestApproval { .. }) {
self.analytics_events_client
.track_server_response(completed_at_ms, response);
}
}
if let Err(err) = entry.callback.send(Ok(result)) {
warn!("could not notify callback for {id:?} due to: {err:?}");
if entry.callback.send(Ok(result)).is_err() {
warn!("could not notify callback for {id:?}: receiver dropped");
}
}
None => {