""" Bind-host resolution tests — guards against regressing the loopback default. `backend/main.py` resolves its uvicorn bind host from `OMNIVOICE_BIND_HOST` with a default of `127.0.0.1`. Binding to `0.0.0.0` by default would expose every (currently unauthenticated) router on the user's LAN — see the security note in `deploy/docker-compose.yml`. These tests don't boot uvicorn. They re-evaluate the resolution expression used in `main.py`'s `__main__` block, both as a string-level guard against edits that swap the default back to `0.0.0.0`, and as a behavioral guard on the env-var override path. """ import os import re from pathlib import Path _BACKEND_MAIN = Path(__file__).resolve().parent.parent / "backend" / "main.py" def _resolve_bind_host(env: dict) -> str: """Mirror of the resolution logic in `backend/main.py`'s __main__ block. Kept in lock-step with the production expression on purpose — any code change there should require the test to be updated as well, which is exactly the regression boundary we want. """ return env.get("OMNIVOICE_BIND_HOST", "127.0.0.1") def test_default_bind_is_loopback_when_env_unset(): assert _resolve_bind_host({}) == "127.0.0.1" def test_explicit_loopback_env_var_is_honored(): assert _resolve_bind_host({"OMNIVOICE_BIND_HOST": "127.0.0.1"}) == "127.0.0.1" def test_explicit_all_interfaces_env_var_is_honored(): # Used by deploy/docker-compose.yml — must still work as an opt-in override. assert _resolve_bind_host({"OMNIVOICE_BIND_HOST": "0.0.0.0"}) == "0.0.0.0" def test_backend_main_source_does_not_hardcode_all_interfaces_default(): """String-level guard against re-introducing a `host="0.0.0.0"` default in `backend/main.py`. If someone edits the production uvicorn.run call to hardcode 0.0.0.0 again, this test fails with a pointer to the security rationale in the surrounding comment.""" src = _BACKEND_MAIN.read_text(encoding="utf-8") # The health-check thread legitimately binds 127.0.0.1; the production # call should reference OMNIVOICE_BIND_HOST (env-driven), not a hardcoded # 0.0.0.0 literal. Allow `0.0.0.0` to appear in *comments* (security # rationale block) but not in any uvicorn.run(...) host= argument. bad_pattern = re.compile(r'uvicorn\.run\([^)]*host\s*=\s*["\']0\.0\.0\.0["\']') assert not bad_pattern.search(src), ( "backend/main.py contains a hardcoded host=\"0.0.0.0\" in uvicorn.run(). " "Use OMNIVOICE_BIND_HOST env var (default 127.0.0.1) instead — see " "the security comment block above the call site." ) def test_backend_main_source_references_omnivoice_bind_host(): """Companion to the above: the env-var-driven path must be present so a future refactor doesn't accidentally remove it (which would silently revert to whichever literal defaults the new code chose).""" src = _BACKEND_MAIN.read_text(encoding="utf-8") assert "OMNIVOICE_BIND_HOST" in src, ( "backend/main.py no longer references OMNIVOICE_BIND_HOST — the " "loopback-default contract is unenforced." )