6270d8c956
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.
65 lines
2.4 KiB
Python
65 lines
2.4 KiB
Python
import json
|
|
|
|
import pytest
|
|
|
|
from browser_cli.auth.server_identity import load_or_create_server_identity, public_key_hex, sign_challenge, verify_challenge_signature
|
|
from browser_cli.errors import BrowserNotConnected
|
|
from browser_cli.remote import known_hosts
|
|
|
|
def _challenge(tmp_path):
|
|
key = load_or_create_server_identity(tmp_path / "server.pem")
|
|
msg = {
|
|
"type": "challenge",
|
|
"nonce": "00" * 32,
|
|
"server_version": "0.16.4",
|
|
"min_client_version": "0.9.0",
|
|
"server_pubkey": public_key_hex(key),
|
|
}
|
|
msg["server_sig"] = sign_challenge(msg, key)
|
|
return msg
|
|
|
|
def test_challenge_signature_verifies(tmp_path):
|
|
challenge = _challenge(tmp_path)
|
|
|
|
assert verify_challenge_signature(challenge) is True
|
|
|
|
challenge["nonce"] = "11" * 32
|
|
assert verify_challenge_signature(challenge) is False
|
|
|
|
def test_known_host_mismatch_is_rejected(monkeypatch, tmp_path):
|
|
path = tmp_path / "known_hosts.json"
|
|
challenge = _challenge(tmp_path)
|
|
monkeypatch.setattr(known_hosts, "KNOWN_HOSTS_PATH", path)
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(json.dumps({"browser-host.example": "00" * 32}), encoding="utf-8")
|
|
|
|
with pytest.raises(BrowserNotConnected, match="REMOTE SERVER IDENTITY CHANGED"):
|
|
known_hosts.verify_known_host("browser-host.example", challenge)
|
|
|
|
def test_unknown_non_interactive_host_is_rejected(monkeypatch, tmp_path):
|
|
path = tmp_path / "known_hosts.json"
|
|
challenge = _challenge(tmp_path)
|
|
monkeypatch.setattr(known_hosts, "KNOWN_HOSTS_PATH", path)
|
|
monkeypatch.setattr("sys.stdin.isatty", lambda: False)
|
|
|
|
with pytest.raises(BrowserNotConnected, match="Unknown remote server identity"):
|
|
known_hosts.verify_known_host("browser-host.example", challenge)
|
|
|
|
def test_loopback_unknown_host_is_allowed(monkeypatch, tmp_path):
|
|
path = tmp_path / "known_hosts.json"
|
|
challenge = _challenge(tmp_path)
|
|
monkeypatch.setattr(known_hosts, "KNOWN_HOSTS_PATH", path)
|
|
monkeypatch.setattr("sys.stdin.isatty", lambda: False)
|
|
|
|
known_hosts.verify_known_host("127.0.0.1:8765", challenge)
|
|
|
|
assert not path.exists()
|
|
|
|
def test_save_and_remove_known_host(tmp_path):
|
|
path = tmp_path / "known_hosts.json"
|
|
known_hosts.save_known_host("browser-host.example:443", "11" * 32, path)
|
|
|
|
assert json.loads(path.read_text(encoding="utf-8")) == {"browser-host.example": "11" * 32}
|
|
assert known_hosts.remove_known_host("browser-host.example", path) is True
|
|
assert known_hosts.load_known_hosts(path) == {}
|