fix(workspace): make folder selection reliable - #6100
Conversation
|
Changed-file classification: UI-sensitive. Author-reported runtime / visual state
Checkboxes are author attestations. GitHub Actions results remain the execution evidence for CI; this check does not prove that a local command ran. This comment updates automatically when the description or changed files change. |
88d1c27 to
1fed344
Compare
Route foreground Bash, Python, tmux sessions, and detached jobs through one positive-mount bubblewrap profile. Clear inherited environment and network access, protect credentials and repository metadata, hide Odysseus data roots, and apply bounded resources while preserving one writable workspace.
1fed344 to
d6491f7
Compare
d6491f7 to
2af64a0
Compare
There was a problem hiding this comment.
Pull request overview
Makes workspace selection server-owned and reliable while incorporating the stacked Linux process-sandbox foundation.
Changes:
- Adds persisted workspace validation, creation, synchronization, and picker UI.
- Introduces Bubblewrap, seccomp, and brokered-network process isolation.
- Expands regression coverage across workspace and sandbox behavior.
Reviewed changes
Copilot reviewed 47 out of 47 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| THREAT_MODEL.md | Documents sandbox behavior. |
| tests/test_workspace_picker.py | Tests picker workflows and persistence. |
| tests/test_workspace_confine.py | Extends workspace confinement tests. |
| tests/test_seccomp_policy.py | Verifies generated seccomp policy. |
| tests/test_seccomp_launcher.py | Tests launcher security and failures. |
| tests/test_seccomp_generator.py | Tests architecture verification. |
| tests/test_sandbox_network_policy.py | Tests network-policy propagation. |
| tests/test_foreground_model_routing.py | Tests web-toggle network mapping. |
| tests/test_agent_bash_windows.py | Tests unsupported-platform blocking. |
| tests/seccomp_probe.c | Probes runtime syscall restrictions. |
| static/style.css | Styles picker status and controls. |
| static/js/workspace.js | Implements server-synchronized picker behavior. |
| static/js/slashCommands.js | Integrates workspace persistence with commands. |
| static/js/chat.js | Synchronizes workspace before chat actions. |
| src/tool_schemas.py | Updates workspace tool description. |
| src/tool_index.py | Updates indexed tool description. |
| src/tool_execution.py | Adds workspace and network-policy enforcement. |
| src/teacher_escalation.py | Propagates sandbox network profiles. |
| src/execution_sandbox.py | Constructs the process sandbox. |
| src/constants.py | Defines managed workspace and log paths. |
| src/bg_monitor.py | Restores background-job network policy. |
| src/bg_jobs.py | Sandboxes detached jobs. |
| src/agent_tools/subprocess_tools.py | Sandboxes Bash, Python, and tmux. |
| src/agent_tools/filesystem_tools.py | Updates workspace status wording. |
| src/agent_loop.py | Propagates sandbox network profiles. |
| setup.py | Creates the managed workspace during setup. |
| security/seccomp/README.md | Documents seccomp generation and installation. |
| security/seccomp/policy.json | Defines sandbox syscall policy. |
| security/seccomp/odysseus-seccomp-launcher.c | Implements the trusted launcher. |
| security/seccomp/Makefile | Builds and installs the launcher. |
| security/seccomp/generated_inner_policy.h | Supplies generated syscall allowlists. |
| security/seccomp/generate.py | Generates deterministic policy artifacts. |
| security/egress/README.md | Documents brokered egress. |
| security/egress/odysseus_egress_bridge.py | Bridges sandbox traffic to the broker. |
| security/egress/Makefile | Installs trusted egress helpers. |
| routes/workspace_routes.py | Adds workspace persistence and creation APIs. |
| routes/chat_routes.py | Maps web preference to network policy. |
| Dockerfile | Installs sandbox dependencies and helpers. |
| docker-compose.yml | Applies the outer seccomp profile. |
| docker-compose.gpu-nvidia.yml | Applies seccomp to NVIDIA deployment. |
| docker-compose.gpu-amd.yml | Applies seccomp to AMD deployment. |
| app.py | Creates the default workspace at startup. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| resolved, _reason = validate_workspace(raw) | ||
| return resolved |
| export function whenWorkspaceReady() { | ||
| if (!_workspaceReadyPromise) _workspaceReadyPromise = _syncServerWorkspace(); | ||
| return _workspaceReadyPromise; |
| else: | ||
| # Non-POSIX fallback retains the lexical precheck and canonical | ||
| # postcheck around this intentionally admin-selected managed path. | ||
| Path(candidate).mkdir(mode=0o700, parents=True, exist_ok=True) |
| _save_selected_workspace(owner, "") | ||
| return {"ok": True, "path": "", "default_path": ensure_default_workspace()} |
| if session_id and not IS_WINDOWS and shutil.which("tmux"): | ||
| stdout, stderr, rc, timed_out = await _run_tmux_bash( | ||
| content, | ||
| session_id=str(session_id), | ||
| cwd=agent_cwd(), | ||
| env=_subproc_env, | ||
| cwd=workspace, | ||
| timeout=DEFAULT_BASH_TIMEOUT, | ||
| progress_cb=progress_cb, | ||
| network_profile=network_profile, | ||
| ) |
| These are open, acknowledged, and contributor help is welcome: | ||
|
|
||
| 1. **No shell/filesystem sandbox.** The agent `bash` and `read_file`/`write_file` tools run as the app process user with no network egress filtering or filesystem confinement. A successful prompt-injection reaching a shell-enabled admin session can make outbound requests to internal services. See #1058 for the sandbox proposal. | ||
| 1. **Linux sandbox portability.** Agent `bash`, Python, tmux, and detached background commands run through a networkless bubblewrap profile with a cleared environment, private temp/home, a single writable workspace, credential-path overlays, read-only `.git` metadata, resource limits, and explicit hiding of Odysseus data/log roots even when they sit below a broader selected workspace. The Docker image includes bubblewrap. Sandboxed process execution fails closed when that profile is unavailable; a portable equivalent for non-Linux hosts is not implemented yet. The sandbox intentionally omits `/proc`, so commands that require process inspection degrade rather than gaining access to the app process namespace. |
Summary
Make workspace selection one server-owned transaction instead of a browser-local hint. Use this folder now validates the current typed value without requiring Enter, reports exact failures, offers safe creation for missing folders below a managed default workspace, refreshes the selected folder before persisting it, and keeps the signed-in user's selection consistent across browsers and computers.
Startup and setup defensively create the named default workspace under the application data root. The picker opens there, lists its folders, adds a New folder action and visible selection status, and updates the shell wording for the process-sandbox core contract in #6120. Server validation rejects broad, sensitive, application-data, malformed, and unsafe symlink paths at both the picker and execution boundaries.
Stack
This PR is a direct sibling follow-up to the process-sandbox core in PR #6120. Its implementation consumes that core contract for workspace validation and policy reporting; it does not inherit #6084, #5819, or #5821.
After #6119, #6118, and PR #6120 land, this branch should be restacked onto current
devso reviewers see only the picker delta. This PR remains a draft until running-app desktop/mobile evidence is attached, and the restack is pending.Target branch
dev, notmain. All PRs land indev;mainis curated by the maintainer at each release. If your PR is onmainby accident, click "Edit" on this PR and change the base.Linked Issue
Fixes #6099
Part of #6091
Part of #5815
Depends on PR #6120
Related: #3104, #5914, and Discussion #4438
Type of Change
Checklist
devdocker compose uporuvicorn app:app) and verified the change works end-to-end. Type-checks and unit tests are not enough.How to Test
python -m pytest -q tests/test_workspace_picker.py tests/test_workspace_confine.py tests/test_execution_sandbox.py tests/test_tool_path_confinement.py tests/test_slash_autocomplete_static.py tests/test_session_context_excludes_slash.py; the validated exact head reports 101 passing tests.node --check static/js/workspace.js,node --check static/js/chat.js,node --check static/js/slashCommands.js, Python compilation for the changed Python modules, andgit diff --check origin/dev...HEAD; each should exit successfully.Focused exact-head validation passed 101 tests. A network-disabled full-suite run on the preceding picker head reached 5,782 passed and 11 skipped with three failures that reproduced unchanged on clean current
dev: the runner omits.env.example, one mocked integration test performs DNS validation with networking disabled, and an existing Node browser-smoke fixture does not definenavigator. The final follow-up only sanitizes public errors, documents the already-validated admin path sinks for code scanning, and adds two focused regressions; hosted pytest is the full-suite evidence for the final head.Not run: live application interaction, Docker/native runtime, desktop/mobile rendering, or screenshot/clip capture.
Visual / UI changes — REQUIRED if you touched anything that renders
This changes workspace-picker controls, inline status, and folder-creation interaction. Running-app visual evidence is required before marking the PR ready for review.
Screenshots / clips
Pending running-app desktop/mobile capture of existing-folder selection, missing-folder creation, visible disabled reason, and second-browser synchronization.