"""What the MCP tools send, whatever an agent hands them.""" import asyncio import httpx from fluksio.mcp import server def test_a_name_cannot_climb_out_of_its_route() -> None: """httpx resolves ``..``, so ``save_flow("../ai/config")`` was ``PUT /ai/config``. A prompt-injected assistant acting for a superuser could repoint the provider that way while the stored key stayed. """ seen: list[str] = [] def record(request: httpx.Request) -> httpx.Response: seen.append(f"{request.method} {request.url.raw_path.decode()}") return httpx.Response(200, json={}) async def calls() -> None: previous = server._client mock = httpx.AsyncClient( transport=httpx.MockTransport(record), base_url="http://test" ) server.set_client(mock) try: await server.save_flow("../ai/config", {}) await server.delete_flow("..") await server.get_node_source("..", "../source") finally: server.set_client(previous) await mock.aclose() asyncio.run(calls()) assert seen == [ "PUT /api/v1/flows/%2E%2E%2Fai%2Fconfig", "DELETE /api/v1/flows/%2E%2E", "GET /api/v1/flows/%2E%2E/nodes/%2E%2E%2Fsource/source", ]