test_knowtation_mcp_security.py
python
sha256:cf6265cea8c21d9228d90dec13ef6ec2dab5103d466db9cc4590681832de4bf8
docs(KD-STAGING): sync governance after KD-6b DONE
Human
13 days ago
| 1 | """Tier 7 — security tests for knowtation MCP.""" |
| 2 | |
| 3 | from __future__ import annotations |
| 4 | |
| 5 | import pathlib |
| 6 | |
| 7 | import pytest |
| 8 | |
| 9 | from muse.core.attestation import get_attestation_provider, unregister_attestation_provider |
| 10 | from muse.mcp.errors import MCPApplicationError |
| 11 | from muse.mcp.server import MuseMCPServer |
| 12 | from muse.mcp.namespaces.knowtation import KnowtationNamespace |
| 13 | from muse.plugins.knowtation.mcp_helpers import validate_note_path |
| 14 | |
| 15 | |
| 16 | @pytest.fixture |
| 17 | def smoke_root() -> pathlib.Path: |
| 18 | return pathlib.Path("/Users/aaronrenecarvajal/MUSE_HUB/knowtation-vault-smoke") |
| 19 | |
| 20 | |
| 21 | def test_path_traversal_rejected() -> None: |
| 22 | with pytest.raises(Exception) as exc: |
| 23 | validate_note_path("../../secret.md") |
| 24 | assert exc.value.reason == "PATH_INVALID" |
| 25 | |
| 26 | |
| 27 | def test_activation_gate_blocks_tools(tmp_path: pathlib.Path) -> None: |
| 28 | (tmp_path / ".muse").mkdir() |
| 29 | (tmp_path / ".muse" / "repo.json").write_text('{"domain":"code","repo_id":"x"}', encoding="utf-8") |
| 30 | server = MuseMCPServer(tmp_path) |
| 31 | resp = server.handle_request( |
| 32 | { |
| 33 | "jsonrpc": "2.0", |
| 34 | "id": 1, |
| 35 | "method": "tools/call", |
| 36 | "params": {"name": "knowtation/search", "arguments": {"query": "x"}}, |
| 37 | } |
| 38 | ) |
| 39 | assert resp is not None |
| 40 | assert "error" in resp |
| 41 | |
| 42 | |
| 43 | def test_provider_unregistered_attest(smoke_root: pathlib.Path) -> None: |
| 44 | ns = KnowtationNamespace(smoke_root) |
| 45 | unregister_attestation_provider("knowtation") |
| 46 | try: |
| 47 | with pytest.raises(MCPApplicationError) as exc: |
| 48 | ns.call_tool("knowtation/attest", {"commit_id": "0" * 64}) |
| 49 | assert exc.value.reason == "PROVIDER_UNREGISTERED" |
| 50 | finally: |
| 51 | from muse.mcp.bootstrap import bootstrap_knowtation_mcp |
| 52 | |
| 53 | bootstrap_knowtation_mcp(smoke_root) |
| 54 | assert get_attestation_provider("knowtation") is not None |
| 55 | |
| 56 | |
| 57 | def test_no_secrets_in_search_error(smoke_root: pathlib.Path) -> None: |
| 58 | ns = KnowtationNamespace(smoke_root) |
| 59 | out = ns.call_tool("knowtation/search", {"query": "test", "mode": "lexical"}) |
| 60 | blob = str(out) |
| 61 | for secret in ("MUSE_AIR_HMAC_KEY", "KNOWTATION_HUB_PORT", "BEGIN PRIVATE KEY"): |
| 62 | assert secret not in blob |
| 63 | |
| 64 | |
| 65 | def test_resource_path_escape_rejected(smoke_root: pathlib.Path) -> None: |
| 66 | ns = KnowtationNamespace(smoke_root) |
| 67 | with pytest.raises(MCPApplicationError): |
| 68 | ns.read_resource("knowtation://vault/../../etc/passwd.md") |
File History
1 commit
sha256:cf6265cea8c21d9228d90dec13ef6ec2dab5103d466db9cc4590681832de4bf8
docs(KD-STAGING): sync governance after KD-6b DONE
Human
13 days ago