Skip to content

Cap A2A unary HTTP bodies against unbounded remotes - #326

Merged
tylerreckart merged 7 commits into
mainfrom
fix/a2a-http-unary-body-cap
Sep 21, 2026
Merged

tylerreckart merged 7 commits into
mainfrom
fix/a2a-http-unary-body-cap

Conversation

@cursor

@cursor cursor Bot commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Problem

#317 capped SseReader line/event buffers so a remote A2A or --connect stream cannot OOM the process. rpc_call and http_get still appended every libcurl write-callback byte with no size limit. A hostile agent-card or JSON-RPC peer that omits Content-Length (or advertises a huge one) can grow HttpResponse::body until OOM. Timeouts bound duration, not memory — a fast peer can still push gigabytes.

--connect and A2A federation share this client. The inbound HTTP API already rejects bodies over 16 MiB; the unary outbound path did not.

Fix

Cap unary responses at 16 MiB (kHttpMaxBodyBytes, same as kSseMaxEventBytes and the inbound API). CURLOPT_MAXFILESIZE_LARGE refuses a declared oversize Content-Length before download; the write callback aborts if a streamed body crosses the cap. Partial buffers are cleared and the error is response exceeded size limit.

rpc_stream is unchanged: it already caps diagnostic raw bytes at 1 MiB and events via SseReader.

Tests

New unit_a2a_http loopback suite (ssrf_guard off):

  • small JSON GET still succeeds
  • Content-Length over the cap is refused
  • streamed body with no Content-Length is refused at the write callback; body is empty

Independently mergeable against main. Touches src/a2a/http.cpp, include/a2a/http.h, tests/test_a2a_http.cpp, CMakeLists.txt, and CHANGELOG.md [Unreleased]. No overlap with #321–#325 except the Unreleased changelog heading (same as those PRs with each other).

Open in Web View Automation 

Note

Medium Risk
Caps affect all federation/agent-card and JSON-RPC unary fetches through a2a/http.cpp; hostile peers are bounded, but legitimate responses over 16 MiB will fail. Unresolved merge conflicts in the sandbox SSRF test (and CMake) would break the build until fixed.

Overview
Outbound A2A unary HTTP (rpc_call, http_get) now enforces a 16 MiB ceiling via kHttpMaxBodyBytes, aligned with inbound API limits and kSseMaxEventBytes. libcurl gets CURLOPT_MAXFILESIZE_LARGE, and the write callback stops appending once the cap is hit, clears partial bodies, and surfaces response exceeded size limit instead of buffering unbounded streams when Content-Length is missing or wrong. rpc_stream is unchanged (already capped by SseReader).

A separate unit_a2a_http loopback test binary covers small GET success, oversize Content-Length, and chunked overflow without Content-Length.

Sandbox /read TOCTOU coverage adds a test-only read_check_hook on SandboxConfig (runs after resolve, before O_NOFOLLOW open) so the leaf-symlink-swap case can plant the swap deterministically instead of racing a pause + helper thread on macOS CI.

Note: The diff still contains unresolved merge conflict markers in tests/test_sandbox_ssrf.cpp (and a conflicting CMake block vs unit_remote_http / unit_json_numbers); those must be resolved before merge.

Reviewed by Cursor Bugbot for commit 0a1a1b5. Bugbot is set up for automated code reviews on this repo. Configure here.

cursoragent and others added 2 commits September 11, 2026 18:18
rpc_call and http_get appended every write-callback byte with no size
limit. SSE was capped in #317, but a hostile agent-card or JSON-RPC
peer that omits Content-Length could still grow the process until OOM.
Refuse bodies over 16 MiB (inbound API / kSseMaxEventBytes), abort via
MAXFILESIZE and the write callback, and clear a partial buffer.

Co-authored-by: Tyler Reckart <tylerreckart@users.noreply.github.com>
macos-arm64 CI raced the 80 ms pause plus helper thread: resolve
finished after the planter, so O_NOFOLLOW never saw the swap. Plant
the leaf symlink from a post-resolve hook instead.

Co-authored-by: Tyler Reckart <tylerreckart@users.noreply.github.com>
…dy-cap

# Conflicts:
#	CHANGELOG.md
#	tests/test_sandbox_ssrf.cpp
@tylerreckart
tylerreckart enabled auto-merge (squash) September 21, 2026 13:24
Keep both Unreleased changelog entries.

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

cursor Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_87e4c98d-c61b-4c5b-b531-36c16eb94eba)

Keep both Unreleased changelog entries.

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

cursor Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_f57a64bf-3aa0-41e7-81b4-81e1247cf182)

Keep both Unreleased changelog entries.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep both Unreleased changelog entries.

Co-authored-by: Cursor <cursoragent@cursor.com>
@tylerreckart
tylerreckart merged commit 40675cf into main Sep 21, 2026
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.

3 participants