Use raw bodies to avoid render request escaping#4579
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
size-limit report 📦
|
Review: Use raw bodies to avoid render request escaping (#4579)OverviewThis PR replaces Good coverage on the new code paths: Issues found (posted inline)
Not flagged inline — needs attention
Since Minor
No correctness issues found in the core encode/decode round-trip logic itself (header parsing, boolean/array field handling, and the |
a54ebae to
ce32f4b
Compare
ReviewWhat this PR does: Fixes #3584 by switching the render-request body from Overall the implementation is careful — header-injection is guarded against for the password ( Main concern: breaking wire-protocol change with no version gate, contradicting documented rolling-deploy guidanceThis repo explicitly documents the Node Renderer as a standalone service that can be deployed independently from Rails (
The promise there is that a version mismatch produces a clean, actionable error — and indeed This PR removes
Every prior protocol version bump could still rely on both ends spearing the same wire encoding (form-urlencoded), so mismatches always reached the protocol-version check. This PR is the first change to break the wire encoding itself, silently invalidating the "you'll get a clear error" guarantee the deployment docs promise for that topology — and Suggested fixes (any one of these):
Minor notes
Nice test coverage on the new raw-body path, and the CRLF-injection guard on the password header is a good catch. The main ask is closing the version-skew gap described above before this ships, given it's a documented supported deployment topology. |
…rmbody compat - Run protocol/password prechecks before raw header shape validation so a malformed but unauthenticated raw request fails auth (401) first, matching the parsed-body path and /asset-exists - Keep @fastify/formbody registered (with test) so a not-yet-upgraded gem in a rolling deploy still reaches the protocol/render path instead of an unactionable 415; package.json and pnpm-lock.yaml now match main - Sanitize every raw render metadata header value against CRLF injection, not just password, and document the three-way field-list sync requirement - Send every non-stream string response with explicit text/plain + nosniff, clearing the CodeQL js/reflected-xss alert on the render response path - Revert the incidental Gemfile.lock BUNDLED WITH bump (2.5.4 stays) - Add the [Pro] CHANGELOG entry with the renderer-first deploy-order note Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
+ci-run-hosted |
Hosted CI RequestedTriggered 9 workflow(s) for View progress in the Actions tab. |
Review: Use raw bodies to avoid render request escapingOverviewReplaces the Bug: dev-mode gem/renderer version-mismatch check is skipped for raw requests
Other notes (non-blocking)
Overall this is a solid, well-tested change; the main thing worth fixing before merge is the version-mismatch precheck gap. |
rawRenderPrecheckBody omitted the fields checkProtocolVersion uses for the non-production gem/renderer version-mismatch 412, silently skipping that guard on the raw path (the common non-bundle render path). Forward both headers and add a test asserting a mismatched gem version still 412s outside production. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review: Use raw bodies to avoid render request escaping (#4579)OverviewReplaces the What I verified
Findings (see inline comments)
Minor / non-blocking nits (not inline-commented)
Overall the design is sound, well-tested, and the rolling-deploy compatibility tradeoffs are clearly documented in the PR description. The three findings above are all fixable in isolation and none are blocking from a correctness/security standpoint of the core protocol change. |
… at the type level - rawRenderPrecheckBody no longer defines keys for absent headers, so the protocol-version 412's "received fields" list only names fields the client actually sent; test added for the no-metadata case - ResponseResult marks Content-Type as never: setResponse intentionally forces text/plain on string payloads (reflected-XSS hardening), so a future caller setting a custom Content-Type now fails to compile instead of being silently clobbered Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review: PR #4579 — Use raw bodies to avoid render request escapingOverviewSwitches non-bundle render requests from Strengths
Minor observations (non-blocking)
Security
VerdictSolid, carefully-reasoned change with strong test coverage on both the Ruby and Node sides. No correctness or security bugs found in the diff — only the minor style/robustness notes above, none of which block merging. |
Pro Node Renderer Benchmark Summary
▲/▼ non-zero change vs baseline · 0.0% exact/near-zero match · 🔴 significant regression · 🟢 significant improvement (tracked measures) · (n) = baseline |
Core Benchmark Summary
▲/▼ non-zero change vs baseline · 0.0% exact/near-zero match · 🔴 significant regression · 🟢 significant improvement (tracked measures) · (n) = baseline |
Pro (shard 1/2) Benchmark Summary
▲/▼ non-zero change vs baseline · 0.0% exact/near-zero match · 🔴 significant regression · 🟢 significant improvement (tracked measures) · (n) = baseline |
Pro (shard 2/2) Benchmark Summary
▲/▼ non-zero change vs baseline · 0.0% exact/near-zero match · 🔴 significant regression · 🟢 significant improvement (tracked measures) · (n) = baseline |
PR-Batch Handoff — continuation lane
|
Summary
Fix #3584.
Pull Request checklist
- [ ] Update documentationAgent Decision Log
Continuation commit b62f91b (review closeout) made these non-blocking decisions:
@fastify/formbodyremoval (review thread)PROTOCOL_VERSIONstays 2.0.0.PROTOCOL_VERSIONbump would not help either drift direction, since an old renderer fails on the new content type before any version check.PROTOCOL_VERSIONbump.js/reflected-xss(1 high) at the render response sendsetResponseso every non-stream string response carries explicittext/plain; charset=utf-8+X-Content-Type-Options: nosniff(previously only the >= 400 branch did).ResponseResultsets its own Content-Type.react_on_rails_pro/Gemfile.lockBUNDLED WITH2.5.4 → 4.0.10Confidence note: raw-path worker tests 60/60 locally (including new 401-precedence and legacy urlencoded round-trip tests); Pro request/http-client specs 104 examples 0 failures; rubocop, RBS, tsc, eslint, prettier clean. Streaming jest suites fail/hang locally on a clean checkout too (environmental); hosted CI is the arbiter for those. Net package.json + pnpm-lock.yaml diff vs main is zero after the formbody restore.