fix: block app_api access to shell routes (#3225)

This commit is contained in:
RaresKeY
2026-06-07 16:19:08 +03:00
committed by GitHub
parent cbbb41dfb1
commit a3784da172
5 changed files with 98 additions and 10 deletions
+8 -6
View File
@@ -2693,14 +2693,15 @@ async def _cookbook_register_task(session_id: str, model: str, host: str,
# Paths the generic `app_api` tool will refuse to call. Auth/token/user
# administration is too risky to route through an agent surface even
# when the agent is admin-context accidental "delete account"
# style mistakes have permanent blast radius.
# administration and host shell execution are too risky to route through an
# agent surface even when the agent is admin-context; accidental account or
# command mistakes have permanent blast radius.
_APP_API_BLOCKLIST_PREFIXES = (
"/api/auth", # login/logout/password
"/api/users", # user CRUD (bare /api/users list+create+delete must also block)
"/api/tokens", # api token mgmt (bare /api/tokens list+create must also block)
"/api/admin", # admin one-shots (wipe etc.)
"/api/shell", # host shell execution must stay behind named command tooling
"/api/backup/restore", # destructive restore
)
@@ -2737,7 +2738,7 @@ _APP_API_BLOCKLIST_METHOD_PATH = (
async def do_app_api(content: str, owner: Optional[str] = None) -> Dict:
"""Generic loopback to any internal Odysseus API endpoint. Lets the
"""Generic loopback to allowed internal Odysseus API endpoints. Lets the
agent reach the full UI-button surface (cookbook, email, notes,
calendar, skills, sessions, gallery, research, etc.) without us
landing a named tool wrapper for every one.
@@ -2751,7 +2752,8 @@ async def do_app_api(content: str, owner: Optional[str] = None) -> Dict:
The `endpoints` action returns the OpenAPI surface (method + path +
summary) so the agent can discover what's reachable. A blocklist
refuses auth/user/admin paths to keep blast radius bounded.
refuses sensitive auth/user/admin/shell paths to keep blast radius
bounded.
"""
import httpx
try:
@@ -2811,7 +2813,7 @@ async def do_app_api(content: str, owner: Optional[str] = None) -> Dict:
if not path.startswith("/"):
path = "/" + path
if any(path.startswith(p) for p in _APP_API_BLOCKLIST_PREFIXES):
return {"error": f"Path blocked for safety: {path}. Auth/user/admin endpoints are off-limits via app_api.", "exit_code": 1}
return {"error": f"Path blocked for safety: {path}. Sensitive endpoints are off-limits via app_api.", "exit_code": 1}
method = (args.get("method") or "GET").upper()
if method not in ("GET", "POST", "PUT", "PATCH", "DELETE"):