Repository navigation
Per-command network allow lists, and deny reasons from the ask callback - #591
Merged
Merged
Conversation
…llback
registerCommandNetworkLists(commandId, { allowedDomains }) gives one
invocation an allow list of its own for as long as it runs, in addition
to the configured one; unregisterCommandNetworkLists(commandId) removes
it and reset() removes all. Until now the only way to let one command
reach one more host was updateConfig, which widens every command and
the default injectHosts scope. For a connection the order is:
deniedDomains denies, allowedDomains allows, strictAllowlist denies,
the list registered for the id in the proxy username allows, then the
ask callback, else deny. Entries get the same check as
network.allowedDomains; the predicate moves to domain-pattern.ts so the
manager does not load the schema module. An invalid entry throws, and
so does a commandId shorter than 22 characters, because the id is the
only thing that ties a connection to a list.
SandboxAskCallback may now resolve to { allow: false, reason }, and the
reason is reported in the violation line in place of "user denied". It
is sanitized like the rest of the line and cut to 500 UTF-16 code
units. Only the value true allows: a callback that resolved to some
other truthy value used to allow and now denies.
SandboxManager.askCallbackDenyReason is true so a caller can tell
whether a reason object is understood before returning one.
64 cases through the live proxy against a loopback listener: the order of evaluation, entry validation, how the id is read from the proxy username, each shape the callback can answer with, what the reason is reduced to before it is stored, and the warning under SRT_DEBUG.
Closed
Comments only: the JavaScript emitted with comments removed is byte-identical for every file, and the test names are unchanged. Kept: every invariant and what could be done without it, the documentation of exported names, and the reasons for what is not done the obvious way. Cut: how the code came to be as it is, narration of the lines that follow, and explanations repeated at several sites.
… a real wrap Under an external `network.httpProxyPort` the wrap hands the proxy no username, so no connection names an id and a registered list never applies. It fails closed; the README and the doc comment now say so, and registering in that state logs one debug line. The 22-character floor is measured on the key the map holds, with U+FFFD left out: every unpaired surrogate becomes one, so an id made of them passed the floor while carrying nothing. New tests: a list applies over SOCKS5 as over CONNECT, and it reaches a sandboxed curl through the commandId its wrap was given.
No conflicts. The README said "the first release after v0.0.77" of the ask callback allowing on `true` alone; 0.0.78 has been cut since, without it.
No conflicts. The README said the ask callback's change comes in 0.0.79, which was cut without it.
Merged
This was referenced Oct 16, 2026
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.
Adds a per-command network allow list, and lets the ask callback deny with a reason.
What changes
SandboxManager.registerCommandNetworkLists(commandId, { allowedDomains })andunregisterCommandNetworkLists(commandId). Entries are checked likenetwork.allowedDomains; an invalid one, or an id under 22 characters (unpaired surrogates not counted), throws.deniedDomainsdenies,allowedDomainsallows,strictAllowlistdenies, the list for the proxy username's id allows, the ask callback decides, else deny.{ allow: false, reason }; the reason (sanitized, 500 characters) goes in the violation line. Only=== trueallows.SandboxManager.askCallbackDenyReasonmarks a release that reads it.Who sees a difference
{ allow: false, reason }as an allow.ISandboxManageradd three members.Known limits
network.httpProxyPort(registering succeeds, with oneSRT_DEBUGline), nor under a non-ASCII id too long for the proxy username.Merge order
Conflicts with #584 and #615 in one comment-only hunk, the JSDoc of
commandId(sandbox-manager.ts ~1755): keep both paragraphs. #615 logscommandIdunder debug; this PR keeps ids out of error text.Testing
68 new cases in
test/sandbox/update-config.test.ts(CONNECT, SOCKS5, sandboxedcurl); same failing names asmain.