You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Read this first
The ask callback allows a connection only with true. A callback that resolved to another truthy value (1, a string, an object) allowed in 0.0.79 and now denies. It may also resolve to { allow: false, reason }. (#591)
Linux: a sandboxed command stays in the namespaces made for it. It can no longer create a user namespace, or a mount namespace of its own. Whatever sandboxes itself that way stops working inside the sandbox by default: the namespace sandbox of Chromium and Electron, rootless podman or buildah, a nested bubblewrap, unshare -r. allowNestedUserNamespaces: true lifts it, for the configuration or for a single wrap. An embedder that compiles the seccomp helper into its own binary has to rebuild it. (#588, #617)
Linux: denyWrite takes precedence over allowWrite however the two are nested. A write path beneath a denyWrite directory is now read-only, as on macOS. (#684)
With a filterRequest, a request target that names another path once normalised is answered 400.network.ambiguousRequestTarget chooses between "refuse" (the default), "asSpelled" (what 0.0.79 sent) and "normalize". (#698)
Fixed: denies that did not apply
Linux, glob denies on some file systems. A denyRead pattern covers what lies beneath a directory whose listing gives no entry types (some NFS and FUSE mounts, XFS with ftype=0). (#672)
Glob walk. A directory that cannot be read is denied whole, and a directory that answers ENOENT under one of its names is asked for under its others. A matching link kept under its own spelling stays in the deny list. On Windows a drive root or a share root is a base like any other: C:/Users*/id.pem and //server/share/x*/y.key match. (#575, #690)
Linux, a file above a write deny. Where a file lies above a write-denied path, in a directory the command may write, the file is kept there read-only. In a worktree or a submodule that is its .git file. (#705)
Linux: the mounts of a deny set do not depend on the order it was written in. (#585)
Linux: VM sockets (AF_VSOCK) are refused with EPERM wherever the seccomp filter runs. (#691)
Credential files. A credentials.files entry takes precedence over allowRead and allowWrite on every platform: nothing at or beneath it is bound back or granted. (#680, #686)
Windows: a path in both denyRead and denyWrite keeps the read deny, and deny entries rank against allow entries as on the other platforms. (#656, #686; #650)
Names that hold a backslash. Off Windows the real path of such a name is asked of the file system, the same on every runtime. (#683, #689)
Fixed: sandboxes that did not start
A folder named like [user-id] or backup[2024-01-15]. Brackets that spell no set of characters are read as text, so no path entry, and no working directory, keeps a sandbox from starting. (#687, #718)
Linux without IPv6. The relays inside the sandbox name the family they listen on, and fall back to IPv4. With some socat versions nothing listened and every proxied command was refused. (#719; #706)
Linux, a mount point that cannot be made. An absent denyWrite path where bubblewrap cannot create a mount point (a protected folder of a Windows drive under WSL 2, a path under /usr) no longer keeps every command from starting. (#688)
Linux: a fifo or a socket above a write deny no longer keeps a command from starting. (#705)
macOS: git over SSH works through the sandbox's proxy. Every fetch or push was refused. (#681)
Fixed: other
Linux: an empty read-only file of the caller's own at a denyWrite path is no longer removed from the host after a command. (#673)
Linux: the mount points made for absent deny paths are removed when the last sandbox is done, also after a wrap that fails or overlaps another. (#671)
Temp files. A process that ends with process.exit(), or whose initialize() fails, leaves none of the library's objects in the temp directory. A generated CA's key was among them. (#670)
TLS termination: a certificate's serial number never begins with a zero byte. About one certificate in 65,000 was refused by strict parsers. (#685)
Proxy: an upstream that sends more than its Content-Length no longer loses its whole answer. (#717)
Proxy: in a tunnel, the 502 for a backend that failed is written only where nothing has gone back to the client yet. (#702)
Linux: the two relays inside the sandbox take a burst of connections. Of 128 made at once, about 20 used to fail. (#701)
Linux: a bwrap --version probe that gets no answer is not repeated at every ask. (#674)
Linux: wraps walk their patterns one at a time, which bounds memory and the time the event loop is held. (#679)
Linux: the refusal at 9,000 bubblewrap arguments says how many come from built-in write protection and what lowers each share. (#675)
Linux:getLinuxGlobPatternWarnings() also names a credentials.files deny whose pattern is skipped. (#669)
getFsWriteConfig() compiles a read rule once and reads brackets in linear time. (#718)
New
Per-command network allow lists:SandboxManager.registerCommandNetworkLists(commandId, { allowedDomains }) and unregisterCommandNetworkLists(commandId). (#591)
filterRequest can rewrite and observe. An allow may carry removeHeaders, setHeaders and onResponse; a deny may choose its status; the callback gets a second argument with scheme, sni, target, requestTarget and rawHeaders. New opt-in network options: stripResponseHeaders, refuseOpaqueTunnels, requireHostMatch. (#663)
TLS termination runs in-process on the tunnel's own socket, with no per-tunnel listener. New network.tlsTerminate options maxTunnels (default 256) and handshakeTimeoutMs (default 10000). (#665, #700)
srt proxy, also built as a single-file srt-proxy: the HTTP proxy on its own, with an external decider process that decides every request. See docs/srt-proxy.md. srt-proxy --version prints the version. (#666, #668)
network.ambiguousRequestTarget, and requestTarget, normalizedRequestTarget and requestTargetIsAmbiguous for the callback. (#698)
allowNestedUserNamespaces (Linux), for the configuration or for a single wrap. (#588)
A refused CONNECT says why: the 403's status phrase carries the reason, such as 403 host is not on the allow list. (#681)
checkDependencies() warns when the package is installed where a sandboxed command can write it. (#595)
README: what a --control-fd line changes, and reference entries for allowPty, bwrapPath, socatPath, seccomp, ripgrep, git.safeDirectories and credentials. (#578)
For whoever upgrades
Ask callback: only true allows. A wrapper that tests the callback's result for truthiness reads { allow: false, reason } as an allow. ISandboxManager has three new members: registerCommandNetworkLists, unregisterCommandNetworkLists, askCallbackDenyReason. (#591)
Linux, namespaces: see above. With the seccomp helper the refused calls fail with EPERM; with no helper in the chain bubblewrap disables user namespaces, which needs bubblewrap 0.8.0 or later that is not installed setuid. Where neither applies nothing limits namespaces, and a line under SRT_DEBUG says so. (#588, #617)
Linux, denyWrite over allowWrite: with denyWrite: ["~"] a project under ~ is read-only; with denyWrite: ["/tmp"] so is the sandbox's own TMPDIR. The command starts, and a write fails with Read-only file system. (#684)
Linux, worktrees and submodules: with one as the working directory, its .git file can be read and cannot be changed, moved or removed. git status, log and diff work as before. (#705)
Linux, after a hard kill (SIGKILL, OOM, power loss) the empty mount points made for absent deny paths stay on the host until someone removes them. (#673)
Linux: a wrap with nothing to walk waits its turn behind wraps that walk. (#679)
Credential files: an allowRead or allowWrite path at or beneath a credentials.files entry is not bound back or granted. On Windows a mode: "mask" file is read-denied, and a write grant beneath a denyWrite keeps read and loses write. (#680, #686)
macOS: a read entry applies under both spellings of its path, as written and with the links on the way resolved. (#680)
filterRequest: besides the ambiguous targets above, a # and what follows are cut off before the callback is asked, a target that is not valid UTF-8 or not in origin form gets 400, and TRACE and TRACK get 405. (#663, #698)
tlsTerminate: needs Node, or Bun 1.4 or later; on an older Bun initialize() throws. A CONNECT past maxTunnels gets 503. A tunnel that owes no response is closed after about 6 s, a handshake that does not finish after 10 s, and one whose request has not moved after 300 to 360 s. The sni passed to filterRequest is lower-cased. (#665, #700)
Glob patterns, four spellings read differently: a * or ? between brackets is a member of the set ([r*]eadme.txt matches readme.txt); brackets with nothing in them are text (list[].ts is that name); a [ that nothing closes is text; a name that spells __GLOBSTAR__ is that name. (#718)
Glob patterns, brackets that spell no set are text:[user-id] is a folder of that name. A valid set stays a set: **/[slug]/*.env does not match a folder really named [slug]. (#687)
Deep imports:globToRegex and denyGlobRegex moved from dist/sandbox/sandbox-utils.js to dist/sandbox/glob-pattern.js. Neither is a package export. (#718)