Skip to content

emrg: enforce workspace-write boundary in write/edit tools - #1092

Merged
argszero merged 1 commit into
masterfrom
feature/workspace-write-boundary
Sep 1, 2026
Merged

emrg: enforce workspace-write boundary in write/edit tools#1092
argszero merged 1 commit into
masterfrom
feature/workspace-write-boundary

Conversation

@argszero

@argszero argszero commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Summary

The write and edit tools only boundary-checked the read-only sandbox tier (check_read_only_file_write); under workspace-write they skipped validation entirely. An agent in a workspace-write session could write/edit anywhere outside the session cwd (e.g. a sibling directory), while the bash tool correctly blocked absolute-path escapes — a tool asymmetry that a workspace-write session could use to escape its sandbox.

Root cause

  • write_tool.py:70 / edit_tool.py:85 only called the read-only check when sandbox == "read-only". The workspace-write tier fell through to the raw path.write_text / path.edit with no boundary validation.
  • The daemon injects args["workspace"] = session cwd (daemon.py:2762), but the write/edit tools never consumed it under workspace-write.

Fix

Add check_workspace_write() in bash_tool.py mirroring the bash tool's workspace-write semantics, and call it from both tools' workspace-write branch:

  • Protected daemon files (~/.emrg/config.toml, emrgd.token, tasks.yml, projects.yml, rants.jsonl) → blocked
  • ~/.emrg itself → blocked (would erase the daemon's data dir)
  • Absolute path outside the workspace root (and not OS temp) → blocked
  • OS temp → allowed (mirrors dsh's workspace + backend-promised temp area)
  • Relative path → assumed in-workspace (cwd = workspace root), matching bash

The tools now pass the original file_path (not the resolved one) so relative paths keep the in-workspace assumption — resolving a relative path against the daemon cwd would wrongly classify it as absolute-outside-workspace.

This aligns with dsh's writableRoots single-source + tool-symmetry design (bash and fs write tools share one writable-root boundary).

Testing

  • +7 tests covering write/edit positive & negative workspace-write states (outside-workspace block, inside-workspace allow, OS-temp allow, protected-config block).
  • Reproduced the exact rant scenario (session cwd /Users/argszero/Downloads/1 writing to sibling /Users/argszero/Downloads/2/poem.txt) — now correctly blocked.
  • pytest tests/1207 passed, 1 skipped.
  • Agent.md doc-count guard synced 1200 → 1207.

…-09-01T15:10:23)

The write/edit tools only boundary-checked the 'read-only' sandbox tier;
under 'workspace-write' they skipped validation entirely, letting an agent
write/edit anywhere outside the session cwd while the bash tool correctly
blocked absolute-path escapes — a tool asymmetry.

Add check_workspace_write() in bash_tool (mirroring the bash tool's
workspace-write semantics: protected daemon files and ~/.emrg blocked,
absolute paths outside the workspace root blocked, OS temp allowed,
relative paths assumed in-workspace), and call it from the write/edit
tools' workspace-write branch. Pass the ORIGINAL file_path so relative
paths keep the in-workspace assumption.

Fixes the asymmetric hole and aligns with dsh's writableRoots single-source
+ tool-symmetry design. +7 tests (write/edit positive & negative states),
Agent.md doc-count 1200->1207.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 20260901-154922

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 20260901-155702

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle 20260901-160415

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant