feat: token-auth removal, security hardening, Stripe-style compat layer (v0.9.2)
- Remove token auth entirely; only Ed25519 pubkey auth or --no-auth - Add 32 MB message-size cap in serve and client (DoS protection) - Set Unix socket to 0o600 after bind in native_host (multi-user hardening) - Enforce browser-cli/VERSION user-agent on all TCP connections - Add PROTOCOL_MIN_CLIENT check (>= 0.9.0) server- and client-side - Include server_version + min_client_version in challenge frame - Add browser_cli/version_manager.py: parse_version, get_installed_version - Add browser_cli/compat.py: Stripe-style versioning layer with adapt_request / adapt_response hooks; baseline 0.9.2, no shims needed yet - Fix BrowserCLI key handling: no Path() wrap for agent specs - Fix _multi_browser_targets() to forward key to remote_browser_targets() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+5
-15
@@ -30,7 +30,6 @@ from browser_cli.client import (
|
||||
REGISTRY_PATH,
|
||||
active_browser_targets,
|
||||
display_browser_name,
|
||||
save_remote_token,
|
||||
remote_target_for_alias,
|
||||
remote_browser_targets,
|
||||
)
|
||||
@@ -191,16 +190,12 @@ def _print_version(ctx, param, value):
|
||||
"--remote", default=None, metavar="HOST:PORT",
|
||||
help="Connect to a remote browser exposed via 'browser-cli serve'.",
|
||||
)
|
||||
@click.option(
|
||||
"--token", default=None, metavar="TOKEN",
|
||||
help="Auth token for the remote browser-cli serve instance.",
|
||||
)
|
||||
@click.option(
|
||||
"--key", default=None, metavar="PATH",
|
||||
help="Ed25519 private key PEM for pubkey auth with a remote serve instance.",
|
||||
)
|
||||
@click.pass_context
|
||||
def main(ctx, browser, remote, token, key):
|
||||
def main(ctx, browser, remote, key):
|
||||
"""Control your running browser from the terminal via a Chrome extension."""
|
||||
ctx.ensure_object(dict)
|
||||
ctx.obj["browser"] = browser
|
||||
@@ -209,13 +204,10 @@ def main(ctx, browser, remote, token, key):
|
||||
os.environ["BROWSER_CLI_PROFILE"] = browser
|
||||
ctx.call_on_close(lambda: os.environ.pop("BROWSER_CLI_PROFILE", None))
|
||||
ctx.obj["remote"] = remote
|
||||
ctx.obj["token"] = token
|
||||
ctx.obj["key"] = key
|
||||
if remote:
|
||||
os.environ["BROWSER_CLI_REMOTE"] = remote
|
||||
ctx.call_on_close(lambda: os.environ.pop("BROWSER_CLI_REMOTE", None))
|
||||
if token:
|
||||
save_remote_token(remote, token)
|
||||
if key:
|
||||
os.environ["BROWSER_CLI_KEY"] = key
|
||||
ctx.call_on_close(lambda: os.environ.pop("BROWSER_CLI_KEY", None))
|
||||
@@ -399,7 +391,6 @@ def clients_group(ctx):
|
||||
|
||||
browser_alias = (ctx.obj or {}).get("browser")
|
||||
remote = (ctx.obj or {}).get("remote") or os.environ.get("BROWSER_CLI_REMOTE")
|
||||
token = (ctx.obj or {}).get("token") or os.environ.get("BROWSER_CLI_TOKEN")
|
||||
key = (ctx.obj or {}).get("key")
|
||||
|
||||
if not remote and browser_alias:
|
||||
@@ -407,15 +398,14 @@ def clients_group(ctx):
|
||||
# then show ALL clients from that remote (not just the one resolved profile).
|
||||
resolved = remote_target_for_alias(browser_alias)
|
||||
if resolved:
|
||||
resolved_token = token or resolved.token
|
||||
try:
|
||||
targets = remote_browser_targets(resolved.remote, resolved_token)
|
||||
targets = remote_browser_targets(resolved.remote)
|
||||
except (BrowserNotConnected, RuntimeError) as e:
|
||||
console.print(f"[red]Error:[/red] {e}")
|
||||
sys.exit(1)
|
||||
for target in targets:
|
||||
try:
|
||||
result = send_command("clients.list", profile=target.profile, remote=resolved.remote, token=resolved_token, key=key)
|
||||
result = send_command("clients.list", profile=target.profile, remote=resolved.remote, key=key)
|
||||
for c in (result or []):
|
||||
c["profile"] = target.display_name
|
||||
all_clients.append(c)
|
||||
@@ -423,7 +413,7 @@ def clients_group(ctx):
|
||||
continue
|
||||
elif remote:
|
||||
try:
|
||||
result = send_command("clients.list", profile=browser_alias, remote=remote, token=token, key=key)
|
||||
result = send_command("clients.list", profile=browser_alias, remote=remote, key=key)
|
||||
for c in (result or []):
|
||||
c["profile"] = c.get("profile") or browser_alias or "remote"
|
||||
all_clients.append(c)
|
||||
@@ -455,7 +445,7 @@ def clients_group(ctx):
|
||||
if target.remote is None:
|
||||
continue
|
||||
try:
|
||||
result = send_command("clients.list", profile=target.profile, remote=target.remote, token=target.token)
|
||||
result = send_command("clients.list", profile=target.profile, remote=target.remote)
|
||||
for c in (result or []):
|
||||
c["profile"] = target.display_name
|
||||
all_clients.append(c)
|
||||
|
||||
Reference in New Issue
Block a user