fix(runtime): enforce local control across Cookbook wrappers

This commit is contained in:
Alexandre Teixeira
2026-10-02 23:39:13 +01:00
parent bcc0e54e1b
commit 3834cd72b1
6 changed files with 68 additions and 10 deletions
+11
View File
@@ -118,6 +118,17 @@ def _require_cookbook_scope(request: Request, allowed: set[str]) -> str:
because cookbook surfaces expose host topology, task logs, tmux
commands, and model-serving controls.
"""
# Internal transport/owner attribution is not a scoped external credential.
# In no-login mode, this wrapper must preserve the native local-operator
# boundary even though it invokes endpoint functions without dependencies.
from src.agent_runtime.authority import is_internal_tool_request
from src.auth_helpers import _auth_disabled
from core.middleware import INTERNAL_TOOL_HEADER
if is_internal_tool_request(request) or request.headers.get(INTERNAL_TOOL_HEADER):
raise HTTPException(403, "Internal Cookbook calls require a dedicated producer")
if _auth_disabled():
from routes.shell_routes import _require_admin
_require_admin(request)
owner = _scope_owner(request, allowed)
if not getattr(request.state, "api_token", False):
require_admin(request)
+9
View File
@@ -426,6 +426,13 @@ def setup_cookbook_routes() -> APIRouter:
if not claimed:
_require_admin(request)
router = APIRouter(tags=["cookbook"], dependencies=[Depends(protect_native_control)])
def protect_local_model_producer(request, remote_host):
# Scoped wrappers can call endpoint functions directly, without FastAPI
# dependencies. Enforce native control at the actual producer as well.
if not remote_host and getattr(request.state, "local_model_authority", None) is None:
from routes.shell_routes import _require_admin
_require_admin(request)
_cookbook_state_path = Path(COOKBOOK_STATE_FILE)
_state_get_cache = {"ts": 0.0, "mtime": 0.0, "value": None}
_tasks_status_cache = {"ts": 0.0, "value": None}
@@ -1100,6 +1107,7 @@ def setup_cookbook_routes() -> APIRouter:
"""Download a HuggingFace model in a tmux session.
Uses `hf download` CLI directly — runs in tmux via `script -qc`
for real TTY progress, streams ANSI-stripped output via log file."""
protect_local_model_producer(request, req.remote_host)
require_admin(request)
# Defence-in-depth: even though this endpoint is admin-gated, refuse
# values that would land in shell contexts with metacharacters.
@@ -2018,6 +2026,7 @@ def setup_cookbook_routes() -> APIRouter:
keep strict validation, but serving local cached models must not require
a fake org/name wrapper.
"""
protect_local_model_producer(request, req.remote_host)
require_admin(request)
# Defence-in-depth: reject values that could break out of shell contexts.
validate_remote_host(req.remote_host)