Files
openswarm/backend/tests/test_node_trust.py
T

134 lines
5.7 KiB
Python

"""The Node children get the OS roots, because Node ignores the OS trust store.
ENG-407 armed truststore so the PYTHON backend trusts what the browser trusts. 9router and the claude
CLI are Node and verify against Node's compiled-in list, so on a machine with an endpoint tool's or a
corporate proxy's root, sign-in now works and the first model call still dies (ENG-408).
`NODE_USE_SYSTEM_CA=1` would make the whole file unnecessary; it landed in Node 22.15 and the bundled
runtime is v20.18.1, so we export the OS roots and point NODE_EXTRA_CA_CERTS at them.
DRILLED 2026-08-27 against a real Node TLS server signed by a throwaway root:
stock Node -> REFUSED UNABLE_TO_VERIFY_LEAF_SIGNATURE (the ThinkPad bug)
NODE_EXTRA_CA_CERTS incl. root -> OK 200 (the cure)
NODE_EXTRA_CA_CERTS OS-only -> REFUSED (trust not widened)
"""
import os
import platform
import pytest
from backend.config import node_trust
@pytest.fixture(autouse=True)
def p_no_cache():
"""The export is cached once per process; a cached answer would make every case below a no-op."""
node_trust.reset_cache_for_test()
yield
node_trust.reset_cache_for_test()
ROUTER = "backend/apps/nine_router/process.py"
CLI = "backend/apps/agents/manager/configure_provider_env.py"
def test_an_export_that_yields_nothing_sets_no_variable(tmp_path, monkeypatch):
"""Fail to today's behaviour. An unset variable is Node's bundled roots, which is every build
before this; a broken export must never be louder than that."""
monkeypatch.setattr(node_trust, "p_mac_roots", lambda: [])
monkeypatch.setattr(node_trust, "p_windows_roots", lambda: [])
monkeypatch.setattr(platform, "system", lambda: "Darwin")
assert node_trust.node_ca_env(str(tmp_path / "r.pem")) == {}
def test_an_unwritable_destination_sets_no_variable(monkeypatch):
monkeypatch.setattr(platform, "system", lambda: "Darwin")
monkeypatch.setattr(node_trust, "p_mac_roots", lambda: ["-----BEGIN CERTIFICATE-----\nx\n"])
assert node_trust.node_ca_env("/proc/nope/cannot/write.pem") == {}
def test_linux_is_left_alone(monkeypatch, tmp_path):
"""Node on Linux already reads OpenSSL's default paths, which IS the system store there."""
monkeypatch.setattr(platform, "system", lambda: "Linux")
assert node_trust.export_os_roots(str(tmp_path / "r.pem")) is None
def test_an_absurd_store_is_refused_rather_than_handed_to_node(monkeypatch, tmp_path):
monkeypatch.setattr(platform, "system", lambda: "Darwin")
monkeypatch.setattr(node_trust, "p_mac_roots",
lambda: ["-----BEGIN CERTIFICATE-----\nx\n" * (node_trust.MAX_CERTS + 1)])
assert node_trust.export_os_roots(str(tmp_path / "r.pem")) is None
def test_a_real_export_on_this_machine_produces_parseable_pem(tmp_path):
"""Skipped off the two platforms that need it; on them it must produce real certificates."""
if platform.system() not in node_trust.NEEDS_EXPORT:
return
p = node_trust.export_os_roots(str(tmp_path / "roots.pem"))
assert p and os.path.exists(p)
body = open(p).read()
assert body.count(node_trust.PEM_HEADER) > 10, "an OS root store is not this small"
assert body.count(node_trust.PEM_HEADER) == body.count("-----END CERTIFICATE-----")
def test_both_node_children_are_wired_not_just_one():
"""The recurring defect: a fix applied to one spawn site and not the other. The router and the
CLI are two separate Node processes and BOTH verify provider TLS."""
for path in (ROUTER, CLI):
src = open(path).read()
assert "node_ca_env" in src, f"{path} spawns Node without the OS roots"
def test_it_is_additive_and_never_replaces_the_env():
"""setdefault, not assignment: a lane that deliberately set its own CA path keeps it, and this
can never blank a variable someone else needed."""
src = open(CLI).read()
i = src.index("node_ca_env(")
assert "p_env.setdefault(k, v)" in src[i:i + 300]
def test_the_export_happens_once_per_process(monkeypatch, tmp_path):
"""It used to shell out to `security` and rewrite ~250KB on EVERY spawn: measured three times in
five seconds on one turn."""
calls = {"n": 0}
def p_count():
calls["n"] += 1
return ["-----BEGIN CERTIFICATE-----\nx\n-----END CERTIFICATE-----\n"]
monkeypatch.setattr(platform, "system", lambda: "Darwin")
monkeypatch.setattr(node_trust, "p_mac_roots", p_count)
dest = str(tmp_path / "r.pem")
a = node_trust.node_ca_env(dest)
b = node_trust.node_ca_env(dest)
c = node_trust.node_ca_env(dest)
assert calls["n"] == 1, f"exported {calls['n']} times"
assert a == b == c and a
def test_a_failed_export_is_cached_too_and_does_not_retry_every_spawn(monkeypatch, tmp_path):
"""The failure path is the one that would otherwise shell out forever on a machine where the
store cannot be read at all."""
calls = {"n": 0}
def p_empty():
calls["n"] += 1
return []
monkeypatch.setattr(platform, "system", lambda: "Darwin")
monkeypatch.setattr(node_trust, "p_mac_roots", p_empty)
dest = str(tmp_path / "r.pem")
assert node_trust.node_ca_env(dest) == {}
assert node_trust.node_ca_env(dest) == {}
assert calls["n"] == 1
def test_the_caller_cannot_mutate_the_cache(monkeypatch, tmp_path):
monkeypatch.setattr(platform, "system", lambda: "Darwin")
monkeypatch.setattr(node_trust, "p_mac_roots",
lambda: ["-----BEGIN CERTIFICATE-----\nx\n-----END CERTIFICATE-----\n"])
dest = str(tmp_path / "r.pem")
first = node_trust.node_ca_env(dest)
first["NODE_EXTRA_CA_CERTS"] = "/tmp/evil.pem"
assert node_trust.node_ca_env(dest)["NODE_EXTRA_CA_CERTS"] != "/tmp/evil.pem"