mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-23 10:42:00 +00:00
fix(browser): enable SSRF guard when terminal runs in container
When terminal.backend is docker/modal/daytona/ssh/singularity, the terminal runs in a sandboxed container with network isolation, but the browser still runs on the host. The SSRF guard was skipped because _is_local_backend() only checked browser.cloud_provider, not the terminal backend. Now _is_local_backend() also checks TERMINAL_ENV — when the terminal is containerized, the browser is treated as non-local and SSRF protection is enabled. Fixes #38690
This commit is contained in:
parent
c7e8854cb3
commit
6984026f12
2 changed files with 47 additions and 2 deletions
|
|
@ -190,6 +190,39 @@ class TestIsLocalBackend:
|
|||
|
||||
assert browser_tool._is_local_backend() is False
|
||||
|
||||
@pytest.mark.parametrize("backend", ["docker", "modal", "daytona", "ssh", "singularity"])
|
||||
def test_container_terminal_backend_is_not_local(self, monkeypatch, backend):
|
||||
"""Terminal running in a container → NOT local (browser on host can access internal networks)."""
|
||||
monkeypatch.setattr(browser_tool, "_is_camofox_mode", lambda: False)
|
||||
monkeypatch.setattr(browser_tool, "_get_cloud_provider", lambda: None)
|
||||
monkeypatch.setenv("TERMINAL_ENV", backend)
|
||||
|
||||
assert browser_tool._is_local_backend() is False
|
||||
|
||||
def test_empty_terminal_env_is_local(self, monkeypatch):
|
||||
"""Empty TERMINAL_ENV → local backend."""
|
||||
monkeypatch.setattr(browser_tool, "_is_camofox_mode", lambda: False)
|
||||
monkeypatch.setattr(browser_tool, "_get_cloud_provider", lambda: None)
|
||||
monkeypatch.setenv("TERMINAL_ENV", "")
|
||||
|
||||
assert browser_tool._is_local_backend() is True
|
||||
|
||||
def test_local_terminal_env_is_local(self, monkeypatch):
|
||||
"""Explicit 'local' TERMINAL_ENV → local backend."""
|
||||
monkeypatch.setattr(browser_tool, "_is_camofox_mode", lambda: False)
|
||||
monkeypatch.setattr(browser_tool, "_get_cloud_provider", lambda: None)
|
||||
monkeypatch.setenv("TERMINAL_ENV", "local")
|
||||
|
||||
assert browser_tool._is_local_backend() is True
|
||||
|
||||
def test_camofox_overrides_container_backend(self, monkeypatch):
|
||||
"""Camofox mode always counts as local, even with container terminal."""
|
||||
monkeypatch.setattr(browser_tool, "_is_camofox_mode", lambda: True)
|
||||
monkeypatch.setattr(browser_tool, "_get_cloud_provider", lambda: None)
|
||||
monkeypatch.setenv("TERMINAL_ENV", "docker")
|
||||
|
||||
assert browser_tool._is_local_backend() is True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Post-redirect SSRF check
|
||||
|
|
|
|||
|
|
@ -619,7 +619,7 @@ def _is_local_mode() -> bool:
|
|||
|
||||
|
||||
def _is_local_backend() -> bool:
|
||||
"""Return True when the browser runs locally (no cloud provider).
|
||||
"""Return True when the browser runs locally AND the terminal is also local.
|
||||
|
||||
SSRF protection is only meaningful for cloud backends (Browserbase,
|
||||
BrowserUse) where the agent could reach internal resources on a remote
|
||||
|
|
@ -627,8 +627,20 @@ def _is_local_backend() -> bool:
|
|||
Chromium without a cloud provider — the user already has full terminal
|
||||
and network access on the same machine, so the check adds no security
|
||||
value.
|
||||
|
||||
However, when the terminal runs in a container (docker, modal, daytona,
|
||||
ssh, singularity), the browser on the host can access internal networks
|
||||
that the terminal cannot. In this case, SSRF protection should be
|
||||
enabled even though the browser is technically "local".
|
||||
"""
|
||||
return _is_camofox_mode() or _get_cloud_provider() is None
|
||||
if _is_camofox_mode():
|
||||
return True
|
||||
if _get_cloud_provider() is not None:
|
||||
return False
|
||||
# When terminal runs in a container, browser on host can access
|
||||
# internal networks the terminal can't → treat as non-local.
|
||||
terminal_backend = os.getenv("TERMINAL_ENV", "local").strip().lower()
|
||||
return terminal_backend in ("local", "")
|
||||
|
||||
|
||||
_auto_local_for_private_urls_resolved = False
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue