Summary
Brig is a tool for running AI coding agents safely. When you let an AI agent read and edit your code, you want it boxed off from the rest of your machine, so brig runs each agent inside what’s known as a sandbox. You can think of a sandbox as a locked room that is supposed to see only the project you point it at and nothing else. Brig mounts two things into that room: a working directory brig owns, and the specific project folder you name when you start a run. Technically, brig's strongest mode runs the agent in a microVM with hardware-level isolation, and that isolation was never the problem here.
The flaw (GHSA-wp6x-29qx-fpr7, tracked as ENDOR-VUL-2026-1809) found that while brig guarded the working directory carefully, it handed the project folder to the underlying runtime as a raw path, without checking where it actually pointed. Because the agent already has read-write access to that project folder, it can swap one of the subfolders for a shortcut (a symlink) aimed at any other directory on your machine. This could enable the agent to escape the sandbox and access the underlying system.
The shortcut does nothing on its own. The next time you narrow a run to that subfolder, which is the normal way to point an agent at one part of a monorepo, brig follows the shortcut on the host and mounts whatever it targets into the sandbox, read and write.
In plain terms, the agent picks a door elsewhere on your computer and unlocks it on the next run. The impact is high: the sandbox can then reach files it was never given, including SSH keys, cloud credentials, and other projects, all with the permissions of the person running brig, and writing to the right file turns that into full control of the account. It is fixed in brig 0.3.0, which now resolves the path safely before mounting it.
Affected versions
I confirmed read and write on two configurations: macOS 15 on arm64 with brig 0.2.0 from Homebrew on the hull backend over Virtualization.framework (the microVM backend that carries brig's strongest isolation claim), and Ubuntu 24.04 on arm64 with nerdctl over containerd. The escape works the same way on both, which matters, because it means the strongest isolation mode does not change the outcome.
Details
brig runs AI agents in a sandbox and mounts two things into it: the guest home, which brig creates and controls, and a project directory you name on the command line. The workspace share is guarded carefully. It gets a trusted-prefix descent, a per-component symlink refusal, an identity check after each open, and a re-check right before the path reaches the runtime. The project share got one check: refuse a path that resolves to /.
That gap is the whole finding. brig passed the project path to the virtual machine monitor as it was typed, without resolving it, and the monitor resolved the path itself, on the host, outside the sandbox. Every directory at or below the project is the sandbox's to rewrite, because the project is mounted read-write. So an agent replaces a subdirectory with a symlink to any other directory on the host, and the next run that names that subdirectory hands the unresolved path to the monitor, which follows the link and exports the target read-write into the sandbox.
The maintainers classified it as CWE-59 (improper link resolution before file access), with brig acting as a confused deputy. I agree with that framing, and it is worth being precise about what does not happen here. This is not a hypervisor escape and not a guest-kernel escape. Both boundaries hold the entire time. brig asks the monitor to export the directory, so the isolation the sandbox provides is not what fails. The confused deputy is brig itself.
The two-run sequence
The reason this is reachable is a two-run sequence, and both operator commands are ordinary documented usage. The agent's only action is planting the link, and it runs with the agent's own privileges inside the sandbox, which is the entire premise of running an agent at all. Here is the full reproduction:
# setupmkdir -p ~/lab/monorepo/frontendecho 'console.log(1)' > ~/lab/monorepo/frontend/app.jsmkdir -p ~/escape-targetecho "HOST-PROOF-1 $(openssl rand -hex 32)" > ~/escape-target/proof.txt# 1. the operator starts the agent on the repositorybrig run claude ~/lab/monorepo -d# 2. baseline: the sandbox cannot reach the targetbrig sh claude sh -c "cat $HOME/escape-target/proof.txt"# 3. the agent plants one symlink inside the project it was givenbrig sh claude sh -c "rm -rf /work/monorepo/frontend && ln -s $HOME/escape-target /work/monorepo/frontend"# 4. the operator later runs the agent on that subdirectorybrig run claude ~/lab/monorepo/frontend -d# 5. the sandbox reads and writes the hostbrig sh claude sh -c 'cat /work/frontend/proof.txt'brig sh claude sh -c 'echo "HOST-PROOF-2 written from inside the brig sandbox" > /work/frontend/proof2.txt'# 6. observed on the hostcat ~/escape-target/proof2.txt
The elegant part, and the reason it is easy to miss, is step 3's control. Inside the guest the planted link dangles, because /Users/<user>/escape-target does not exist in the guest namespace. The agent gains nothing in the run that plants it. The link only means something when brig resolves it on the host, which brig does at step 4, in a later run, on behalf of an operator who typed a path that looks entirely normal.
What the sandbox reaches
Once the link is followed, the sandbox reads and writes any host directory it chose, after the fact, with the privileges of the user running brig. On the macOS microVM backend I confirmed reading and writing a directory that was never named on any brig command line:
$ brig info claude | grep -E 'ISOLATION|PROJECT'ISOLATION microVM (hull, hvi backend)PROJECT /Users/pkennedy/lab/monorepo/frontend (read-write, mounted at /work/frontend)$ brig sh claude sh -c 'cat /work/frontend/proof.txt'HOST-PROOF-1 860d552515d635d7a0cf1d845928f8b75d159994b6d5337376e5e9e78c1cda52$ brig sh claude sh -c 'echo "HOST-PROOF-2 ..." > /work/frontend/proof2.txt; echo rc=$?'rc=0$ cat ~/escape-target/proof2.txtHOST-PROOF-2 written from inside the brig sandbox at 2026-09-18T15:08:54Z
On Linux, with the link pointed at the host user's home, the sandbox's own /proc/self/mountinfo shows /home/dev mounted at /work/frontend, and from there I read the host SSH private key that was never given to the sandbox and brig's own session index, and wrote back to the host.
The reachable set is the point. ~/.ssh, ~/.aws, ~/.config/gh/hosts.yml, brig's own state, and the guest home of every other session are all one symlink away. Write access to ~/.ssh/authorized_keys or a shell startup file converts this to code execution as the host user. The credential argument in brig's own security documentation rests on a narrow blast radius of one guest home and one token. This removes the radius: an agent handed a fine-grained token scoped to a single repository reaches every credential on the machine. Neither --network isolated nor --network offline affects it, because the escape is a filesystem share, not a network path.
I also pointed the link at /proc and confirmed that a write to /work/<name>/sys/kernel/core_pattern returns success, which is a host root command-execution primitive. I did not fire a payload through it, because core_pattern is not namespaced and my test host was nested, so I reported that step as inferred rather than demonstrated. Being clear about the line between what I proved and what I inferred is part of the report.
The root cause
The vulnerable code computes the resolved path, uses it for a single check, and then throws it away:
eal, err := filepath.EvalSymlinks(abs)// ... refuse only if real resolves to the filesystem root ...// Resolved to judge it, and the path as typed is what gets mounted.c.Project = absc.GuestProject = GuestProject(abs)
c.Project holds the path as typed. Both runtime adapters concatenate that string into a mount specification and let another process resolve it later. A comment above the share-building code recorded the reasoning that made this look safe: the project is a directory the user named, not one brig created, so the swap the workspace check defends against was assumed to have nobody to perform it. That assumption was the bug. The sandbox has had the project read-write for the whole session, so the swap has exactly the same actor the workspace check already defends against. The project needed the workspace's control and did not have it.
The fix
The maintainers fixed it in 0.3.0 by reaching the project the same way the guest home already was. The path is split at the first directory the invoking user can write. Above the split, entries are not the sandbox's to change, so that part is opened by name and legitimate system links keep working. From there down, brig descends one component at a time against the directory it already holds open, refuses every symlink, and confirms after each open that what it opened is what it looked at. The boot descends a second time and holds the directory open across the handover to the runtime, because the share is a string the runtime resolves on its own schedule.
Two details of the fix are worth calling out. A symlink anywhere below the split is refused, not only one at the directory being named, because the link can sit at any component of the path. And a link the operator created is refused too, because brig cannot distinguish it from a planted one, so the rule is about links rather than about who made them. The PROJECT row in the run envelope now reports the directory the run resolved to, rather than the path as typed, which closes the gap where an operator who checked the envelope was shown the name they had typed rather than where it led.
If you cannot upgrade yet, resolve the project path yourself and name the resolved directory, which leaves no link for the runtime to follow:
brig run claude "$(cd ~/lab/monorepo/frontend && pwd -P)"
Checking the last component is not enough on its own, because the link can sit at any component. Treat any project whose path passes through a directory a sandbox has mounted read-write as suspect.
Disclosure
This was a genuinely good vendor interaction. I reported the finding with a complete reproduction and the analysis of the two-run sequence that makes it reachable. The brig maintainers triaged it internally, reproduced the defect with a regression test against the unfixed code, and confirmed it under their own security model, framing the invariant precisely: a user who mounts a subdirectory of a previously shared project must not gain access to an unrelated host directory because an entry in the sandbox was replaced with a symlink.
We had a substantive, respectful exchange on two points. The maintainers proposed retitling from my original "sandbox escape" framing to "project-mount symlink traversal," on the grounds that the microVM isolation did not fail and the phrase would imply it did. They were right, and I concurred.
We also compared CVSS scores. The only difference between my score and theirs was Privileges Required: running code inside a running sandbox is what brig is for, but it is still a required level of access, so PR:L is the honest call, and both of us landed on High regardless. I agreed with the score and the reclassification.
The maintainers accepted the report, requested a CVE, and published under coordinated disclosure. The full advisory is GHSA-wp6x-29qx-fpr7, and it is part of Endor Labs' vulnerability research, tracked as ENDOR-VUL-2026-1809 on the vulnerability reporting transparency page.
Takeaway
The lesson here is not about symlinks specifically. It is that a security control has to cover every path that reaches the same sink, not just the path someone thought to harden. brig built a careful, correct control for the workspace share and a second, weaker standard for the project share, and the two mounts end up in the same place: a host path concatenated into a mount spec that another process resolves. The attacker went through the door that was left with one lock. Two paths to the same dangerous operation should share one implementation, so that hardening one hardens both. That is exactly what the 0.3.0 fix does.
What's next?
When you're ready to take the next step in securing your software supply chain, here are 3 ways Endor Labs can help:









