fix(bot-screen): vault page operations honour the human lease

browser_vault_fill / enter_code / save_login reach the page over the supervisor CDP socket, outside
_run_browser_command, so they skipped the Bot Desktop fence: with a human holding the screen an ordinary
browser_snapshot was refused while enter_code focused, inspected and wrote to the very form the human was
typing into. Their tool handlers now run under the same run_fenced admission + epoch check as every other
page access.
This commit is contained in:
teknium1
2026-09-13 14:43:19 -07:00
parent 88bd29213b
commit e1fd8f5dde
2 changed files with 38 additions and 5 deletions

View File

@@ -109,3 +109,23 @@ def test_browser_console_supervisor_fast_path_is_fenced_while_human_controls_sha
assert evaluated == [] and commands == [], "human holds the lease, yet the page was evaluated"
assert "WHAT-THE-HUMAN-TYPED" not in raw
assert result.get("code") == "human_has_control"
def test_vault_page_operations_respect_the_human_lease(monkeypatch):
"""The vault tools reach the page over the supervisor socket, outside `_run_browser_command`: while a
human holds the screen they must be refused like every other page access, and never focus, inspect or
write to the form the human is typing into."""
from tools import browser_tool as browser
from tools import browser_vault_tool as vault
browser._active_sessions["default"] = {"session_name": "review", "cdp_url": None, "features": {"local": True}}
touched = []
for name in ("browser_vault_fill", "browser_vault_enter_code", "browser_vault_save_login"):
monkeypatch.setattr(vault, name, lambda *a, **k: touched.append(name) or json.dumps({"success": True}))
lease.acquire("human")
for handler in (vault._handle_vault_fill, vault._handle_vault_enter_code, vault._handle_vault_save_login):
res = json.loads(handler({"handle": "vault_x"}, task_id="default"))
assert res["code"] == "human_has_control", handler.__name__
assert touched == [], "no vault page access while the human holds the screen"
lease.release("human")
assert json.loads(vault._handle_vault_fill({"handle": "vault_x"}, task_id="default"))["success"] is True

View File

@@ -660,12 +660,26 @@ BROWSER_VAULT_ENTER_CODE_SCHEMA = {
}
def _fenced_page_op(task_id: Optional[str], fn) -> str:
"""Vault operations focus, inspect and fill the page over the supervisor socket, bypassing
``_run_browser_command``; they must honour the Bot Desktop lease like every other page access,
or a human typing a credential on the taken-over screen could be read or written to."""
from tools.browser_tool import _active_sessions, _last_session_key
from tools.browser_tool_session import run_fenced
session = _active_sessions.get(_last_session_key(task_id or "default")) or {}
res = run_fenced(session, lambda: {"raw": fn()})
return res["raw"] if "raw" in res else json.dumps(res)
def _handle_vault_enter_code(args: Dict[str, Any], **kwargs) -> str:
return browser_vault_enter_code(handle=str(args.get("handle") or ""), task_id=kwargs.get("task_id"))
tid = kwargs.get("task_id")
return _fenced_page_op(tid, lambda: browser_vault_enter_code(handle=str(args.get("handle") or ""), task_id=tid))
def _handle_vault_save_login(args: Dict[str, Any], **kwargs) -> str:
return browser_vault_save_login(label=str(args.get("label") or ""), task_id=kwargs.get("task_id"))
tid = kwargs.get("task_id")
return _fenced_page_op(tid, lambda: browser_vault_save_login(label=str(args.get("label") or ""), task_id=tid))
def _handle_vault_list(args: Dict[str, Any], **kwargs) -> str:
@@ -677,9 +691,8 @@ def _handle_vault_unlock(args: Dict[str, Any], **kwargs) -> str:
def _handle_vault_fill(args: Dict[str, Any], **kwargs) -> str:
return browser_vault_fill(
handle=str(args.get("handle") or ""), task_id=kwargs.get("task_id")
)
tid = kwargs.get("task_id")
return _fenced_page_op(tid, lambda: browser_vault_fill(handle=str(args.get("handle") or ""), task_id=tid))
from tools.registry import no_cache_check_fn, registry # noqa: E402