Detect JSON-RPC application errors in proxy audit middleware#4709
Open
gmogmzGithub wants to merge 1 commit intostacklok:mainfrom
Open
Detect JSON-RPC application errors in proxy audit middleware#4709gmogmzGithub wants to merge 1 commit intostacklok:mainfrom
gmogmzGithub wants to merge 1 commit intostacklok:mainfrom
Conversation
The audit middleware previously determined outcome solely from the HTTP status code. Because MCP servers return errors inside HTTP 200 responses per the JSON-RPC spec, application-level failures (expired tokens, API errors, invalid parameters) were logged as outcome=success, making them invisible in audit logs. This change inspects the response body for a JSON-RPC error field when the HTTP status is 200. If found, the outcome is recorded as application_error with the error code and truncated message in metadata. A new DetectApplicationErrors config flag (default true) controls the behavior independently of IncludeResponseData, avoiding the need to enable full response capture just for error detection. Fixes stacklok#4678 Signed-off-by: Guillermo Gomez <guillermogomezmora@gmail.com>
b87bbf7 to
c4c842f
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4709 +/- ##
==========================================
+ Coverage 68.63% 68.67% +0.04%
==========================================
Files 516 517 +1
Lines 54113 54160 +47
==========================================
+ Hits 37138 37194 +56
+ Misses 14111 14103 -8
+ Partials 2864 2863 -1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
outcome=success, making them invisible in audit logs."error"field. If found, it recordsoutcome=application_errorwith the JSON-RPC error code and truncated message as metadata.DetectApplicationErrorsconfig flag (defaulttrue) that controls this behavior independently ofIncludeResponseData, so error detection works without enabling full response capture.Fixes #4678 as a BUG FIX
Changes
pkg/audit/event.goOutcomeApplicationErrorconstantpkg/audit/config.goDetectApplicationErrorsconfig flag withShouldDetectApplicationErrors()helperpkg/audit/auditor.goerrorDetectionBodyprefix buffer toresponseWriter, JSON-RPC error check inlogAuditEvent(), error metadata attachmentpkg/mcp/response.goParseMCPResponse()helper for detecting JSON-RPC errors in response bodiespkg/mcp/response_test.gopkg/audit/auditor_test.goDoes this introduce a user-facing change?
Yes. Audit logs now distinguish between true successes and application-level failures. Events that previously showed
outcome=successfor JSON-RPC errors will now showoutcome=application_errorwithjsonrpc_error_codeandjsonrpc_error_messagein metadata. This is enabled by default and can be disabled via thedetectApplicationErrorsaudit config flag.Special notes for reviewers
errorDetectionBodybuffer is only allocated whenIncludeResponseDatais false. When response data capture is already enabled, the existingrw.bodybuffer is reused to avoid double-buffering.prefix[0] == '{'guard skips JSON parsing for non-JSON responses (health checks, SSE keepalives).jsonRPCError/jsonRPCResponseEnvelopestructs are used instead ofjsonrpc2.DecodeMessagebecause the library'swireErrortype is unexported, making it impossible to extract the numeric error code from outside the package.