Homebrew 7.0.0 shipped on September 13th with “stronger sandboxing” in the opening line of the release notes. Installation is being split into a fetch phase that has network access and writable caches, and an install phase that runs offline with read-only caches. Sandboxed builds are blocked from reading your home directory by default. Cask install hooks run confined, and a cask sandbox escape via LaunchServices was closed in the same release by denying app launching, Mach services, and Unix socket connections. Formula post_install and cask flight blocks, which are arbitrary Ruby today, are deprecated in favour of declarative *_steps delivered as signed data, with a removal deadline of December 2027.
Homebrew, which I help maintain, has run macOS formula builds under sandbox-exec since 2015. The last four months turned that from a build-hygiene measure into a stated security programme. 6.0.0 in June brought a Linux build sandbox based on Bubblewrap and made third-party taps require explicit trust before their Ruby is evaluated. By July the Bubblewrap implementation had been replaced with Landlock, which is built into the kernel and works inside unprivileged Docker, and by August Bubblewrap was removed. The 7.0.0 deprecation table lists the Linux sandbox opt-out variable as removed, with “no replacement opt-out”. A pull request opened four days before the release extends the sandbox to archive extraction and disk-image handling, and sandbox rule changes now require security review.
“Tap trust remains the primary protection against malicious third-party casks; sandboxing mainly limits accidental damage and adds installation safeguards. It cannot make untrusted software safe to run,” per the release notes. Vendor .pkg installers still execute outside the sandbox, and installed applications run with your privileges.
Install-time code can be gated (an allowlist controls whether it runs), confined (a sandbox restricts what it can reach), or replaced with data that is interpreted rather than executed. The past year’s allowlist rollout was gating. The sandbox proposals now open across issue trackers are confinement, and Homebrew’s *_steps migration pairs confinement with replacement. All three responses leave the same job open: validating the confined phase’s output.
Agents
Homebrew’s sandbox mechanisms come from the same small set of primitives the LLM coding agent vendors adopted over the past year. Claude Code’s sandboxed bash tool uses Seatbelt on macOS and bubblewrap on Linux, open-sourced as sandbox-runtime. Codex CLI uses Seatbelt and Landlock plus seccomp, and OpenAI built a custom restricted-token sandbox for Windows after evaluating and rejecting AppContainer. Cursor’s agent terminal uses Seatbelt and Landlock and runs the Linux sandbox inside WSL2 on Windows. Gemini CLI offers Seatbelt profiles or a container. The motivation is the one package manager maintainers cite: these tools run unreviewed code, and asking permission each time stopped working. Anthropic reports that users approve about 93% of permission prompts.
The two problems overlap beyond sharing a mechanism, because agents install packages. The Shai-Hulud worm that hit npm in September 2025 spread through postinstall scripts, and one of its persistence tricks was writing a hook into Claude Code’s settings file so it survived cache cleanup with the agent’s privileges. The nx compromise a month earlier shipped a postinstall payload that attempted to use locally installed AI tools to scan for credentials. Mandiant has since reported an attacker hijacking a coding assistant session and spreading Shai-Hulud across roughly 100 internal repositories through poisoned package recommendations; an agent that installs dependencies on your behalf multiplies both the install rate and the blast radius.
The crossover back into package managers is explicit in Homebrew’s brew sandbox-exec, which exposes its formula sandbox for arbitrary commands, and in its nested-sandbox handling, which exists, per the code comment, for running brew “inside an existing macOS sandbox (e.g. an agent’s)”. pnpm’s lead maintainer opened an issue in August proposing to wrap dependency lifecycle scripts in bubblewrap or Landlock on Linux and Seatbelt on macOS, the agent vendors’ exact stack, and the issue body ends with the note that it was written by an agent. A Deno maintainer’s hardening proposal from July names Landlock, seccomp, and Seatbelt as the planned follow-up.
Shipped
The list of package managers with OS-level confinement on by default is short, and most of it predates the agents. opam has sandboxed by default since 2.0 in 2018: every package’s build and install commands run under bubblewrap on Linux with the network cut and writes limited to the build directory, and under sandbox-exec on macOS with a deny-write, deny-network policy. Downloads happen outside the sandbox, which is the same fetch/build split Homebrew is now migrating to. opam on Windows runs unsandboxed, and this month opam’s maintainers opened an issue to investigate Landlock as a bubblewrap replacement, for the same Docker-compatibility reasons Homebrew switched.
Swift Package Manager compiles and runs Package.swift manifests inside a Seatbelt sandbox, and SE-0303 and SE-0332 gave plugins a no-network, temp-writes-only sandbox with declared permissions on top. The implementation file states what happens elsewhere: “On platforms that don’t support sandboxing, the command line is returned unmodified.” On Linux the sandbox is a no-op, a gap flagged in the tracker and acknowledged in the accepted proposal text.
Nix has run Linux builds in namespaces by default since 2.2 in 2019, and Guix builds in an equivalent isolated environment. Both projects document why: a 2024 Nix advisory states the sandbox “is not primarily intended as a security mechanism, but as an aid to improve reproducibility and purity of Nix builds”, and a 2025 pair of CVEs let a local user escalate through the build users of Nix, Lix, and Guix alike. The macOS Nix sandbox exists and is still off by default. Bazel is in the same category: build actions run in a namespace sandbox on Linux and under sandbox-exec on macOS, restricted to their declared inputs, the strictest read confinement in this section, with reproducibility as the stated goal.
The oldest example is Gentoo’s Portage sandbox, an LD_PRELOAD shim from the early 2000s whose own README calls it a QA measure, since static binaries and anything that clears the environment bypass it. Alongside it, Portage’s default FEATURES now include network, PID, and IPC namespace isolation for ebuild phases, and those are kernel boundaries.
apt splits the same way: download methods have run as a dedicated _apt user since apt 1.1, while the seccomp filter added in 1.6 has been disabled by default since 2018 for compatibility. dpkg maintainer scripts run as root, and confinement so far amounts to a wiki ideas page.
pacman 7.0 (2024) runs downloads as a dedicated user under Landlock and seccomp, a narrower confinement than the build sandboxes above, and 7.1 tightened it with syscall restrictions and finer-grained configuration. Alpine’s abuild rootbld builds packages in a bubblewrap sandbox where networking is granted only to packages declaring options=net, though it is opt-in. mise shipped opt-in Landlock and Seatbelt sandboxing for its task runner this year. RPM 4.20 added an unshare plugin that can strip network and filesystem access from scriptlets, and it ships disabled in every distribution.
CocoaPods shipped sandbox-pod in 2013, a wrapper that ran the whole pod install under sandbox-exec. The generated profile still contains (allow network-outbound) with a “TODO make this stricter if possible” comment, the revisit issue was closed by a stale bot in 2017, and the binary, shipped thirteen years before the current proposals and long abandoned, is still listed in the gemspec.
Deno is the only JavaScript package installer with any confinement, and its deny-by-default permission model is enforced in-process by the runtime rather than by the OS, with FFI as a documented way around it and a steady stream of permission-bypass advisories. The maintainers concede in their own hardening issue that an approved npm lifecycle script currently “executes with essentially full host access”, because the node shim inside it spawns deno run -A.
Allowlists
The run of supply chain attacks from nx through Shai-Hulud 2.0 produced a lot of shipped security work in JavaScript, all of it controlling which code runs rather than confining it. Every major client now blocks dependency lifecycle scripts by default behind an allowlist: pnpm led in January 2025, Bun had gated scripts since 2023, Yarn flipped the default in April 2026, and npm 12 followed in July. Cooldowns on newly published versions went from a proposal to on-by-default at pnpm, Yarn, and Deno within about a year, with most other ecosystems shipping opt-in equivalents.
An RFC titled “Make npm install scripts opt-in” was opened in November 2021, collected around 369 thumbs-up, and stayed open for four and a half years before being closed in favour of the 2026 RFC that npm 12 implements. GitHub’s immediate response to Shai-Hulud was entirely credential and publishing hardening; the default flip on scripts came eight months after the worm and five years after the original RFC.
I surveyed the allowlist mechanisms in June; an allowlist controls which packages get to run code, while a sandbox limits what that code can reach once it runs. The pnpm sandbox issue makes the case for the second axis directly: an allowlist is binary, and most packages with build scripts need to compile something in their own directory, which is a permission you can grant while withholding your SSH keys and the network; approving esbuild’s postinstall today extends that trust to every future version of it.
Proposals
The open proposals all date from the last fourteen months and specify the same primitives. Beyond pnpm’s and Deno’s, RubyGems has a December proposal to split bundle install into a download phase in which only Bundler’s own code runs, and an install phase that could run sandboxed with credentials stripped, and it has a maintainer in support: “I think this makes sense.” Spack’s April issue proposes Landlock and Seatbelt applied in-process between fetch and build, on a tracker whose original sandbox request from 2017 opened with “I could easily sneak a rm -rf ~ into a package.” Arch has a draft merge request giving install scriptlets a private network namespace, which CachyOS already ships.
Cargo’s sandbox issue has been open since 2018. The intervening years produced watt, Cackle, a pre-RFC from dtolnay written after serde_derive briefly shipped a precompiled binary, and an official 2024 project goal that ended with a specific negative result: WASI lacks process spawning, and build scripts exist to spawn pkg-config, C compilers, and git, so the WebAssembly approach fails. Then in August 2026 the arrayref attack delivered a typosquatted proc-macro dependency whose build.rs downloaded and ran a remote binary, with infrastructure Wiz links to DPRK campaigns. The issue filed in response was closed the same day as a duplicate of the 2024 allowlist thread. Zig’s proposal, from Andrew Kelley himself, is the most radical design on any tracker, compiling every build.zig to WebAssembly so it can only emit a build graph as data, on top of a fetch step that is already inert, and it has been open since January 2023.
pip’s “Sandbox the running of setup.py” was opened in 2017 by a PyPA maintainer and cites Homebrew’s sandbox.rb as the model to copy. It is still open, along with a 2022 privilege-drop issue and a 2026 AppArmor one. Poetry’s maintainers gave the flattest refusal in the survey: a post-xz request for Landlock or seccomp self-sandboxing was closed as out of scope on portability grounds. uv’s threat model places the local machine inside the trust boundary and leaves confinement out of scope, and its maintainers decline on principle to add install hooks in the first place. The only Python-adjacent tool with a sandbox on its public roadmap is pixi, which already ships conda link scripts disabled by default behind a setting whose opt-in value is spelled insecure.
Gradle’s sandbox mode issue has been open since 2019, Maven’s security page states that “the Maven security model assumes you trust the pom.xml” and isolation is your job, and JEP 486 permanently disabled the Security Manager in JDK 24, which removed the platform’s in-process sandbox primitive in the same year demand for one returned. NuGet’s 2020 issue about warning on code execution in MSBuild target files got a maintainer reply that removing it “will break many packages”, and was revived this July citing Shai-Hulud, with a commenter pointing at Yarn as the existence proof that fail-closed defaults are survivable. The C++ managers are absent from every category above: Conan recipes and vcpkg portfiles run arbitrary Python and CMake at install time, and I found no sandbox proposal on either tracker.
Removal
A second strategy has a better shipping record than sandboxing: removing the code execution entirely. Go and Elm omitted install hooks from the start. NuGet deleted install.ps1 with PackageReference in 2017, though MSBuild targets from packages still execute at build time, which real malware has used. Composer runs only the root project’s scripts and put plugins behind an allowlist in 2021. Julia’s Artifacts system replaced most build.jl usage with declarative content-addressed downloads. CPAN’s practical mitigation is x_static_install, which skips executing Makefile.PL for pure-Perl distributions. Cabal’s Hooks build type narrows arbitrary Setup.hs replacement to named hook points. The distributions are doing the same for scriptlets, with Fedora replacing useradd scripts with declarative sysusers.d metadata and Arch surveying scriptlet usage with declarative hooks as the goal. Homebrew’s *_steps migration combines removal with confinement: the hook becomes signed data, and the remaining executable steps run inside the sandbox.
Dart is the counterexample: pub’s install step historically consisted of placing files on disk. The native-assets build hooks stabilised this year add execution to that path, deferred to build time, in an environment the docs describe as semi-hermetic, with reproducibility as the sole stated goal.
Primitives
2026’s proposals name the same primitives because few exist. On Linux, Landlock is in default kernels across the major distributions, runs unprivileged without a daemon or container runtime, and versions its ABI so a client can apply whatever protections the running kernel offers, which is the compatibility posture a package manager needs. Homebrew, pacman, and mise ship it. opam and Spack have open proposals to adopt it, and every agent vendor’s Linux sandbox is built on Landlock, namespaces, or both. On macOS, Homebrew, Claude Code, and Chrome all run on Seatbelt, which Apple deprecated in 2016 and has neither removed nor replaced, a situation developers have formally asked Apple to resolve. Windows lacks a directly comparable primitive, which is why Codex built a custom sandbox from restricted tokens, Cursor and Claude Code confine through WSL2, and the pnpm proposal may add Windows support only later.
The agent problem and the package manager problem meet in CI, where a GitHub-hosted Actions job gets a virtual machine of its own and isolation ends at that boundary: every uses: step (itself a package install) and every package manager invoked inside the job shares the runner’s filesystem, environment, and secrets. The VM protects GitHub’s infrastructure, and everything the job values, secrets included, is inside it, so an install script runs with the same reach as the workflow that invoked it. Self-hosted runners are frequently persistent and shared, which removes even the per-job boundary. The shipped control is an allowlist over which actions may run, and confinement exists as Harden-Runner, a third-party agent that monitors and filters runner egress. CI is also where kernel sandboxes compose worst: inside a container: job, bubblewrap fails for want of privileged namespaces (opam has had the issue open since 2020), which is why Homebrew switched to Landlock and why Spack’s proposal applies restrictions in-process. The nx compromise started this way, with a workflow injection leaking the npm token that published the malicious versions.
The open boundary is the one I keep coming back to from the threat model work: the hand-off. A sandboxed build that writes an install manifest, a cache entry, or a step list which the package manager then acts on with full privileges has moved the trust decision to whatever validates that output, and Homebrew’s LaunchServices escape was a hand-off bug. The removal strategy is one answer: a signed step list or a build graph emitted as data has a schema, and the package manager interprets it instead of executing it. The proposals now open at pnpm, RubyGems, Deno, and Spack still have to specify what validates a sandboxed build’s output.