fix(webui): respect full access in file preview
This commit is contained in:
@@ -28,7 +28,7 @@ def file_preview_payload(
|
|||||||
scope: WorkspaceScope,
|
scope: WorkspaceScope,
|
||||||
max_bytes: int = MAX_FILE_PREVIEW_BYTES,
|
max_bytes: int = MAX_FILE_PREVIEW_BYTES,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Return a text preview for a file inside the session workspace."""
|
"""Return a text preview for a file allowed by the session workspace scope."""
|
||||||
|
|
||||||
path = _clean_preview_path(raw_path)
|
path = _clean_preview_path(raw_path)
|
||||||
if not path:
|
if not path:
|
||||||
@@ -40,7 +40,7 @@ def file_preview_payload(
|
|||||||
resolved = resolve_allowed_path(
|
resolved = resolve_allowed_path(
|
||||||
path,
|
path,
|
||||||
workspace=scope.project_path,
|
workspace=scope.project_path,
|
||||||
allowed_root=scope.project_path,
|
allowed_root=scope.project_path if scope.restrict_to_workspace else None,
|
||||||
strict=True,
|
strict=True,
|
||||||
)
|
)
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
|
|||||||
@@ -2914,7 +2914,11 @@ def test_handle_file_preview_rejects_paths_outside_workspace(tmp_path) -> None:
|
|||||||
outside = tmp_path / "secret.py"
|
outside = tmp_path / "secret.py"
|
||||||
outside.write_text("secret = True\n", encoding="utf-8")
|
outside.write_text("secret = True\n", encoding="utf-8")
|
||||||
|
|
||||||
gateway = _basic_handler(MagicMock(), workspace_path=workspace)
|
gateway = _basic_handler(
|
||||||
|
MagicMock(),
|
||||||
|
workspace_path=workspace,
|
||||||
|
default_restrict_to_workspace=True,
|
||||||
|
)
|
||||||
gateway.tokens.api_tokens["tok"] = time.monotonic() + 300.0
|
gateway.tokens.api_tokens["tok"] = time.monotonic() + 300.0
|
||||||
key = "websocket:file-preview"
|
key = "websocket:file-preview"
|
||||||
enc = quote(key, safe="")
|
enc = quote(key, safe="")
|
||||||
@@ -2928,6 +2932,39 @@ def test_handle_file_preview_rejects_paths_outside_workspace(tmp_path) -> None:
|
|||||||
assert resp.status_code == 403
|
assert resp.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
def test_handle_file_preview_allows_paths_outside_workspace_in_full_access(tmp_path) -> None:
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
|
from websockets.datastructures import Headers
|
||||||
|
from websockets.http11 import Request
|
||||||
|
|
||||||
|
workspace = tmp_path / "workspace"
|
||||||
|
workspace.mkdir()
|
||||||
|
outside = tmp_path / "notes.py"
|
||||||
|
outside.write_text("value = 42\n", encoding="utf-8")
|
||||||
|
|
||||||
|
gateway = _basic_handler(
|
||||||
|
MagicMock(),
|
||||||
|
workspace_path=workspace,
|
||||||
|
default_restrict_to_workspace=False,
|
||||||
|
)
|
||||||
|
gateway.tokens.api_tokens["tok"] = time.monotonic() + 300.0
|
||||||
|
key = "websocket:file-preview"
|
||||||
|
enc = quote(key, safe="")
|
||||||
|
req = Request(
|
||||||
|
f"/api/sessions/{enc}/file-preview?path={quote(str(outside), safe='')}",
|
||||||
|
Headers([("Authorization", "Bearer tok")]),
|
||||||
|
)
|
||||||
|
|
||||||
|
resp = gateway.http._handle_file_preview(req, enc)
|
||||||
|
|
||||||
|
assert resp.status_code == 200
|
||||||
|
body = json.loads(resp.body.decode())
|
||||||
|
assert body["path"] == str(outside.resolve())
|
||||||
|
assert body["display_path"] == outside.resolve().as_posix()
|
||||||
|
assert body["content"].splitlines() == ["value = 42"]
|
||||||
|
|
||||||
|
|
||||||
def test_handle_webui_thread_get_backfills_legacy_missing_user_rows(
|
def test_handle_webui_thread_get_backfills_legacy_missing_user_rows(
|
||||||
tmp_path,
|
tmp_path,
|
||||||
monkeypatch,
|
monkeypatch,
|
||||||
|
|||||||
Reference in New Issue
Block a user