Summary
When the filesystem config specifies denyRead and allowRead on overlapping regions, allowWrite paths nested under an allowRead ancestor silently become read-only. The sandbox command fails with EROFS or Read-only file system on write operations that should be permitted.
Reproduction
Config
import { wrapCommandWithSandboxLinux } from './src/sandbox/linux-sandbox-utils.js'
const wrapped = await wrapCommandWithSandboxLinux({
command: 'touch /tmp/srt-test/workspace/project/hello.txt',
needsNetworkRestriction: false,
readConfig: {
denyOnly: ['/tmp/srt-test'],
allowWithinDeny: ['/tmp/srt-test/workspace'],
},
writeConfig: {
allowOnly: ['/tmp/srt-test/workspace/project'],
denyWithinAllow: [],
},
})
Setup
mkdir -p /tmp/srt-test/workspace/project
Command
eval "$wrapped"
# → touch: cannot touch '/tmp/srt-test/workspace/project/hello.txt': Read-only file system
Expected vs Actual Behavior
Expected: /tmp/srt-test/workspace/project/hello.txt is created successfully. allowWrite paths should remain writable regardless of allowRead configuration.
Actual: The write fails with Read-only file system. The allowWrite path is silently downgraded to read-only.
Root Cause
In generateFilesystemArgs() (src/sandbox/linux-sandbox-utils.ts), when processing a denyRead directory, the function:
- Mounts
--tmpfs over the denied path (wiping everything under it)
- Re-binds
allowWrite paths with --bind (writable)
- Re-allows
allowRead paths with --ro-bind (read-only)
Since bwrap processes mounts in argument order, a later mount on a parent path hides earlier mounts on child paths. When allowRead is a parent of allowWrite, step 3's --ro-bind overwrites step 2's --bind:
--tmpfs /tmp/srt-test
--bind /tmp/srt-test/workspace/project /tmp/srt-test/workspace/project
--ro-bind /tmp/srt-test/workspace /tmp/srt-test/workspace ← HIDES the --bind above
Fix
Swap the loop order: emit allowRead ro-binds before allowWrite re-binds. Write mounts then layer on top and remain writable.
Environment
- OS: Linux (any distro with bubblewrap)
- bwrap version: any (mount ordering is fundamental to bwrap's design)
- sandbox-runtime: current
main
Summary
When the filesystem config specifies
denyReadandallowReadon overlapping regions,allowWritepaths nested under anallowReadancestor silently become read-only. The sandbox command fails withEROFSorRead-only file systemon write operations that should be permitted.Reproduction
Config
Setup
Command
Expected vs Actual Behavior
Expected:
/tmp/srt-test/workspace/project/hello.txtis created successfully.allowWritepaths should remain writable regardless ofallowReadconfiguration.Actual: The write fails with
Read-only file system. TheallowWritepath is silently downgraded to read-only.Root Cause
In
generateFilesystemArgs()(src/sandbox/linux-sandbox-utils.ts), when processing adenyReaddirectory, the function:--tmpfsover the denied path (wiping everything under it)allowWritepaths with--bind(writable)allowReadpaths with--ro-bind(read-only)Since bwrap processes mounts in argument order, a later mount on a parent path hides earlier mounts on child paths. When
allowReadis a parent ofallowWrite, step 3's--ro-bindoverwrites step 2's--bind:Fix
Swap the loop order: emit
allowReadro-binds beforeallowWritere-binds. Write mounts then layer on top and remain writable.Environment
main