Skip to content

Detect JSON-RPC application errors in proxy audit middleware#4709

Open
gmogmzGithub wants to merge 2 commits intostacklok:mainfrom
gmogmzGithub:fix/audit-jsonrpc-error-detection
Open

Detect JSON-RPC application errors in proxy audit middleware#4709
gmogmzGithub wants to merge 2 commits intostacklok:mainfrom
gmogmzGithub:fix/audit-jsonrpc-error-detection

Conversation

@gmogmzGithub
Copy link
Copy Markdown

@gmogmzGithub gmogmzGithub commented Apr 9, 2026

Summary

  • The proxy audit middleware 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.
  • Adds JSON-RPC error detection: after receiving an HTTP 200, the middleware inspects a small prefix of the response body for a top-level "error" field. If found, it records outcome=application_error with the JSON-RPC error code and truncated message as metadata.
  • Adds a DetectApplicationErrors config flag (default true) that controls this behavior independently of IncludeResponseData, so error detection works without enabling full response capture.

Fixes #4678 as a BUG FIX

Changes

File Change
pkg/audit/event.go Add OutcomeApplicationError constant
pkg/audit/config.go Add DetectApplicationErrors config flag with ShouldDetectApplicationErrors() helper
pkg/audit/auditor.go Add errorDetectionBody prefix buffer to responseWriter, JSON-RPC error check in logAuditEvent(), error metadata attachment
pkg/mcp/response.go Add ParseMCPResponse() helper for detecting JSON-RPC errors in response bodies
pkg/mcp/response_test.go 9 test cases covering error detection, valid responses, truncated JSON, edge cases
pkg/audit/auditor_test.go 7 test cases covering buffer capture, config toggle, and full middleware integration

Does this introduce a user-facing change?

Yes. Audit logs now distinguish between true successes and application-level failures. Events that previously showed outcome=success for JSON-RPC errors will now show outcome=application_error with jsonrpc_error_code and jsonrpc_error_message in metadata. This is enabled by default and can be disabled via the detectApplicationErrors audit config flag.

Special notes for reviewers

  • The errorDetectionBody buffer is only allocated when IncludeResponseData is false. When response data capture is already enabled, the existing rw.body buffer is reused to avoid double-buffering.
  • A prefix[0] == '{' guard skips JSON parsing for non-JSON responses (health checks, SSE keepalives).
  • Custom jsonRPCError/jsonRPCResponseEnvelope structs are used instead of jsonrpc2.DecodeMessage because the library's wireError type is unexported, making it impossible to extract the numeric error code from outside the package.
  • This approach only covers Streamable HTTP JSON responses. Legacy SSE transport (where POST returns 202 and the response arrives on a separate GET stream) is not covered — documented as a known limitation per discussion in Proxy audit middleware records outcome=success for JSON-RPC application-level errors in HTTP 200 responses #4678.

@gmogmzGithub gmogmzGithub force-pushed the fix/audit-jsonrpc-error-detection branch from b87bbf7 to c4c842f Compare April 9, 2026 17:01
@github-actions github-actions bot added the size/M Medium PR: 300-599 lines changed label Apr 10, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 10, 2026

Codecov Report

❌ Patch coverage is 72.13115% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.89%. Comparing base (a4f47dc) to head (e6fd4ee).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
pkg/audit/auditor.go 57.50% 14 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4709      +/-   ##
==========================================
+ Coverage   68.86%   68.89%   +0.02%     
==========================================
  Files         517      518       +1     
  Lines       54441    54499      +58     
==========================================
+ Hits        37492    37545      +53     
- Misses      14078    14083       +5     
  Partials     2871     2871              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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>
- Extract detectApplicationError into separate method to reduce
  cyclomatic complexity of logAuditEvent (gocyclo > 15)
- Fix gci import formatting in config.go
- Regenerate swagger docs for new DetectApplicationErrors config field

Signed-off-by: Guillermo Gomez <guillermogomezmora@gmail.com>
@gmogmzGithub gmogmzGithub force-pushed the fix/audit-jsonrpc-error-detection branch from c4c842f to e6fd4ee Compare April 12, 2026 18:21
@github-actions github-actions bot added size/M Medium PR: 300-599 lines changed and removed size/M Medium PR: 300-599 lines changed labels Apr 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/M Medium PR: 300-599 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Proxy audit middleware records outcome=success for JSON-RPC application-level errors in HTTP 200 responses

2 participants