fix(sandbox-proxy): cap file-op and config request body size - #5316
Merged
Conversation
Quick file ops (write/unlink/mkdir/rename/read/glob/grep) and the config PUT route forward the raw request body to the sandbox daemon via c.req.text() in proxyDaemon, with no size limit — unlike the neighboring suggest-commit/judge-review/preview-invoke routes, which already cap their bodies. Any authenticated org member could send an arbitrarily large POST to these endpoints and have it buffered entirely into the Studio API process memory before being forwarded. Add a shared 10MB bodyLimit (matching the org-fs upload-size pattern) to these routes so an oversized request is rejected with 413 before being read into memory.
pedrofrxncx
enabled auto-merge (squash)
July 27, 2026 23:25
decocms Bot
pushed a commit
that referenced
this pull request
Jul 27, 2026
PR: #5316 fix(sandbox-proxy): cap file-op and config request body size Bump type: patch - decocms (apps/api/package.json): 4.138.6 -> 4.138.7 Deploy-Scope: server
pedrofrxncx
added a commit
that referenced
this pull request
Jul 28, 2026
Quick file ops (write/unlink/mkdir/rename/read/glob/grep) and the config PUT route forward the raw request body to the sandbox daemon via c.req.text() in proxyDaemon, with no size limit — unlike the neighboring suggest-commit/judge-review/preview-invoke routes, which already cap their bodies. Any authenticated org member could send an arbitrarily large POST to these endpoints and have it buffered entirely into the Studio API process memory before being forwarded. Add a shared 10MB bodyLimit (matching the org-fs upload-size pattern) to these routes so an oversized request is rejected with 413 before being read into memory.
pedrofrxncx
pushed a commit
that referenced
this pull request
Jul 28, 2026
PR: #5316 fix(sandbox-proxy): cap file-op and config request body size Bump type: patch - decocms (apps/api/package.json): 4.138.6 -> 4.138.7 Deploy-Scope: server
pedrofrxncx
added a commit
that referenced
this pull request
Jul 28, 2026
…routes (#5319) #5316 added a 10MB body-size cap to the file-op and config routes on this router, since proxyDaemon reads forwarded bodies fully into memory via c.req.text() before forwarding to the daemon. Four sibling routes on the same router pass forwardJsonBody: true through the identical code path but were left uncapped: git/diff, git/publish, git/discard, git/rebase. Any authenticated org member could POST an arbitrarily large body to these and have it buffered entirely into the Studio API process's memory. Reused the same bodyLimit middleware (renamed from fileOpBodyLimit to forwardedBodyLimit since it now covers all forwardJsonBody: true routes) on the four previously-uncapped routes.
pedrofrxncx
added a commit
that referenced
this pull request
Jul 28, 2026
…utes (#5330) #5316/#5319 capped request bodies clients send into this router (forwardedBodyLimit) because proxyDaemon buffers them fully into memory via c.req.text() before forwarding. The response side had the same gap: every route reads the daemon's (or, for preview-fetch/preview-invoke, the customer's own preview server's) response fully via upstream.text() with no ceiling. A runaway git diff, a chatty exec script, or a huge preview page gets buffered entirely into the Studio API process's memory. Added readBoundedText(), which reads a Response body in chunks and throws UpstreamPayloadTooLargeError once it exceeds the same 10MB cap already used for request bodies, instead of buffering an unbounded amount. Wired it into proxyDaemon, fetchDaemonJson (used by suggest-commit/judge-review), and the preview-fetch/preview-invoke routes — replacing every remaining upstream.text() call in this file.
pedrofrxncx
added a commit
that referenced
this pull request
Jul 28, 2026
apps/api/src/tools/sandbox/{patch-sandbox-operator,resolve-env,sync-git-credentials}.ts
each PUT to the daemon's /_sandbox/config and, on a non-ok response, read the
error body with a plain res.text() with no size cap. sandbox-proxy.ts already
bounds every proxied response to the same endpoint via readBoundedText()
(#5316/#5319/#5330) — these three call sites hit /_sandbox/config directly
(during publish, rebase, and sandbox start) and were missed, so a
compromised or misbehaving daemon can still make the API process buffer an
unbounded error body into memory on that path.
Extracted readBoundedText()/UpstreamPayloadTooLargeError out of
sandbox-proxy.ts into apps/api/src/lib/bounded-text.ts (no behavior change)
so the tools/sandbox call sites can reuse it without an api-routes ->
tools-layer dependency, and switched all three sites to it with the same
10MB cap sandbox-proxy.ts already applies to this endpoint.
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.
Source: a resource-bound/hardening gap found while auditing the agent sandbox proxy routes (
apps/api/src/api/routes/sandbox-proxy.ts), the surviving relative of the assignedagent-sandbox-sessions.tsfile (deleted by #5240's revert just before this HEAD).Why: the "quick file ops" (
write/unlink/mkdir/rename/read/glob/grep) and theconfigPUT route all forward the raw request body to the sandbox daemon by first reading it fully into memory withc.req.text()insideproxyDaemon. Every other route on this same router that forwards a body (suggest-commit,judge-review,preview-invoke) already caps its size withbodyLimit; these did not. Any authenticated org member (or a buggy client) can POST an arbitrarily large body to these endpoints and have it buffered entirely into the Studio API process's memory before ever reaching the daemon.Fix: add a shared 10MB
bodyLimit(sized to comfortably cover real source-file writes, well under the org-fs upload cap of 500MB used for actual binary uploads) to all the affected routes, rejecting oversized requests with 413 before they're read into memory.How to verify:
bun run fmtandcd apps/api && bunx tsc --noEmitboth pass. No existing test exercises the full Hono route table for this file (its test file,sandbox-proxy.test.ts, only unit-tests the exported pure helpersredactRepoDir/withClaimGitLock, per this repo's unit-vs-e2e testing split) — wiring a well-tested Hono middleware into the router isn't itself a new logic path, so no new test was added.Checks run locally:
bun run fmt,cd apps/api && bunx tsc --noEmit(both clean). Full CI validates the rest.Summary by cubic
Add a 10MB request body cap to sandbox file-op and config endpoints to prevent unbounded buffering and memory spikes. Oversized requests now return 413 before the body is read.
bodyLimittowrite,unlink,mkdir,rename,read,glob,grep, andconfigroutes.suggest-commit,judge-review, andpreview-invoke.Written for commit 335ca97. Summary will update on new commits.