diff --git a/agent/a2aagent/a2a_agent.go b/agent/a2aagent/a2a_agent.go index ac72ef5926..5589e236c0 100644 --- a/agent/a2aagent/a2a_agent.go +++ b/agent/a2aagent/a2a_agent.go @@ -33,6 +33,7 @@ import ( itrace "trpc.group/trpc-go/trpc-agent-go/internal/trace" "trpc.group/trpc-go/trpc-agent-go/log" "trpc.group/trpc-go/trpc-agent-go/model" + "trpc.group/trpc-go/trpc-agent-go/platform" semconvtrace "trpc.group/trpc-go/trpc-agent-go/telemetry/semconv/trace" "trpc.group/trpc-go/trpc-agent-go/tool" ) @@ -155,6 +156,7 @@ func (r *A2AAgent) sendErrorEvent( err error, ) *model.ResponseError { respErr := model.ResponseErrorFromError(err, model.ErrorTypeRunError) + redactAgentResponseError(respErr) agent.EmitEvent(ctx, invocation, eventChan, event.New( invocation.InvocationID, r.name, @@ -166,6 +168,17 @@ func (r *A2AAgent) sendErrorEvent( return respErr } +func redactAgentResponseError(respErr *model.ResponseError) { + if respErr == nil { + return + } + redactor, err := platform.NewRedactor() + if err != nil { + return + } + respErr.Message = redactor.Redact(respErr.Message) +} + // validateA2ARequestOptions validates that all A2A request options are of the correct type func (r *A2AAgent) validateA2ARequestOptions(invocation *agent.Invocation) error { if invocation.RunOptions.A2ARequestOptions == nil { diff --git a/agent/a2aagent/a2a_agent_test.go b/agent/a2aagent/a2a_agent_test.go index 0b83a5b9ce..970a62c404 100644 --- a/agent/a2aagent/a2a_agent_test.go +++ b/agent/a2aagent/a2a_agent_test.go @@ -2914,6 +2914,33 @@ func TestA2AAgent_sendErrorEvent_UsesRunErrorType(t *testing.T) { require.Equal(t, evt.Response.Error, respErr) } +func TestA2AAgent_sendErrorEvent_RedactsSensitiveMessage(t *testing.T) { + a := &A2AAgent{name: "remote-agent"} + eventCh := make(chan *event.Event, 1) + invocation := &agent.Invocation{InvocationID: "inv-test"} + + respErr := a.sendErrorEvent( + context.Background(), + eventCh, + invocation, + fmt.Errorf("request failed Authorization: Bearer raw-token\napi_key=sk-1234567890abcdef\nCookie: session=abc; sid=def"), + ) + + require.NotNil(t, respErr) + require.Equal(t, model.ErrorTypeRunError, respErr.Type) + for _, secret := range []string{"raw-token", "sk-1234567890abcdef", "session=abc", "sid=def"} { + require.NotContains(t, respErr.Message, secret) + } + for _, redacted := range []string{"Authorization: ****", "api_key=****", "Cookie: ****"} { + require.Contains(t, respErr.Message, redacted) + } + + evt := <-eventCh + require.NotNil(t, evt) + require.NotNil(t, evt.Response) + require.Equal(t, respErr, evt.Response.Error) +} + func TestA2AAgent_aggregateEventContent_IgnoresErrorResponses(t *testing.T) { a := &A2AAgent{name: "remote-agent"} builder := &strings.Builder{}