feat: harden remote serve and reuse connections
Testing / remote-protocol-compat (0.9.5) (push) Successful in 56s
Testing / remote-protocol-compat (0.9.3) (push) Successful in 59s
Testing / test (push) Successful in 1m1s
Build & Publish Package / publish (push) Successful in 33s
Package Extension / package-extension (push) Successful in 36s
Testing / remote-protocol-compat (0.9.5) (push) Successful in 56s
Testing / remote-protocol-compat (0.9.3) (push) Successful in 59s
Testing / test (push) Successful in 1m1s
Build & Publish Package / publish (push) Successful in 33s
Package Extension / package-extension (push) Successful in 36s
- Gate TCP serve commands with safe-by-default policies, per-key allow tokens, per-key rate limiting, and audit labels. - Reuse authenticated encrypted remote sessions and parallelize/caches multi-browser fanout to reduce repeated handshake roundtrips. - Increase paged native-host batch size with extension-side byte budgeting to speed large tab listings safely. - Point install output at public Chrome Web Store / Firefox AMO listings by default, with --dev preserving unpacked workflows. - Share search-engine metadata between CLI and SDK and bump the package/extension version to 0.16.0. - Cover the new security, pooling, paging, install, and fanout behavior with expanded Python and extension tests.
This commit is contained in:
@@ -10,6 +10,7 @@ from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
|
||||
SAFE_COMMANDS = {
|
||||
"browser-cli.targets",
|
||||
"clients.list",
|
||||
"extension.capabilities",
|
||||
"extension.info",
|
||||
@@ -74,15 +75,23 @@ DANGEROUS_PREFIXES = (
|
||||
"storage.",
|
||||
)
|
||||
|
||||
# Server-side key-management control commands. Gated separately so a key can be
|
||||
# trusted for browser use without also being able to list or add trusted keys.
|
||||
KEY_COMMANDS = {
|
||||
"browser-cli.auth.keys",
|
||||
"browser-cli.auth.trust",
|
||||
}
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CommandPolicy:
|
||||
allow_read_page: bool = False
|
||||
allow_control: bool = False
|
||||
allow_dangerous: bool = False
|
||||
allow_keys: bool = False
|
||||
|
||||
@classmethod
|
||||
def unrestricted(cls) -> "CommandPolicy":
|
||||
return cls(allow_read_page=True, allow_control=True, allow_dangerous=True)
|
||||
return cls(allow_read_page=True, allow_control=True, allow_dangerous=True, allow_keys=True)
|
||||
|
||||
def _is_control(command: str) -> bool:
|
||||
if command in CONTROL_COMMANDS:
|
||||
@@ -93,6 +102,8 @@ def _is_control(command: str) -> bool:
|
||||
|
||||
def command_category(command: str) -> str:
|
||||
name = str(command or "")
|
||||
if name in KEY_COMMANDS:
|
||||
return "keys"
|
||||
if name in DANGEROUS_COMMANDS or any(name.startswith(prefix) for prefix in DANGEROUS_PREFIXES):
|
||||
return "dangerous"
|
||||
if name in READ_PAGE_COMMANDS:
|
||||
@@ -113,7 +124,9 @@ def assert_command_allowed(command: str, policy: CommandPolicy) -> None:
|
||||
return
|
||||
if category == "dangerous" and policy.allow_dangerous:
|
||||
return
|
||||
if category == "keys" and policy.allow_keys:
|
||||
return
|
||||
raise PermissionError(
|
||||
f"Raw command '{command}' is {category} and blocked by default; "
|
||||
"use --allow-read-page, --allow-control, or --allow-dangerous explicitly"
|
||||
"use --allow-read-page, --allow-control, --allow-dangerous, or --allow-keys explicitly"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user