Repository navigation
Conversation
v0ropaev
force-pushed
the
fix/proxy-deny-reason-in-band
branch
from
October 11, 2026 09:04
1d142a1 to
c8b3b6a
Compare
A destination on no list at all is the common denial and the one deniedDomainReasons cannot reach, since there is no deniedDomains entry to key a reason on. network.allowlistDenyReason names it once. The plain-HTTP handler also passed no explain callback and answered with the generic body, so a reason that reaches a refused CONNECT as its status phrase reached a plain-HTTP client not at all, although the body is the one place a client prints without -v. Signed-off-by: Dmitry Voropaev <dy.voropaev@gmail.com>
v0ropaev
force-pushed
the
fix/proxy-deny-reason-in-band
branch
from
October 28, 2026 08:40
c8b3b6a to
4d4a200
Compare
Author
|
Rebased on main. The only conflict was the README option list after #745, and the deny-reason tests still pass on top of it. |
This branch has not been deployed
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.
Closes #273.
Rewritten. When I opened this in September,
HttpProxyServerOptions.filterreturned a bare boolean and had nowhere to put a reason, so the branch built that plumbing. Main has it now, through theexplaincallback andmarkFor, and in a better shape than mine. So this is down to the two things that are still missing, on top of yours.The off-allowlist deny has no reason to configure
deniedDomainReasonsis keyed by the exactdeniedDomainsentry, which means it can only speak for a destination somebody listed. The denial the issue is actually about is the other one, a host on no list at all, and there is no key to hang text on. Both sites that raise it,strictAllowlistatsandbox-manager.ts:800and the no-ask-callback path at:819, sayhost is not on the allow listand nothing else.network.allowlistDenyReasonis that text. It goes through the samedenied()helper, so it lands in the violation line, in the CONNECT status phrase and in the HTTP body with no further wiring. Unset, everything reads as it does today.This is the part of #273 I think is load-bearing. The reporter's complaint is that the agent reads the refusal as a proxy fault and retries with
--no-proxy; what they want to say is "do not retry via alternate DNS / proxy / VPN", and no deny-list entry exists for the host that triggered it.The plain-HTTP handler drops the reason
The CONNECT handler passes an
explaincollector and puts the reason in the status phrase. The plain-HTTP handler callsoptions.filterwith four arguments and then answersrespondDenied(res, ...allowlistDeny), so the reason is neither collected nor written, andrespondDeniedtakes one as its second parameter.That is backwards with respect to what clients read. curl prints a 403 body and does not print a CONNECT status phrase unless you ask for
-v:Measured against a proxy built from
createHttpProxyServerwith a filter that denies and explains, before and after the change:curl 8.7.1, macOS. The
https://line is unchanged either way, which is the point: the phrase is there on the wire and curl does not show it.The resolved-address refusal already does it this way, and the README describes it that way, "the reason in the body and, for a CONNECT, as the status phrase". The host-allowlist deny was the one path that did not.
Four lines in the handler: collect the reason, and pass it as the body with the generic text as the fallback.
Tests
test/sandbox/allowlist-deny-reason.test.ts, four tests throughSandboxManager, checking the configured reason in the CONNECT phrase, in the plain-HTTP body and in the violation line, the generic text when the field is unset, and that the schema rejects an empty string.Two more in
proxy-deny-violations.test.tsbeside your CONNECT phrase test, drivingcreateHttpProxyServerdirectly: the reasonfilter()explains becomes the plain-HTTP body, and an unexplained deny keepsConnection blocked by network allowlist. The first fails on main, which is the gap above.bun test test/sandboxgives 13 failures on this branch and the same 13 on a clean main (rgmissing from PATH, the tls-terminate keep-alive case, the chunked-body decider cases, the temp-object teardown cases).npm run typecheck,npm run lint:checkand prettier are clean.Not here
Reaching an
https://client in band still needs a TLS-terminated 403 rather than a refusal at the CONNECT, which inverts the ordering of the allowlist filter against termination. That is a design call, not something to slip in here, and I have not tried it.