"""Safety policy for generic command execution surfaces. Dedicated first-party CLI/SDK methods keep their normal behavior. This module only gates raw surfaces where a single string can trigger arbitrary browser capabilities: ``browser-cli command``, ``browser-cli script``, and the HTTP ``/command`` endpoint. """ from __future__ import annotations from dataclasses import dataclass SAFE_COMMANDS = { "clients.list", "extension.capabilities", "extension.info", "group.list", "group.query", "group.tabs", "page.info", "perf.status", "tabs.active_in_window", "tabs.count", "tabs.filter", "tabs.list", "tabs.query", "tabs.status", "windows.list", } READ_PAGE_COMMANDS = { "dom.attr", "dom.exists", "dom.query", "dom.text", "extract.html", "extract.images", "extract.json", "extract.links", "extract.markdown", "extract.text", "tabs.html", } CONTROL_PREFIXES = ( "navigate.", "nav.", "group.", "session.", "tabs.", "windows.", ) CONTROL_COMMANDS = { "dom.check", "dom.clear", "dom.click", "dom.focus", "dom.hover", "dom.key", "dom.poll", "dom.scroll", "dom.select", "dom.submit", "dom.type", "dom.uncheck", "dom.wait_for", "extension.reload", } DANGEROUS_COMMANDS = { "dom.eval", "tabs.screenshot", } DANGEROUS_PREFIXES = ( "storage.", ) @dataclass(frozen=True) class CommandPolicy: allow_read_page: bool = False allow_control: bool = False allow_dangerous: bool = False @classmethod def unrestricted(cls) -> "CommandPolicy": return cls(allow_read_page=True, allow_control=True, allow_dangerous=True) def _is_control(command: str) -> bool: if command in CONTROL_COMMANDS: return True if any(command.startswith(prefix) for prefix in CONTROL_PREFIXES): return command not in SAFE_COMMANDS and command not in READ_PAGE_COMMANDS and command not in DANGEROUS_COMMANDS return False def command_category(command: str) -> str: name = str(command or "") if name in DANGEROUS_COMMANDS or any(name.startswith(prefix) for prefix in DANGEROUS_PREFIXES): return "dangerous" if name in READ_PAGE_COMMANDS: return "read-page" if name in SAFE_COMMANDS: return "safe" if _is_control(name): return "control" return "unknown" def assert_command_allowed(command: str, policy: CommandPolicy) -> None: category = command_category(command) if category == "safe": return if category == "read-page" and policy.allow_read_page: return if category == "control" and policy.allow_control: return if category == "dangerous" and policy.allow_dangerous: return raise PermissionError( f"Raw command '{command}' is {category} and blocked by default; " "use --allow-read-page, --allow-control, or --allow-dangerous explicitly" )