Harden M9: Ed25519 attestation, full SHA-256 integrity, locked-by-default, prompt improvements, registry verification

This commit is contained in:
rob
2026-07-20 16:50:49 -03:00
parent 04153a4bbd
commit 56cb34276e
34 changed files with 2549 additions and 257 deletions
+90 -2
View File
@@ -12,6 +12,7 @@ from cmdforge.mcp_client import (
McpServerConfig,
McpClientManager,
_build_server_env,
_build_http_headers,
_normalize_result,
_sanitize,
_fingerprint,
@@ -34,14 +35,15 @@ class TestMcpServerConfig:
@pytest.mark.parametrize(
"change, message",
[
({"transport": "streamable-http"}, "Unsupported MCP transport"),
({"transport": "streamable-http"}, "requires a url"),
({"args": "-y"}, "list of strings"),
({"timeout": 0}, "greater than 0"),
({"timeout": True}, "must be a number"),
],
)
def test_validation_rejects_invalid_config(self, change, message):
cfg = McpServerConfig(name="test", command="server", **change)
command = None if change.get("transport") == "streamable-http" else "server"
cfg = McpServerConfig(name="test", command=command, **change)
with pytest.raises(ValueError, match=message):
cfg.validate()
@@ -55,6 +57,31 @@ class TestMcpServerConfig:
b = McpServerConfig(name="b", command="cmd", args=["-v"])
assert _fingerprint(a) == _fingerprint(b)
def test_streamable_http_accepts_https(self):
cfg = McpServerConfig(
name="remote", transport="streamable-http",
url="https://example.com/mcp", approved=True,
)
cfg.validate()
def test_streamable_http_allows_loopback_http_only(self):
McpServerConfig(
name="local", transport="streamable-http",
url="http://127.0.0.1:8000/mcp",
).validate()
with pytest.raises(ValueError, match="must use HTTPS"):
McpServerConfig(
name="remote", transport="streamable-http",
url="http://example.com/mcp",
).validate()
def test_streamable_http_rejects_url_credentials(self):
with pytest.raises(ValueError, match="must not contain credentials"):
McpServerConfig(
name="remote", transport="streamable-http",
url="https://user:[email protected]/mcp",
).validate()
class TestSanitize:
def test_redacts_bearer(self):
@@ -85,6 +112,15 @@ class TestEnvironmentIsolation:
)
assert _build_server_env(cfg)["SERVER_TOKEN"] == "allowed"
def test_http_header_reference_is_resolved(self, monkeypatch):
monkeypatch.setenv("MCP_TOKEN", "secret")
cfg = McpServerConfig(
name="remote", transport="streamable-http",
url="https://example.com/mcp",
headers={"Authorization": "Bearer ${MCP_TOKEN}"},
)
assert _build_http_headers(cfg)["Authorization"] == "Bearer secret"
def test_missing_environment_reference_fails(self, monkeypatch):
monkeypatch.delenv("CMDFORGE_MISSING", raising=False)
cfg = McpServerConfig(
@@ -122,6 +158,19 @@ class TestMcpConfigPersistence:
def test_load_no_file(self, temp_mcp_file):
assert load_mcp_config() == []
def test_streamable_http_round_trip(self, temp_mcp_file):
save_mcp_config([McpServerConfig(
name="remote", transport="streamable-http",
url="https://example.com/mcp",
headers={"Authorization": "Bearer ${MCP_TOKEN}"},
approved=True,
)])
loaded = load_mcp_config()[0]
assert loaded.transport == "streamable-http"
assert loaded.command is None
assert loaded.url == "https://example.com/mcp"
assert loaded.headers == {"Authorization": "Bearer ${MCP_TOKEN}"}
def test_save_creates_0600_permissions(self, temp_mcp_file):
save_mcp_config([McpServerConfig(name="test", command="echo")])
perms = oct(temp_mcp_file.stat().st_mode & 0o777)
@@ -651,6 +700,45 @@ class TestRegisteredMcpTool:
serve()
class TestStreamableHttpServerSafety:
def test_defaults_are_loopback_with_origin_allowlist(self):
from cmdforge.mcp_server import _validate_http_server_options
origins, token = _validate_http_server_options(
"127.0.0.1", 8000, [], None, None
)
assert token is None
assert origins == [
"http://localhost:8000", "http://127.0.0.1:8000"
]
def test_nonlocal_requires_https_and_auth(self):
from cmdforge.mcp_server import _validate_http_server_options
with pytest.raises(ValueError, match="external-url"):
_validate_http_server_options("0.0.0.0", 8000, [], "secret", None)
with pytest.raises(ValueError, match="auth-token"):
_validate_http_server_options(
"0.0.0.0", 8000, [], None, "https://mcp.example.com"
)
def test_nonlocal_infers_https_origin_and_expands_token(self, monkeypatch):
from cmdforge.mcp_server import _validate_http_server_options
monkeypatch.setenv("MCP_AUTH", "secret-value")
origins, token = _validate_http_server_options(
"0.0.0.0", 8000, [], "${MCP_AUTH}",
"https://mcp.example.com",
)
assert origins == ["https://mcp.example.com"]
assert token == "secret-value"
def test_nonlocal_rejects_insecure_origin(self):
from cmdforge.mcp_server import _validate_http_server_options
with pytest.raises(ValueError, match="origins must use HTTPS"):
_validate_http_server_options(
"0.0.0.0", 8000, ["http://example.com"], "secret",
"https://mcp.example.com",
)
class TestCmdForgeMcpServerEndToEnd:
def test_stdio_discovery_and_invocation(self, tmp_path, monkeypatch):
pytest.importorskip("mcp")