Back to skill

Security audit

Umbrel Proxy Manager

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it says at a high level, but its config-updating script can turn untrusted service mapping data into shell commands.

Review before installing or running. Use only trusted mapping files, prefer dry-run where available, avoid running as root, and back up OpenClaw configuration first. Do not schedule it unattended until the shell command construction is fixed and write operations require clearer control.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/update_openclaw_config.py:12
Finding

Shell Command Injection Through Unvalidated Service Mapping Data

Content
View full analysis
str: """Run a shell command and return output.""" try: result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True) return result.stdout.strip() except subprocess.CalledProcessError as e: print(f"Command failed: {cmd}") print(f"Error: {e.stderr}") return "" ``` ```python def get_openclaw_plugin_config(plugin_name: str) -> Dict: """Get current OpenClaw config for a plugin.""" cmd = f"openclaw config get plugins.entries.{plugin_name}" output = run_command(cmd) ``` ```python def update_plugin_config(plugin_name: str, base_url: str) -> bool: """Update OpenClaw config for a plugin.""" config_path = f"plugins.entries.{plugin_name}.config.baseUrl" cmd = f"openclaw config set {config_path} {base_url}" print(f"Updating {plugin_name}: {base_url}") output = run_command(cmd) ``` ```python for mapping in mappings: app_name = mapping['app_name'] proxy_port = mapping['proxy_port'] plugin_name = map_app_to_plugin(app_name) base_url = f"http://localhost:{proxy_port}" # Check current config current_config = get_openclaw_plugin_config(plugin_name) current_url = current_config.get('baseUrl', '') if current_url == base_url: print(f"{plugin_name}: Already configured correctly ✓") continue # Update config if update_plugin_config(plugin_name, base_url): updates_applied += 1 else: updates_failed += 1 ``` ### Technical Analysis The script loads `app_name` and `proxy_port` from a JSON mapping file. The file can be selected through the `--mappings-file` command-line option, and neither field is val ...[truncated 2382 chars]
Remediation
View remediation
str: try: result = subprocess.run( args, capture_output=True, text=True, check=True, ) return result.stdout.strip() except subprocess.CalledProcessError as exc: print(f"Command failed: {args!r}") print(f"Error: {exc.stderr}") return "" ``` Call it as follows: ```python run_command([ "openclaw", "config", "get", f"plugins.entries.{plugin_name}", ]) ``` ```python run_command([ "openclaw", "config", "set", config_path, base_url, ]) ``` 2. **Validate plugin names** Prefer an explicit allowlist of supported OpenClaw plugins. If arbitrary plugin names are required, enforce a restrictive pattern such as: ```python import re if not re.fullmatch(r"[A-Za-z0-9._-]+", plugin_name): raise ValueError("Invalid plugin name") ``` 3. **Validate ports as integers** Reject strings, booleans, shell syntax, and out-of-range values: ```python if isinstance(proxy_port, bool): raise ValueError("Invalid proxy port") proxy_port = int(proxy_port) if not 1 <= proxy_port <= 65535: raise ValueError("Proxy port is outside the valid range") ``` 4. **Validate the mapping schema** Before processing, ensure each entry is an object containing only expected fields with defined types. Reject malformed or unexpected values rather than attempting to use them. 5. **Treat externally supplied mapping files as untrusted** Document the trust boundary for `--mappings-file`. Avoid processing mapping files from shared or attacker-writable directories, and verify ownership and permissions when appropriat ...[truncated 281 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (21)

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding

The code broadly aligns with part of the description in that it verifies accessibility of certain Umbrel-related proxy endpoints and updates OpenClaw configuration. However, the declared purpose overstates the behavior: the script does not discover services dynamically, inspect Docker networking, or map internal container IPs to exposed host ports. Instead, it checks for specific container names and tests predetermined localhost ports for a small fixed set of services. This is a material description-behavior mismatch because the primary claimed automation and discovery capabilities are not present.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description emphasizes service discovery, proxy/port mapping, and configuration updates for OpenClaw. In contrast, the provided script is a diagnostic utility: it reads existing service mappings from a file, probes proxy and internal endpoints over HTTP, summarizes health/status, and optionally writes results to a JSON file. While connectivity testing is adjacent to interacting with Umbrel proxy services, it is materially different from the declared primary purpose and omits the key advertised behaviors of discovery, mapping, and config updating.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

The code partially matches the description because it does interact with Umbrel-exposed proxy endpoints and updates OpenClaw config. However, key claimed behaviors are not implemented: there is no automatic discovery of running services, no extraction or mapping of Docker internal IPs, and no general proxy-service management logic. Instead, the script is a targeted health-check-and-config-sync utility for a small hardcoded set of services on expected localhost ports. That is a material description-to-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding

The declared description presents a broader capability: discovering Umbrel proxy services, mapping Docker networking details, and updating OpenClaw configuration. This code chunk only performs the last part. It reads service mappings from an existing JSON file, derives localhost proxy URLs from proxy_port values, and updates OpenClaw config entries through the openclaw CLI. There is no Docker inspection, no service discovery, and no internal-IP-to-host-port mapping logic in the supplied code. Because the description claims core capabilities that are absent from this chunk, the description does not accurately represent the actual behavior shown.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · scripts/discover_umbrel_services.py (reported line 15)May include surrounding context.

python
def run_command(cmd: str) -> str:
    """Run a shell command and return output."""
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True)
        return result.stdout.strip()
    except subprocess.CalledProcessError as e:
        print(f"Command failed: {cmd}")

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · scripts/test_connectivity.py (reported line 18)May include surrounding context.

python
def run_command(cmd: str) -> str:
    """Run a shell command and return output."""
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True)
        return result.stdout.strip()
    except subprocess.CalledProcessError as e:
        print(f"Command failed: {cmd}")

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · scripts/umbrel_openclaw_sync.py (reported line 17)May include surrounding context.

python
def run_command(cmd: str) -> str:
    """Run a shell command and return output."""
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True)
        return result.stdout.strip()
    except subprocess.CalledProcessError as e:
        print(f"Command failed: {cmd}")

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
98% confidence
Finding

This is a concrete command-injection risk, not just generic subprocess usage. run_command accepts raw command strings and is used by get_openclaw_plugin_config and update_plugin_config, where plugin_name and base_url are interpolated directly into shell commands; because app_name comes from a JSON mappings file, a crafted entry could turn a configuration update into arbitrary shell execution. The skill's purpose—discovering and reconfiguring proxy-exposed services—means it operates on trusted local infrastructure, so abuse could compromise the host or alter security-sensitive service routing.

Content

Scanner excerpt · scripts/update_openclaw_config.py (reported line 15)May include surrounding context.

python
def run_command(cmd: str) -> str:
    """Run a shell command and return output."""
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True)
        return result.stdout.strip()
    except subprocess.CalledProcessError as e:
        print(f"Command failed: {cmd}")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The README advertises automatic discovery, connectivity testing, and automatic OpenClaw configuration updates, but does not warn that running the skill will modify local service configuration and probe network-exposed services. In an admin-oriented skill this is likely intended functionality, but undocumented automatic changes increase the risk of unsafe use, especially if users run it with elevated privileges or in automation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The recommendation to use the skill for regular maintenance via cron encourages unattended execution of scripts that perform network checks and automatic configuration updates, without any caution about change control, logging, or failure handling. In this context, the danger is not overtly malicious behavior but the increased blast radius of misconfiguration, unexpected service remapping, or repeated probes occurring automatically over time.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding

The skill advertises shell, network, file read, and file write behavior but does not declare any tool scope or permissions boundaries. In a skill ecosystem, that omission weakens reviewability and increases the chance an agent executes powerful operations without explicit user awareness, especially because the documented workflow includes modifying configuration and probing local services.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The description does not prominently warn that the skill can automatically modify OpenClaw configuration, even though the body later states that it updates config automatically and recommends restarting the gateway. Automatic configuration changes can redirect integrations, break service routing, or silently alter trusted endpoints if users invoke the skill without understanding the side effects.

Content

No source excerpt is available for this finding.

File System Enumeration

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code scans file system directories looking for sensitive files. This could be reconnaissance for credential theft.

Content

Scanner excerpt · SKILL.md (reported line 115)May include surrounding context.

md
If config updates fail:
1. Ensure OpenClaw isn't running: `openclaw gateway stop`
2. Check permissions: `ls -la ~/.openclaw/openclaw.json` (or your OpenClaw config path)
3. Use verbose mode: `python3 scripts/update_openclaw_config.py --verbose`

## Example Test Results

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/discover_umbrel_services.py (reported line 15)May include surrounding context.

python
def run_command(cmd: str) -> str:
    """Run a shell command and return output."""
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True)
        return result.stdout.strip()
    except subprocess.CalledProcessError as e:
        print(f"Command failed: {cmd}")

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/test_connectivity.py (reported line 18)May include surrounding context.

python
def run_command(cmd: str) -> str:
    """Run a shell command and return output."""
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True)
        return result.stdout.strip()
    except subprocess.CalledProcessError as e:
        print(f"Command failed: {cmd}")

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/umbrel_openclaw_sync.py (reported line 17)May include surrounding context.

python
def run_command(cmd: str) -> str:
    """Run a shell command and return output."""
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True)
        return result.stdout.strip()
    except subprocess.CalledProcessError as e:
        print(f"Command failed: {cmd}")

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The JSON maps a single externally reachable proxy port to numerous unrelated internal backend services and ports, including databases and daemon endpoints that are not themselves user-facing proxy services. This expands the skill’s knowledge surface beyond its stated purpose and risks exposing or normalizing access paths to sensitive internal services if the data is consumed by other tooling or leaked.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

This file discloses internal container names, Docker IP addresses, and service ports across multiple applications unrelated to simple proxy discovery, effectively revealing internal network topology. Such topology data can aid lateral movement, service enumeration, and targeting of high-value backend components if an attacker gains access to the skill files or downstream generated config.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
96% confidence
Finding

The helper executes arbitrary shell strings with shell=True, and those strings are built later from variable data such as plugin_name and base_url. If an attacker can influence values loaded from umbrel_services.json or app-to-plugin mapping inputs, they can inject shell metacharacters and execute arbitrary commands on the host. In this skill context, the script manages local service discovery and configuration, so it is likely to run with meaningful local access, increasing the risk.

Content

Scanner excerpt · scripts/update_openclaw_config.py (reported line 15)May include surrounding context.

python
def run_command(cmd: str) -> str:
    """Run a shell command and return output."""
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True)
        return result.stdout.strip()
    except subprocess.CalledProcessError as e:
        print(f"Command failed: {cmd}")

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest and module description emphasize discovery, mapping, and updating OpenClaw config, which reads primarily as service inspection and configuration assistance. In addition to discovery, the script persists the full mappings to a local JSON file, which is extra behavior not stated in the file description and goes beyond pure discovery.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

This code persists network-discovery results to disk in umbrel_services.json, which affects user data on the local filesystem. While the function prints after saving, there is no warning or confirmation before the write in the execution path, and the module docstring does not disclose that a file will be created.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.