fix: block app_api access to Cookbook host controls (#3231)

This commit is contained in:
RaresKeY
2026-06-07 20:20:11 +03:00
committed by GitHub
parent 00e8084969
commit 3a91c11ff8
5 changed files with 111 additions and 10 deletions
+90
View File
@@ -470,6 +470,52 @@ async def test_app_api_blocks_shell_routes_before_loopback(monkeypatch):
assert "Sensitive endpoints" in result["error"]
@pytest.mark.asyncio
async def test_app_api_blocks_cookbook_host_control_routes_before_loopback(monkeypatch):
import httpx
from src.tool_implementations import do_app_api
class UnexpectedAsyncClient:
def __init__(self, *args, **kwargs):
raise AssertionError("app_api should block host-control routes before loopback")
monkeypatch.setattr(httpx, "AsyncClient", UnexpectedAsyncClient)
blocked_calls = (
(
"api/cookbook/packages/install",
{"pip": "hf_transfer"},
"package installation is host code execution",
),
(
"/api/cookbook/rebuild-engine",
{"engine": "llamacpp"},
"engine rebuild mutates local or remote host state",
),
(
"/api/cookbook/kill-pid",
{"pid": 12345, "signal": "TERM"},
"process signalling is host control",
),
)
for path, body, error_text in blocked_calls:
result = await do_app_api(
json.dumps(
{
"action": "call",
"method": "POST",
"path": path,
"body": body,
}
),
owner="admin",
)
assert result["exit_code"] == 1
assert error_text in result["error"]
@pytest.mark.asyncio
async def test_app_api_endpoint_discovery_hides_shell_routes(monkeypatch):
_install_core_middleware_stub(monkeypatch)
@@ -513,6 +559,50 @@ async def test_app_api_endpoint_discovery_hides_shell_routes(monkeypatch):
assert all(not endpoint["path"].startswith("/api/shell") for endpoint in result["endpoints"])
@pytest.mark.asyncio
async def test_app_api_endpoint_discovery_hides_cookbook_host_control_routes(monkeypatch):
_install_core_middleware_stub(monkeypatch)
import httpx
from src.tool_implementations import do_app_api
class FakeResponse:
def json(self):
return {
"paths": {
"/api/cookbook/packages": {"get": {"summary": "List Cookbook Packages"}},
"/api/cookbook/packages/install": {"post": {"summary": "Install Package"}},
"/api/cookbook/rebuild-engine": {"post": {"summary": "Rebuild Engine"}},
"/api/cookbook/kill-pid": {"post": {"summary": "Kill Process"}},
"/api/cookbook/gpus": {"get": {"summary": "List GPUs"}},
}
}
class FakeAsyncClient:
def __init__(self, *args, **kwargs):
pass
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc, tb):
return False
async def get(self, *args, **kwargs):
return FakeResponse()
monkeypatch.setattr(httpx, "AsyncClient", FakeAsyncClient)
result = await do_app_api(json.dumps({"action": "endpoints", "filter": "cookbook"}), owner="admin")
assert result["exit_code"] == 0
paths = {(endpoint["method"], endpoint["path"]) for endpoint in result["endpoints"]}
assert ("GET", "/api/cookbook/packages") in paths
assert ("GET", "/api/cookbook/gpus") in paths
assert ("POST", "/api/cookbook/packages/install") not in paths
assert ("POST", "/api/cookbook/rebuild-engine") not in paths
assert ("POST", "/api/cookbook/kill-pid") not in paths
@pytest.mark.asyncio
async def test_public_agent_policy_blocks_sensitive_tools(monkeypatch):
auth_mod = _install_core_auth_stub(monkeypatch)