{"record":{"id":"007af60c5b089681","repo":"odysseus-dev/odysseus","slug":"cmd-binary-base-or-empty-is-not-allowed-m","errorCode":null,"errorMessage":"cmd binary '{base or '(empty)'}' is not allowed. Must start with one of: {', '.join(sorted(_SERVE_CMD_ALLOWLIST))}","messagePattern":"cmd binary '(.+?)' is not allowed\\. Must start with one of: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"routes/cookbook_helpers.py","lineNumber":730,"sourceCode":"        return f\"{match.group('flag')}{match.group('sep')}{quote}{mapped}{quote}\"\n\n    return _LLAMA_CPP_PYTHON_TYPE_FLAG_RE.sub(repl, cmd)\n\n\ndef _check_serve_binary(seg: str) -> None:\n    \"\"\"Validate that a single command segment starts with an allowlisted binary\n    (after skipping leading env-var assignments like `CUDA_VISIBLE_DEVICES=0`).\"\"\"\n    try:\n        tokens = shlex.split(seg) if seg.strip() else []\n    except ValueError:\n        raise HTTPException(400, \"Invalid cmd — could not parse\")\n    if not tokens:\n        return\n    env_re = re.compile(r\"^[A-Za-z_][A-Za-z0-9_]*=\")\n    first = next((t for t in tokens if not env_re.match(t)), \"\")\n    base = os.path.basename(first)\n    if base not in _SERVE_CMD_ALLOWLIST:\n        raise HTTPException(\n            400,\n            f\"cmd binary '{base or '(empty)'}' is not allowed. Must start with one of: \"\n            f\"{', '.join(sorted(_SERVE_CMD_ALLOWLIST))}\",\n        )\n\n\ndef _is_safe_serve_subshell(subshell: str) -> bool:\n    return bool(\n        _SAFE_PRINTF_SUBSHELL_RE.fullmatch(subshell)\n        or _SAFE_FIND_MMPROJ_SUBSHELL_RE.fullmatch(subshell)\n    )\n\n\ndef _validate_serve_cmd(v: str | None) -> str | None:\n    \"\"\"Reject serve commands that aren't in the allowlist or contain shell metachars.\n\n    `req.cmd` is dropped verbatim into a bash/PowerShell wrapper script and\n    executed in a tmux session. Without this gate, an admin (or anyone in the","sourceCodeStart":712,"sourceCodeEnd":748,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/cookbook_helpers.py#L712-L748","documentation":"HTTP 400 from _check_serve_binary() in routes/cookbook_helpers.py: the command's first meaningful token (after skipping leading VAR=value assignments like CUDA_VISIBLE_DEVICES=0) has a basename that is not in _SERVE_CMD_ALLOWLIST. Only allowlisted server binaries may be launched; everything else — shells, curl, arbitrary executables — is refused.","triggerScenarios":"cmd=\"bash -c 'vllm serve ...'\", cmd=\"./start.sh\", cmd=\"python -m myserver ...\" (python vs python3), or a segment containing only env assignments, yielding '(empty)'.","commonSituations":"Using a wrapper script or shell to start the model server; using 'python' instead of 'python3'; a new or renamed inference binary that has not been added to _SERVE_CMD_ALLOWLIST yet; trying to run arbitrary commands through the serve endpoint.","solutions":["Start the command directly with an allowlisted binary — the error message lists the full sorted allowlist (e.g. vllm, llama-server, python3)","Drop bash -c / sh -c / ./script.sh wrappers and invoke the server binary itself","If a new binary is legitimately needed, add it to _SERVE_CMD_ALLOWLIST in routes/cookbook_helpers.py and review the security implications","Put environment setup in env_prefix or leading VAR=value assignments, not in a wrapper command"],"exampleFix":"# before\ncmd = \"bash -c 'vllm serve mistralai/Mistral-7B --port 8000'\"\n# after\ncmd = \"vllm serve mistralai/Mistral-7B --port 8000\"","handlingStrategy":"validation","validationCode":"import os, re, shlex\n# Mirror the server list; keep in sync with _SERVE_CMD_ALLOWLIST in cookbook_helpers.py:\n_SERVE_CMD_ALLOWLIST = {\"vllm\", \"llama-server\", \"python3\"}\n\ndef first_binary(cmd: str) -> str:\n    tokens = shlex.split(cmd)\n    env_re = re.compile(r\"^[A-Za-z_][A-Za-z0-9_]*=\")\n    first = next((t for t in tokens if not env_re.match(t)), \"\")\n    return os.path.basename(first)\n\nif first_binary(cmd) not in _SERVE_CMD_ALLOWLIST:\n    raise ValueError(f\"binary {first_binary(cmd)!r} not allowlisted\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Invoke the server binary directly; no bash -c, sh, or wrapper scripts","Prefer python3 over python","Sync your client allowlist with _SERVE_CMD_ALLOWLIST whenever the backend updates"],"tags":["security","allowlist","serve","http-400","command-injection"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}