mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-16 17:55:26 -04:00
fix(cookbook): validate adopt host (#4282)
This commit is contained in:
@@ -790,7 +790,7 @@ def setup_codex_routes(
|
|||||||
norm = dict(body or {})
|
norm = dict(body or {})
|
||||||
sess = (norm.get("tmux_session") or norm.get("session_id") or "").strip()
|
sess = (norm.get("tmux_session") or norm.get("session_id") or "").strip()
|
||||||
model = (norm.get("model") or norm.get("repo_id") or "").strip()
|
model = (norm.get("model") or norm.get("repo_id") or "").strip()
|
||||||
host = (norm.get("host") or norm.get("remote_host") or "").strip()
|
host = validate_remote_host((norm.get("host") or norm.get("remote_host") or "").strip() or None) or ""
|
||||||
port = norm.get("port") or 8000
|
port = norm.get("port") or 8000
|
||||||
import re as _re
|
import re as _re
|
||||||
if not sess or not _re.fullmatch(r"[a-zA-Z0-9_-]+", sess):
|
if not sess or not _re.fullmatch(r"[a-zA-Z0-9_-]+", sess):
|
||||||
|
|||||||
@@ -7,12 +7,39 @@ in ``remoteHost`` would be injected into that command.
|
|||||||
These pin validation on the host/port before they reach the ssh string, matching
|
These pin validation on the host/port before they reach the ssh string, matching
|
||||||
the validators the rest of the cookbook routes already apply.
|
the validators the rest of the cookbook routes already apply.
|
||||||
"""
|
"""
|
||||||
|
import asyncio
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
|
from starlette.requests import Request
|
||||||
|
|
||||||
import routes.codex_routes as codex_routes
|
import routes.codex_routes as codex_routes
|
||||||
|
|
||||||
|
|
||||||
|
def _route_endpoint(path: str, method: str):
|
||||||
|
router = codex_routes.setup_codex_routes()
|
||||||
|
for route in router.routes:
|
||||||
|
if route.path == path and method in route.methods:
|
||||||
|
return route.endpoint
|
||||||
|
raise AssertionError(f"{method} {path} route not found")
|
||||||
|
|
||||||
|
|
||||||
|
def _launch_request() -> Request:
|
||||||
|
request = Request(
|
||||||
|
{
|
||||||
|
"type": "http",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/codex/cookbook/adopt",
|
||||||
|
"headers": [],
|
||||||
|
"state": {},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
request.state.api_token = True
|
||||||
|
request.state.api_token_owner = "alice"
|
||||||
|
request.state.api_token_scopes = ["cookbook:launch"]
|
||||||
|
return request
|
||||||
|
|
||||||
|
|
||||||
def test_rejects_remote_host_with_shell_metacharacters():
|
def test_rejects_remote_host_with_shell_metacharacters():
|
||||||
task = {"remoteHost": "box; rm -rf ~", "sshPort": ""}
|
task = {"remoteHost": "box; rm -rf ~", "sshPort": ""}
|
||||||
with pytest.raises(HTTPException) as exc:
|
with pytest.raises(HTTPException) as exc:
|
||||||
@@ -47,3 +74,26 @@ def test_default_ssh_port_omits_flag():
|
|||||||
)
|
)
|
||||||
assert host == "box"
|
assert host == "box"
|
||||||
assert port_flag == ""
|
assert port_flag == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_adopt_rejects_ssh_option_host_before_shell(monkeypatch):
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
async def fail_if_shell_runs(*args, **kwargs):
|
||||||
|
calls.append((args, kwargs))
|
||||||
|
raise RuntimeError("shell should not run for invalid host")
|
||||||
|
|
||||||
|
monkeypatch.setattr(asyncio, "create_subprocess_shell", fail_if_shell_runs)
|
||||||
|
|
||||||
|
endpoint = _route_endpoint("/api/codex/cookbook/adopt", "POST")
|
||||||
|
body = {
|
||||||
|
"tmux_session": "serve_abc123",
|
||||||
|
"model": "org/model",
|
||||||
|
"host": "-oProxyCommand=sh",
|
||||||
|
}
|
||||||
|
|
||||||
|
with pytest.raises(HTTPException) as exc:
|
||||||
|
asyncio.run(endpoint(_launch_request(), body))
|
||||||
|
|
||||||
|
assert exc.value.status_code == 400
|
||||||
|
assert calls == []
|
||||||
|
|||||||
Reference in New Issue
Block a user