{"record":{"id":"170868286e63d000","repo":"odysseus-dev/odysseus","slug":"invalid-characters-in-cmd","errorCode":null,"errorMessage":"Invalid characters in cmd","messagePattern":"Invalid characters in cmd","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"routes/cookbook_helpers.py","lineNumber":764,"sourceCode":"\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\n    pre-fix world) could pass arbitrary shell payloads.\n\n    Leading env-var assignments (e.g. `CUDA_VISIBLE_DEVICES=0 python3 ...`)\n    are stripped before checking the binary — several of our cmd builders\n    prepend them, and they shouldn't trip the allowlist.\n    \"\"\"\n    if v is None or v == \"\":\n        return None\n    # Collapse backslash-newline line continuations into single spaces. Serve\n    # commands (vLLM especially) are routinely pasted multi-line with trailing\n    # `\\` — that's a safe shell/shlex continuation, so the command stays ONE\n    # logical invocation and the leading-token allowlist below still governs.\n    v = re.sub(r\"\\\\[ \\t]*\\r?\\n[ \\t]*\", \" \", v).strip()\n    # Backticks and raw newlines are never legitimate here.\n    if any(c in v for c in (\"`\", \"\\n\", \"\\r\")):\n        raise HTTPException(400, \"Invalid characters in cmd\")\n\n    # Known GGUF launcher prelude → validate the serve invocation(s) it guards.\n    m = _GGUF_PRELUDE_RE.match(v)\n    if m:\n        rest = v[m.end():]\n        # rest is `[ENV=…] python3 -m llama_cpp.server … || [ENV=…] llama-server …`\n        for part in rest.split(\"||\"):\n            _check_serve_binary(part.strip())\n        return v\n\n    # Otherwise: a single invocation — no shell metacharacters allowed. Replace\n    # only the exact command substitutions emitted by the Cookbook UI:\n    # $(printf %s 'safe-path') and the mmproj lookup\n    # $(find <path> -iname 'mmproj*.gguf' 2>/dev/null | sort | head -1).\n    def _replace_safe_subshell(match: re.Match[str]) -> str:\n        subshell = match.group(0)\n        return \"/placeholder/safe/path\" if _is_safe_serve_subshell(subshell) else subshell\n","sourceCodeStart":746,"sourceCodeEnd":782,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/cookbook_helpers.py#L746-L782","documentation":"HTTP 400 from the serve-cmd validator in routes/cookbook_helpers.py (~line 764). After collapsing backslash-newline continuations into spaces, the command is scanned for characters that are never legitimate in this field: backticks and raw CR/LF newlines. Their presence suggests command substitution or multi-line shell input that the leading-token allowlist cannot reason about safely.","triggerScenarios":"cmd containing a backtick substitution such as \"vllm serve `cat /tmp/model.txt` --port 8000\"; a cmd pasted with embedded newlines that are not backslash continuations; a CRLF paste leaving a raw carriage return.","commonSituations":"Windows users pasting CRLF commands so a stray CR survives; users writing backtick-style substitution instead of $(); copying a multi-line shell heredoc into the cmd field.","solutions":["Replace backtick substitutions with literal values before submitting","Keep the command on one logical line; use trailing backslash only for line continuations","Strip CR characters client-side when submitting from Windows (normalize CRLF to spaces)","Compute any dynamic value client-side and inline the result into cmd"],"exampleFix":"# before\ncmd = \"vllm serve `cat /tmp/model.txt` --port 8000\"\n# after\ncmd = \"vllm serve mistralai/Mistral-7B --port 8000\"","handlingStrategy":"validation","validationCode":"import re\n\ndef clean_serve_cmd(cmd: str) -> str:\n    cmd = re.sub(r\"\\\\[ \\t]*\\r?\\n[ \\t]*\", \" \", cmd).strip()\n    if any(c in cmd for c in (\"`\", \"\\n\", \"\\r\")):\n        raise ValueError(\"cmd contains backtick or raw newline\")\n    return cmd","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Expand all substitutions client-side; submit literal values","Normalize CRLF before submitting from Windows","Never paste heredocs or multi-line shell into the cmd field"],"tags":["validation","shell-injection","serve","http-400"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}