Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions tests/integration/test_vision_docker_resolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
* a tmpfs ``/workspace`` file with no host path at all, and
* a root-owned mode-600 file the host user cannot read,

both delivered by the resolver's ``base64`` exec-read fallback
(``tools/image_source._resolve_container_fallback``). This is the same path
both delivered by the resolver's ``base64`` exec-read path
(``tools/image_source._resolve_sandbox_file``). This is the same path
that provides terminal-backend confinement for GHSA-gpxw-6wxv-w3qq: under a
non-local backend a non-cache path is read INSIDE the container, never on the
host.
Expand All @@ -20,6 +20,7 @@

Run: pytest -m integration tests/integration/test_vision_docker_resolve.py
"""

import base64
import shutil
import subprocess
Expand All @@ -32,9 +33,12 @@ def _docker_available() -> bool:
if shutil.which("docker") is None:
return False
try:
return subprocess.run(
["docker", "info"], capture_output=True, timeout=5
).returncode == 0
return (
subprocess.run(
["docker", "info"], capture_output=True, timeout=5
).returncode
== 0
)
except (subprocess.TimeoutExpired, OSError):
return False

Expand Down Expand Up @@ -106,7 +110,8 @@ async def test_resolves_tmpfs_workspace_file(docker_backend):

_write_png_in_container(docker_backend, "/workspace/shot.png")
res = await resolve_image_source(
"/workspace/shot.png", ResolveContext(task_id=docker_backend._task_id))
"/workspace/shot.png", ResolveContext(task_id=docker_backend._task_id)
)
assert res.origin == "container"
assert res.mime == "image/png"
assert res.data == _TINY_PNG
Expand All @@ -119,7 +124,8 @@ async def test_resolves_root_owned_mode600_file(docker_backend):

_write_png_in_container(docker_backend, "/workspace/secret.png", mode="600")
res = await resolve_image_source(
"/workspace/secret.png", ResolveContext(task_id=docker_backend._task_id))
"/workspace/secret.png", ResolveContext(task_id=docker_backend._task_id)
)
assert res.origin == "container"
assert res.mime == "image/png"
assert res.data == _TINY_PNG
Expand Down Expand Up @@ -152,6 +158,7 @@ async def test_host_secret_path_reads_container_not_host(docker_backend, tmp_pat
# never returns the HOST bytes.
with pytest.raises(ImageResolutionError) as excinfo:
await resolve_image_source(
str(host_secret), ResolveContext(task_id=docker_backend._task_id))
str(host_secret), ResolveContext(task_id=docker_backend._task_id)
)
# Belt and suspenders: the host secret must not appear anywhere in the error.
assert b"HOST-PRIVATE-KEY".decode() not in str(excinfo.value)
22 changes: 22 additions & 0 deletions tests/tools/test_browser_cleanup.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,28 @@ def test_local_timeout_terminates_daemon_tree(self, tmp_path):
mock_tree_kill.assert_called_once_with(9876)
assert not socket_dir.exists()

def test_remote_cdp_cleanup_without_daemon_pid_removes_transport(
self, tmp_path, caplog
):
browser_tool = self.browser_tool
session_info = {
"session_name": "cdp_remote123",
"cdp_url": "ws://toolbox/sessions/conversation-a",
}
socket_dir = tmp_path / "agent-browser-cdp_remote123"
socket_dir.mkdir()
(socket_dir / "client.sock").write_text("stale")

assert (
browser_tool._terminate_timed_out_browser_daemon(
session_info, str(socket_dir)
)
is False
)

assert not socket_dir.exists()
assert "daemon PID unavailable" not in caplog.text

def test_timeout_reset_does_not_drop_replacement_session(self, tmp_path):
browser_tool = self.browser_tool
timed_out = {"session_name": "cdp_old", "cdp_url": "ws://old"}
Expand Down
12 changes: 12 additions & 0 deletions tests/tools/test_file_tools_tilde_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,15 @@ def test_absolute_tilde_in_workspace_root(self, tmp_path, monkeypatch):

assert str(profile_home) in str(resolved)
assert str(process_home) not in str(resolved)

def test_container_tilde_is_preserved_for_remote_home_expansion(self, monkeypatch):
monkeypatch.setattr(ft, "_uses_container_paths", lambda _task: True)

with patch(
"hermes_constants.get_subprocess_home",
return_value="/Users/host-user",
):
resolved = ft._resolve_path_for_task("~/brand/brief.md", task_id="sprite")

assert resolved == ft.PurePosixPath("~/brand/brief.md")
assert "/Users/host-user" not in str(resolved)
Loading
Loading