cea8a7e994
- Add the n8n community node package with credentials, command mapping, direct serve TCP client, and browser-cli protocol crypto helpers. - Cover Ed25519 signing, canonical JSON, PQ transport encryption, request mapping, and security behavior with unit tests. - Harden serve-http with per-address rate limiting, an 8 MB request body cap, and clear warnings when binding plain HTTP beyond loopback. - Stop one-shot --key overrides from being persisted automatically; document explicit remote trust and keep key-management behind the keys policy tier. - Make HTML-to-Markdown conversion safer by bounding tree depth and dropping unsafe link/image URL schemes. - Bump package and extension release metadata to 0.16.3.
60 lines
2.3 KiB
Python
60 lines
2.3 KiB
Python
"""Remote client auth/key preparation for browser command messages."""
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import os
|
|
from pathlib import Path
|
|
|
|
from browser_cli.remote import registry as remote_registry
|
|
from browser_cli.constants import DEFAULT_KEY_PATH, NO_ROUTE_COMMANDS
|
|
from browser_cli.version_manager import USER_AGENT
|
|
|
|
def load_private_key(key_path: Path | str | None = None):
|
|
"""Load an Ed25519 signing key from file or SSH agent spec."""
|
|
raw = str(key_path) if key_path is not None else os.environ.get("BROWSER_CLI_KEY", str(DEFAULT_KEY_PATH))
|
|
|
|
if raw == "agent" or raw.startswith("agent:"):
|
|
selector = raw[6:] or None
|
|
from browser_cli.auth import agent_find_key
|
|
return agent_find_key(selector)
|
|
|
|
path = Path(raw)
|
|
if not path.exists():
|
|
return None
|
|
try:
|
|
from browser_cli.auth import load_private_key as load_pem_key
|
|
return load_pem_key(path)
|
|
except Exception:
|
|
return None
|
|
|
|
def add_remote_auth_fields(msg: dict, command: str, requested_profile: str | None, remote_endpoint: str, key, auto_router) -> object:
|
|
"""Mutate *msg* with remote auth/routing fields and return the signing key."""
|
|
from browser_cli import transport
|
|
|
|
msg["user_agent"] = USER_AGENT
|
|
msg["accept_encoding"] = transport.client_accept_encoding()
|
|
key_spec = key if key is not None else remote_registry.key_for_remote(remote_endpoint)
|
|
private_key = load_private_key(key_spec)
|
|
|
|
route_profile = requested_profile
|
|
if not route_profile and command not in NO_ROUTE_COMMANDS:
|
|
route_profile = auto_router(remote_endpoint, key=key_spec)
|
|
if route_profile:
|
|
msg["_route"] = route_profile
|
|
return private_key
|
|
|
|
async def add_remote_auth_fields_async(msg: dict, command: str, requested_profile: str | None, remote_endpoint: str, key, auto_router) -> object:
|
|
from browser_cli import transport
|
|
|
|
msg["user_agent"] = USER_AGENT
|
|
msg["accept_encoding"] = transport.client_accept_encoding()
|
|
key_spec = key if key is not None else await asyncio.to_thread(remote_registry.key_for_remote, remote_endpoint)
|
|
private_key = await asyncio.to_thread(load_private_key, key_spec)
|
|
|
|
route_profile = requested_profile
|
|
if not route_profile and command not in NO_ROUTE_COMMANDS:
|
|
route_profile = await auto_router(remote_endpoint, key=key_spec)
|
|
if route_profile:
|
|
msg["_route"] = route_profile
|
|
return private_key
|