Release v0.51.296 — stage-3731 (remote-workspace blocked-root security fix #3731) (#3744)
Some checks failed
Release & Docker / release (push) Has been cancelled
Some checks failed
Release & Docker / release (push) Has been cancelled
* fix: reject blocked roots for remote workspaces * test: cover remote blocked root subpaths * docs(changelog): v0.51.296 security fix + backfill v0.51.295 entries - v0.51.296: #3731 remote-workspace blocked-root rejection. - Backfill the v0.51.295 release block (the #3739 model-pick entry + promote the #3570 revert out of [Unreleased]) which a stage-rebuild dropped from the prior release's CHANGELOG. git-describe versioning makes CHANGELOG-after-tag acceptable. --------- Co-authored-by: hinotoi-agent <paperlantern.agent@gmail.com> Co-authored-by: nesquena-hermes <[email protected]>
This commit is contained in:
10
CHANGELOG.md
10
CHANGELOG.md
@@ -3,6 +3,16 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v0.51.296] — 2026-06-06 — Release JL (stage-3731 — remote-workspace blocked-root security fix)
|
||||
|
||||
### Security
|
||||
- **Remote-terminal workspace resolution now rejects blocked system roots.** `_remote_terminal_workspace_candidate()` returned early for paths under the configured remote terminal cwd *before* the blocked-root guard ran, so an SSH/remote terminal profile whose target-side cwd was a system directory (e.g. `/etc`) could have that root accepted as a local workspace — after which workspace file helpers (which treat `s.workspace` as a local `Path`) could read local system files. The blocked-root guard now runs for both the candidate and the base path, so registration and trusted-workspace resolution reject these roots consistently. (#3731, @Hinotoi-agent)
|
||||
|
||||
## [v0.51.295] — 2026-06-06 — Release JK (stage-3739/3742 — model-pick revert fix + session-status revert)
|
||||
|
||||
### Fixed
|
||||
- **The composer model picker no longer silently reverts your selection on send.** When you explicitly picked a model whose family differed from the active profile's provider (e.g. a `gpt-*` model under an `anthropic`-bound profile), the server's profile-aware resolution (v0.51.290, #3448) rewrote it to the profile default and the dropdown snapped back with no warning. An explicit pick is now honored across both resolution paths (the profile-provider branch and the legacy bare-prefix branch), and if the model genuinely must change (a real provider mismatch) a toast explains it instead of silently swapping. The legitimate stale-session repair path is preserved. (#3739 fixes #3737, @someaka)
|
||||
|
||||
### Removed
|
||||
- **Reverted the manual per-session status labels (Todo / In Progress / Done).** The feature added in v0.51.284 (#3570) stored the chosen status only in browser `localStorage`, keyed by session id, with no server-side backing — so labels silently did not persist across browsers or devices (a user who labeled sessions on one machine saw none of them after switching to a laptop). It also rendered the three statuses as flat top-level entries in the session context menu alongside Copy/Rename/Pin/etc., which crowded the root menu. Removed entirely for now (JS state + cycle logic, context-menu entries, sidebar badge render, CSS, and all locale strings); the feature can be reintroduced later with proper server-side persistence and a less intrusive menu treatment. (reverts #3570)
|
||||
|
||||
|
||||
@@ -93,6 +93,8 @@ def _remote_terminal_workspace_candidate(path: str | Path) -> Path | None:
|
||||
return None
|
||||
candidate = Path(raw).expanduser().resolve()
|
||||
base = Path(cwd).expanduser().resolve()
|
||||
if _is_blocked_workspace_path(candidate, raw) or _is_blocked_workspace_path(base, cwd):
|
||||
return None
|
||||
if candidate == base or _is_within(candidate, base):
|
||||
return candidate
|
||||
return None
|
||||
|
||||
@@ -55,3 +55,14 @@ def test_remote_terminal_workspace_paths_outside_cwd_still_reject(monkeypatch):
|
||||
|
||||
with pytest.raises(ValueError, match="Path does not exist"):
|
||||
workspace.resolve_trusted_workspace("/Users/other/projects/demo")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("workspace_path", ["/etc", "/etc/ssh"])
|
||||
def test_remote_terminal_workspace_system_roots_still_reject(monkeypatch, workspace_path):
|
||||
monkeypatch.setattr(api_config, "get_config", lambda: _remote_config(terminal={"backend": "ssh", "cwd": "/etc"}))
|
||||
|
||||
with pytest.raises(ValueError, match="Path points to a system directory"):
|
||||
workspace.validate_workspace_to_add(workspace_path)
|
||||
|
||||
with pytest.raises(ValueError, match="Path points to a system directory"):
|
||||
workspace.resolve_trusted_workspace(workspace_path)
|
||||
|
||||
Reference in New Issue
Block a user