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
The cowork bwrap sandbox uses a fixed set of default RO mounts (see scripts/cowork-vm-service.js:1299-1321):
/usr, /etc (always)
/bin, /lib, /lib64, /sbin (on non-merged-usr distros)
coworkBwrapMounts.additionalROBinds and additionalBinds allow user mounts. The forbidden set is {/, /proc, /dev, /sys} — none of the default RO mount points are in it. So both forms accept entries that target a default mount as their dst:
String form (pre-existing):"/etc" (RO only — RW would fail the not-under-$HOME rule, RO has no such constraint).
In mergeBwrapArgs, additional binds are appended to defaultArgs, so the user's --ro-bind … /etc is honored over the earlier default --ro-bind /etc /etc. The system view of /etc is silently replaced inside the sandbox — a non-obvious config footgun, not a sandbox escape.
Proposed fix
Add a doctor-side _warn in _doctor_check_bwrap_mounts (scripts/doctor.sh), parallel to the existing disabledDefaultBinds critical-mount warning. Sketch from @RayCharlizard's PR #531 review:
# scripts/doctor.sh — after the rw_binds rendering loop (~line 272)local dst_warned=false
forlinein$ro_binds$rw_binds;do
[[ $line==*' -> '* ]] ||continuelocal dst=${line##* -> }case$dstin
/usr|/usr/*|/etc|/etc/*|/bin|/bin/*|/sbin|/sbin/*|/lib|/lib/*|/lib64|/lib64/*)
_warn "Mount dst '${dst}' shadows a default sandbox mount" \
'(may break system tools inside the sandbox)'
dst_warned=true
;;
esacdone
Note: Travis's sketch only catches the object form (the -> separator). The string form "/etc" is rendered as just /etc by the fmt() helper in PR #531 — the same case-statement needs to apply to bare paths too. Will fold both into the implementation.
Acceptance
_doctor_check_bwrap_mounts warns when an additional RO/RW bind's dst is /usr, /etc, /bin, /sbin, /lib, /lib64, or any subpath thereof.
Triggers for both string form ("/etc") and object form ({src:'/x', dst:'/etc'}).
No false positives on /tmp, /run, $HOME/..., or /sandbox/....
BATS coverage in tests/cowork-bwrap-config.bats for each shadow case + a clean case.
Follow-up from PR #531 review.
Problem
The cowork bwrap sandbox uses a fixed set of default RO mounts (see
scripts/cowork-vm-service.js:1299-1321):/usr,/etc(always)/bin,/lib,/lib64,/sbin(on non-merged-usr distros)coworkBwrapMounts.additionalROBindsandadditionalBindsallow user mounts. The forbidden set is{/, /proc, /dev, /sys}— none of the default RO mount points are in it. So both forms accept entries that target a default mount as theirdst:{ src: "/home/u/fake-etc", dst: "/etc" }passes validation."/etc"(RO only — RW would fail the not-under-$HOMErule, RO has no such constraint).In
mergeBwrapArgs, additional binds are appended todefaultArgs, so the user's--ro-bind … /etcis honored over the earlier default--ro-bind /etc /etc. The system view of/etcis silently replaced inside the sandbox — a non-obvious config footgun, not a sandbox escape.Proposed fix
Add a doctor-side
_warnin_doctor_check_bwrap_mounts(scripts/doctor.sh), parallel to the existingdisabledDefaultBindscritical-mount warning. Sketch from @RayCharlizard's PR #531 review:Note: Travis's sketch only catches the object form (the
->separator). The string form"/etc"is rendered as just/etcby thefmt()helper in PR #531 — the same case-statement needs to apply to bare paths too. Will fold both into the implementation.Acceptance
_doctor_check_bwrap_mountswarns when an additional RO/RW bind'sdstis/usr,/etc,/bin,/sbin,/lib,/lib64, or any subpath thereof."/etc") and object form ({src:'/x', dst:'/etc'})./tmp,/run,$HOME/..., or/sandbox/....tests/cowork-bwrap-config.batsfor each shadow case + a clean case.docs/CONFIGURATION.md(Issue 2 from feat(bwrap): support {src, dst} mount form in coworkBwrapMounts #531 review).Written by Claude Opus 4.7 via Claude Code