Summary
When a sandbox policy sets include_workdir: false and places the workdir (e.g. /sandbox) in read_only, the proxy-mode baseline enrichment in enrich_proto_baseline_paths() unconditionally adds /sandbox to read_write, defeating the policy's read-only intent.
Landlock grants the union of matching rules, so /sandbox appearing in both read_only and read_write results in full write access — the opposite of what the policy requested.
Related
Root Cause
The hardcoded proxy baseline in enrich_proto_baseline_paths():
PROXY_BASELINE_READ_WRITE: ["/sandbox", "/tmp"]
This adds /sandbox to read_write if it's not already in the read_write list. It does not check:
- Whether
include_workdir is false
- Whether the path is already in
read_only (indicating deliberate read-only intent)
Flow
- Gateway delivers policy:
/sandbox in read_only, include_workdir: false, rw: 4 paths
enrich_proto_baseline_paths() adds /sandbox to read_write → rw: 5 (logged as CONFIG:ENRICHED)
prepare() checks include_workdir — it's false, so it doesn't add workdir again (but it's already there from step 2)
- Ruleset built with
/sandbox in both read_only and read_write → Landlock union = read_write
restrict_self() enforces — Landlock is active but /sandbox is writable
Evidence from Testing (OpenShell 0.0.29, NemoClaw sandbox)
Landlock IS enforced (confirming #810 fix works):
$ touch /dev/shm/test → Permission denied (DAC 1777, not in any policy list)
$ touch /var/tmp/test → Permission denied (DAC 1777, not in any policy list)
But workdir is writable despite read_only policy intent:
$ touch /sandbox/test → OK (should be blocked)
$ touch /sandbox/.openclaw/test → OK (should be blocked)
$ touch /sandbox/.local/test → OK (should be blocked)
Policy delivered by gateway (correct):
include_workdir: false
read_only: [/sandbox, /sandbox/.openclaw, ...]
read_write: [/tmp, /dev/null, /sandbox/.openclaw-data, /sandbox/.nemoclaw] # 4 paths
Policy after enrichment (incorrect):
CONFIG:ENRICHED — ro:9 rw:5 (one path added to rw)
Startup log confirms no fallback to container-disk policy:
2026-04-21T16:59:25.188Z INFO openshell_sandbox: Fetching sandbox policy via gRPC
2026-04-21T16:59:25.210Z INFO openshell_sandbox: CONFIG:ENRICHED ...
2026-04-21T16:59:25.212Z INFO openshell_sandbox: CONFIG:PROBED ro:9 rw:5
Suggested Fix
In enrich_proto_baseline_paths(), before adding a baseline path to read_write:
- Check if
include_workdir is false and the path matches the workdir — if so, skip it
- Or: check if the path already exists in
read_only — if so, respect the explicit read-only intent and don't promote it
Option 2 is more general and handles cases beyond just the workdir.
Environment
- OpenShell: 0.0.29
- Kernel: Linux 6.8.0-1053-gcp (Landlock ABI v4)
- NemoClaw: alpha (prekshi/openshell-0.0.29-landlock branch)
Summary
When a sandbox policy sets
include_workdir: falseand places the workdir (e.g./sandbox) inread_only, the proxy-mode baseline enrichment inenrich_proto_baseline_paths()unconditionally adds/sandboxtoread_write, defeating the policy's read-only intent.Landlock grants the union of matching rules, so
/sandboxappearing in bothread_onlyandread_writeresults in full write access — the opposite of what the policy requested.Related
prepare()correctly respectsinclude_workdir: false, but the enrichment runs beforeprepare()and doesn't check the flag.Root Cause
The hardcoded proxy baseline in
enrich_proto_baseline_paths():This adds
/sandboxtoread_writeif it's not already in theread_writelist. It does not check:include_workdirisfalseread_only(indicating deliberate read-only intent)Flow
/sandboxinread_only,include_workdir: false,rw: 4pathsenrich_proto_baseline_paths()adds/sandboxtoread_write→rw: 5(logged asCONFIG:ENRICHED)prepare()checksinclude_workdir— it'sfalse, so it doesn't add workdir again (but it's already there from step 2)/sandboxin bothread_onlyandread_write→ Landlock union =read_writerestrict_self()enforces — Landlock is active but/sandboxis writableEvidence from Testing (OpenShell 0.0.29, NemoClaw sandbox)
Landlock IS enforced (confirming #810 fix works):
But workdir is writable despite read_only policy intent:
Policy delivered by gateway (correct):
Policy after enrichment (incorrect):
Startup log confirms no fallback to container-disk policy:
Suggested Fix
In
enrich_proto_baseline_paths(), before adding a baseline path toread_write:include_workdirisfalseand the path matches the workdir — if so, skip itread_only— if so, respect the explicit read-only intent and don't promote itOption 2 is more general and handles cases beyond just the workdir.
Environment