Skip to content

dashboard: format NDJSON / SSE / form-encoded bodies in the Body tab#37

Open
krishnamallam wants to merge 2 commits into
mainfrom
feat/dashboard-format-structured-bodies
Open

dashboard: format NDJSON / SSE / form-encoded bodies in the Body tab#37
krishnamallam wants to merge 2 commits into
mainfrom
feat/dashboard-format-structured-bodies

Conversation

@krishnamallam

Copy link
Copy Markdown
Owner

Addresses #36.

Summary

The dashboard Body tab pretty-printed only single-object JSON. Structured-but-not-single bodies rendered as one flat line. This adds type-aware formatting:

  • NDJSON / JSONL (application/x-ndjson, application/jsonl) — each line pretty-printed.
  • SSE (text/event-stream) — each data: JSON payload indented; non-data lines kept.
  • form-encoded (application/x-www-form-urlencoded) — raw key = value lines, aligned on =.
  • Single-object JSON unchanged. Anything unrecognized falls back to verbatim. _format_body never raises; redact_marks + HTML-escaping still run after formatting.

content_type is plumbed from the detail route (read from the stored request headers).

Hardening (from an adversarial review of the diff)

  • DoS fix: the first cut used str.ljust(width) with an uncapped width — one ~50 KB form key padded every other line, so a 100 KB urlencoded body rendered ~650 MB server-side. The alignment column is now capped, keeping output proportional to input.
  • Audit fidelity: form values are shown raw (not URL-decoded), and only genuine key=value segments are formatted — prose / partial bodies fall back to verbatim instead of being reshaped.
  • Never-500: a malformed (non-string) content_type is coerced to None rather than throwing.
  • NDJSON/SSE line splitting is wire-accurate (split("\n") + strip \r).

Test plan

  • uv run pytest191 passed, 1 skipped (20 format_body tests, incl. DoS output bound, raw-display fidelity, content-type coercion, charset param, SSE/NDJSON verbatim fallbacks, CRLF, alignment, route-level NDJSON + form rendering)
  • uv run ruff check . && uv run ruff format --check . && uv run mypy upbox — clean

Note on #36

This handles the non-single-JSON cause from #36's triage. The originally reported symptom (a small valid JSON body showing unindented) was not reproduced — the formatter renders valid JSON correctly (already covered by existing passing tests), so that symptom is most likely a stale running upbox start process (a restart picks up merged code), not a code bug. Leaving #36 open so that can be confirmed after merge rather than auto-closing it.

…y tab

The Body tab pretty-printed only single-object JSON; structured-but-not-single
bodies (NDJSON, SSE event streams, urlencoded forms) rendered as one flat line.
Add _format_body(body, content_type): JSON-first, then per-type formatting with
a verbatim fallback that never raises. content_type is plumbed from the detail
route; redact_marks + HTML escaping still run after formatting.
… cases)

Adversarial review of the formatter found and fixed:
- DoS: uncapped ljust in _format_form let one long key pad every line
  (a 100KB urlencoded body rendered ~650MB). Cap the alignment column at 40.
- Fidelity/over-matching: parse_qsl decoded values and accepted non-form text.
  Now require every &-segment to be a real key=value pair (else verbatim) and
  show raw, undecoded values for audit accuracy.
- Robustness: coerce a non-string content_type to None so a malformed row
  can't 500 the Body tab.
- Wire-accurate NDJSON/SSE line splitting (split on \n, strip \r) so a Unicode
  line separator inside a JSON string can't fragment a logical line.
Adds regression + edge tests: DoS output bound, raw display, content-type
coercion, charset param, SSE/NDJSON verbatim fallbacks, CRLF, alignment.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant