Newer
Older
navi-1 / tests / unit / mcp / test_no_literal_secrets.py
"""The tracked MCP configs must never carry a live credential.

``mcp_servers.d/*.json`` is committed, so a token written there is in git
history for good — and ``GET /admin/mcp/config`` hands the same value to the
admin client. A config therefore *names* the variable (``Bearer ${NAVI_MCP_X_TOKEN}``)
and the value lives in the service ``.env``; see ``docs/mcp.md``.

This is the guard on that invariant: it is what fails when someone pastes a
token back into a config file, by hand or through ``PUT /admin/mcp/config``.
"""

import json
import re
from pathlib import Path

import navi.mcp.config as mcp_config
from navi.tools._internal.redact import is_sensitive_key

REPO_ROOT = Path(mcp_config.__file__).resolve().parents[2]
CONFIG_DIR = REPO_ROOT / "mcp_servers.d"

_PLACEHOLDER = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}")

# The credential-bearing servers, and the variable each one names. Keep in step
# with `deploy/env.template`.
EXPECTED_VARIABLES = {
    "gnexus-creds": "NAVI_MCP_GNEXUS_CREDS_TOKEN",
    "gntodo": "NAVI_MCP_GNTODO_TOKEN",
    "hard-panel": "NAVI_MCP_HARD_PANEL_TOKEN",
    "synapse": "NAVI_MCP_SYNAPSE_TOKEN",
    "tgclient": "NAVI_MCP_TGCLIENT_TOKEN",
}


def _configs() -> dict[str, dict]:
    return {
        path.stem: json.loads(path.read_text(encoding="utf-8"))
        for path in sorted(CONFIG_DIR.glob("*.json"))
    }


def test_every_sensitive_header_or_env_value_is_a_placeholder():
    offenders = [
        f"{server}: {field}.{key}"
        for server, cfg in _configs().items()
        for field in ("headers", "env")
        for key, value in (cfg.get(field) or {}).items()
        if is_sensitive_key(key) and "${" not in str(value)
    ]
    assert not offenders, (
        "literal credential in a tracked config — replace it with "
        f"`${{NAVI_MCP_<SERVER>_TOKEN}}` and put the value in .env: {offenders}"
    )


def test_placeholder_names_are_env_variables():
    names = {
        name
        for cfg in _configs().values()
        for field in ("headers", "env")
        for value in (cfg.get(field) or {}).values()
        for name in _PLACEHOLDER.findall(str(value))
    }
    assert names, "no placeholders in any config — did a token get pasted back in?"
    assert not [name for name in names if not name.startswith("NAVI_MCP_")], (
        f"unexpected placeholder names: {sorted(names)}"
    )


def test_the_credential_servers_name_their_variable():
    configs = _configs()
    for server, variable in EXPECTED_VARIABLES.items():
        assert server in configs, f"{server}.json is gone — update this test with it"
        assert configs[server]["headers"]["Authorization"] == f"Bearer ${{{variable}}}"