Skip to content

Answer 502 when an upstream sends an invalid status code - #660

Merged
ant-kurt merged 1 commit into
mainfrom
fix/invalid-upstream-status
Oct 6, 2026
Merged

ant-kurt merged 1 commit into
mainfrom
fix/invalid-upstream-status

Conversation

@ant-kurt

@ant-kurt ant-kurt commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator

What was wrong

When an upstream answered with a response head that Node's res.writeHead refuses (for example HTTP/1.1 099 x), the proxy's response callback threw RangeError [ERR_HTTP_INVALID_STATUS_CODE]. Nothing caught it, so the process running the proxy exited. Every connection through it was dropped, and the sandboxed command lost network access. Any upstream the sandbox is allowed to reach could trigger this. The defect is in the latest release, 0.0.78.

Fix

A new helper, relayResponseHead(res, upstreamRes) in src/sandbox/parent-proxy.ts, writes the upstream status and headers inside a try. All four relay sites now use it: the MITM-socket, parent-proxy and direct paths in http-proxy.ts, and the TLS-terminated path in tls-terminate-proxy.ts. If writeHead throws:

  • the upstream response is destroyed and its body is not piped;
  • if nothing has been sent to the client yet, any partly set headers are cleared and the client gets 502 Bad Gateway;
  • if headers were already sent, the client response is destroyed.

Valid responses are relayed exactly as before. There is no API or configuration change. Opaque CONNECT tunnels are not affected.

Tests

test/sandbox/invalid-upstream-response.test.ts has two groups.

The relayResponseHead cases call the helper inside a real node:http server with a stub upstream, so they exercise the writeHead failure directly. Bun's writeHead throws on these heads just as Node's does. Status 99 and 1000, a control character in a header value, and an invalid header name each give the client a 502 with no upstream header, including one accepted before the rejected one. The upstream is destroyed and the helper returns false. If a head was already sent, the client response is cut off instead. A valid head is relayed. With the try/catch removed, the five failure cases fail.

The end-to-end cases send status 099 and 000 through the plain HTTP (direct) and TLS-terminated paths. Each checks that the client gets 502 with no upstream header or body bytes, and that a following normal request through the same proxy succeeds. Under Bun, which bun test uses, the HTTP client itself rejects these status lines, so these requests take the existing request-error path and never reach writeHead. They pin the 502 outcome and that the proxy keeps serving. The parent-proxy and MITM-socket paths have no end-to-end case. Under Node, the direct and parent-proxy paths were checked by hand: before this change the proxy crashes with Invalid status code: 99. After it, the client gets HTTP/1.1 502 Bad Gateway.

bun test test/sandbox/invalid-upstream-response.test.ts

Generated by Claude Code

An upstream response head that writeHead rejects (for example
"HTTP/1.1 099 x") made writeHead throw inside the response callback. The
exception was uncaught and ended the proxy process.

Relay the upstream response head through one guarded helper,
relayResponseHead, at all four relay sites: the direct, parent-proxy and
MITM-socket paths in http-proxy.ts, and the TLS-terminated path in
tls-terminate-proxy.ts. If writeHead throws (an invalid status, or a
header name or value it refuses), the upstream response is destroyed and
not piped. The client gets 502 Bad Gateway if nothing was sent yet, with
any partly set headers cleared, or its response is destroyed if headers
already went out. The proxy keeps serving.

The new test calls the helper directly with invalid heads, and sends
status 099 and 000 end to end through the direct and TLS-terminated
paths.
@ant-kurt
ant-kurt force-pushed the fix/invalid-upstream-status branch from 5527e48 to da70dc2 Compare October 6, 2026 23:20
@ant-kurt
ant-kurt enabled auto-merge October 6, 2026 23:21
@ant-kurt
ant-kurt merged commit 12a2be9 into main Oct 6, 2026
16 checks passed
@ant-kurt
ant-kurt deleted the fix/invalid-upstream-status branch October 6, 2026 23:31
@ronleizrowice-ant ronleizrowice-ant mentioned this pull request Oct 7, 2026
ronleizrowice-ant added a commit that referenced this pull request Oct 7, 2026
New to the bundle: #655, #660 and the version. #643, #639 and #630 come as
squashes of what it held; in the one block git marks, the workflow's, it
keeps its own side, which has the checksummed bun as well.

Two cases of #655 compare two wrapped commands. Here a wrap that names
mount points names a manifest of its own (#584), so the cases take that
name out before they compare.
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