Skip to content

fix(http): stop leaking tracebacks in JSONL/RPC error responses#361

Open
RealDiligent wants to merge 1 commit into
vouchdev:mainfrom
RealDiligent:fix/critical-issue-rpc-traceback-leak
Open

fix(http): stop leaking tracebacks in JSONL/RPC error responses#361
RealDiligent wants to merge 1 commit into
vouchdev:mainfrom
RealDiligent:fix/critical-issue-rpc-traceback-leak

Conversation

@RealDiligent

@RealDiligent RealDiligent commented Jul 5, 2026

Copy link
Copy Markdown

Summary

Root cause: handle_request() in jsonl_server.py included traceback.format_exc() in the error envelope for unexpected exceptions. HTTP /rpc forwards this verbatim, so bearer-authenticated clients on public deploys receive full stack traces.

Fix: Log with logging.exception() server-side; return only code and message to clients.

Impact: Closes an information-disclosure vector without changing success paths or expected error codes.

Risk / tradeoffs

  • Operators debugging production must check server logs for stack traces (standard practice).

Test plan

  • test_jsonl_internal_error_omits_traceback (new)

Summary by CodeRabbit

  • Bug Fixes
    • Internal server errors now return a cleaner error response without exposing traceback details.
    • Unexpected exceptions are logged server-side while keeping client-facing error output limited to the error code and message.
    • Added a regression test to confirm traceback information is not included in internal error responses.

Unexpected exceptions in handle_request() included full stack traces
in the JSON error envelope, exposing paths and internals to HTTP /rpc
clients. Log server-side and return message only.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The JSONL server's generic exception handler was updated to use Python's logging module instead of including a traceback string in error responses. Exceptions are now logged server-side via a module-level logger, and the client-facing internal_error payload only contains a code and message. A regression test was added to verify traceback data is no longer exposed.

Changes

Internal Error Response Hardening

Layer / File(s) Summary
Logging-based exception handling
src/vouch/jsonl_server.py
Replaces the traceback import with logging, adds a module-level logger _log, and updates the generic exception handler in handle_request to log exceptions via _log.exception(...) while returning an internal_error response with only code and message fields.
Regression test for traceback omission
tests/test_jsonl_server.py
Adds test_jsonl_internal_error_omits_traceback, which monkeypatches the kb.status handler to raise a RuntimeError and asserts the response has ok: False, error.code == "internal_error", and no traceback field.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant JsonlServer as handle_request
  participant Logger as _log

  Client->>JsonlServer: request (e.g. kb.status)
  JsonlServer->>JsonlServer: handler raises unexpected exception
  JsonlServer->>Logger: _log.exception(...)
  JsonlServer->>Client: {ok: false, error: {code: internal_error, message}}
Loading

Related PRs: None identified.

Suggested labels: bug, security

Suggested reviewers: None identified.

🐰 A traceback once leaked through the wire,
now hushed to a log, kept safely inside.
The client sees only a tidy small note,
while errors get logged where the rabbit hole goes.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: removing traceback leakage from JSONL/RPC error responses.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added mcp mcp, jsonl, and http surfaces tests tests and fixtures size: XS less than 50 changed non-doc lines labels Jul 5, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/test_jsonl_server.py (1)

239-250: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Test doesn't cover message-content leakage.

Good regression coverage for the traceback field, but the test doesn't assert on resp["error"]["message"]. Given the handler raises RuntimeError("secret internals"), the current implementation would actually return that string in message — the exact class of leak the PR aims to prevent. If the message fix above is adopted, add an assertion here (e.g., assert "secret internals" not in resp["error"]["message"]) to lock in that behavior too.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_jsonl_server.py` around lines 239 - 250, The regression test for
`handle_request` only checks that `resp["error"]` omits the traceback, but it
still allows the raised `RuntimeError("secret internals")` to leak through
`resp["error"]["message"]`. Update `test_jsonl_internal_error_omits_traceback`
to also assert that the error message does not contain the exception text, using
the existing `jsonl_server.HANDLERS` monkeypatch and `handle_request` flow so
the non-leak behavior is covered end to end.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/vouch/jsonl_server.py`:
- Around line 765-773: The `except Exception as e` handler in `jsonl_server.py`
still returns handler-controlled text via `message: str(e)`, which can leak
sensitive internals to `/rpc` clients. Update the `internal_error` response to
use a generic, static message instead of the raw exception text, and keep the
detailed exception only in `_log.exception("internal error handling %s",
method)` so `req_id`/`method` stay contextual while `str(e)` is not exposed over
HTTP.

---

Nitpick comments:
In `@tests/test_jsonl_server.py`:
- Around line 239-250: The regression test for `handle_request` only checks that
`resp["error"]` omits the traceback, but it still allows the raised
`RuntimeError("secret internals")` to leak through `resp["error"]["message"]`.
Update `test_jsonl_internal_error_omits_traceback` to also assert that the error
message does not contain the exception text, using the existing
`jsonl_server.HANDLERS` monkeypatch and `handle_request` flow so the non-leak
behavior is covered end to end.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a504bf3f-e6a4-498d-96c5-d8627b8f2703

📥 Commits

Reviewing files that changed from the base of the PR and between d80b1e2 and 96ea554.

📒 Files selected for processing (2)
  • src/vouch/jsonl_server.py
  • tests/test_jsonl_server.py

Comment thread src/vouch/jsonl_server.py
Comment on lines 765 to 773
except Exception as e:
_log.exception("internal error handling %s", method)
return {
"id": req_id, "ok": False,
"error": {
"code": "internal_error",
"message": str(e),
"traceback": traceback.format_exc(),
},
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Exception message can still leak sensitive internal details.

Traceback is now correctly kept server-side, but "message": str(e) still forwards the raw exception text to the client over HTTP /rpc. Exception messages frequently embed sensitive internals (file paths, connection strings, IDs, or — as the new test itself demonstrates with RuntimeError("secret internals") — arbitrary secrets a handler author put in an error message). This only partially closes the information-disclosure gap described in the PR objectives.

Consider returning a generic, static message for the internal_error case (logging str(e) server-side alongside the traceback) so unexpected-exception responses never echo handler-controlled text to clients.

🔒 Proposed fix
     except Exception as e:
-        _log.exception("internal error handling %s", method)
+        _log.exception("internal error handling %s: %s", method, e)
         return {
             "id": req_id, "ok": False,
             "error": {
                 "code": "internal_error",
-                "message": str(e),
+                "message": "internal server error",
             },
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
except Exception as e:
_log.exception("internal error handling %s", method)
return {
"id": req_id, "ok": False,
"error": {
"code": "internal_error",
"message": str(e),
"traceback": traceback.format_exc(),
},
}
except Exception as e:
_log.exception("internal error handling %s: %s", method, e)
return {
"id": req_id, "ok": False,
"error": {
"code": "internal_error",
"message": "internal server error",
},
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vouch/jsonl_server.py` around lines 765 - 773, The `except Exception as
e` handler in `jsonl_server.py` still returns handler-controlled text via
`message: str(e)`, which can leak sensitive internals to `/rpc` clients. Update
the `internal_error` response to use a generic, static message instead of the
raw exception text, and keep the detailed exception only in
`_log.exception("internal error handling %s", method)` so `req_id`/`method` stay
contextual while `str(e)` is not exposed over HTTP.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mcp mcp, jsonl, and http surfaces size: XS less than 50 changed non-doc lines tests tests and fixtures

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant