Skip to content

fix(sandbox-proxy): cap file-op and config request body size - #5316

Merged
pedrofrxncx merged 1 commit into
mainfrom
fix/sandbox-proxy-file-op-body-limit-w3
Jul 27, 2026
Merged

fix(sandbox-proxy): cap file-op and config request body size#5316
pedrofrxncx merged 1 commit into
mainfrom
fix/sandbox-proxy-file-op-body-limit-w3

Conversation

@pedrofrxncx

@pedrofrxncx pedrofrxncx commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

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 assigned agent-sandbox-sessions.ts file (deleted by #5240's revert just before this HEAD).

Why: the "quick file ops" (write/unlink/mkdir/rename/read/glob/grep) and the config PUT route all forward the raw request body to the sandbox daemon by first reading it fully into memory with c.req.text() inside proxyDaemon. Every other route on this same router that forwards a body (suggest-commit, judge-review, preview-invoke) already caps its size with bodyLimit; 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 fmt and cd apps/api && bunx tsc --noEmit both 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 helpers redactRepoDir/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.

  • Bug Fixes
    • Apply shared bodyLimit to write, unlink, mkdir, rename, read, glob, grep, and config routes.
    • Aligns with existing caps on suggest-commit, judge-review, and preview-invoke.

Written for commit 335ca97. Summary will update on new commits.

Review in cubic

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
pedrofrxncx enabled auto-merge (squash) July 27, 2026 23:25
@pedrofrxncx
pedrofrxncx merged commit 35cc2cf into main Jul 27, 2026
14 of 15 checks passed
@pedrofrxncx
pedrofrxncx deleted the fix/sandbox-proxy-file-op-body-limit-w3 branch July 27, 2026 23:36
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.
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