feat: add remote trust and server identity pinning
Testing / remote-protocol-compat (0.16.0) (push) Successful in 1m1s
Testing / remote-protocol-compat (0.15.0) (push) Successful in 1m3s
Testing / test (push) Failing after 1m15s
Build & Publish Package / publish (push) Successful in 51s
Package Extension / package-extension (push) Successful in 1m6s

- Add SSH-style server identity keys and known-host verification for remote serve endpoints.
- Add remote add/list/remove commands for explicit endpoint persistence.
- Fix remote clients listing to fan out through target discovery instead of ambiguous auto-routing.
- Add URL glob matching for tabs filter and count with extension tests.
- Add n8n credential pinning for server public keys or SHA256 fingerprints.
- Remove obsolete compat shim behavior while keeping empty compat seams for future protocol changes.
- Bump browser-cli to 0.16.4 and n8n node to 0.3.1.
- Cover known-hosts, remote registry, compat seams, n8n protocol verification, and URL matching with tests.
This commit is contained in:
2026-06-26 08:53:21 +02:00
parent 1ae9c33f00
commit 6270d8c956
28 changed files with 981 additions and 297 deletions
-27
View File
@@ -228,33 +228,6 @@ class TestAuthSuccess:
client.close()
t.join(timeout=2)
def test_uppercase_pubkey_normalized_by_compat(self, tmp_path, monkeypatch):
"""Clients < 0.9.3 may send uppercase pubkeys; compat layer normalises before auth."""
path = tmp_path / "authorized_keys"
pem, pub = generate_keypair() # pub is lowercase hex
path.write_text(pub + "\n")
key_path = tmp_path / "client.key.pem"
key_path.write_bytes(pem)
priv = load_private_key(key_path)
monkeypatch.setattr("browser_cli.client.targets.resolve_socket", _mock_no_browser)
client, server = _pair()
t = _spawn(server, path)
challenge = _recv_framed(client)
nonce = bytes.fromhex(challenge["nonce"])
# old client sends uppercase pubkey
msg = {"id": "x", "command": "tabs.list", "args": {}, "user_agent": "browser-cli/0.9.2", "pubkey": pub.upper()}
msg["sig"] = sign(priv, nonce, msg).hex()
_send_framed(client, json.dumps(msg).encode())
resp = _recv_framed(client)
assert "unauthorized" not in resp.get("error", "").lower()
assert "browser" in resp.get("error", "").lower() or "connected" in resp.get("error", "").lower()
client.close()
t.join(timeout=2)
def test_post_quantum_kex_auth_reaches_proxy(self, tmp_path, monkeypatch):
"""ML-KEM shared secret is decapsulated and bound to the auth signature."""
monkeypatch.setattr("browser_cli.client.targets.resolve_socket", _mock_no_browser)