Back to skill

Security audit

JoyIn Robot Control

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent JoyIn robot-control skill, but it can move a physical robot, access camera/speech data, and change network or AI settings without enforced safety checks or strong secret protections.

Install only if you trust the publisher and can supervise the robot physically. Use limited JoyIn and LLM credentials, keep the API base on a trusted HTTPS JoyIn endpoint, avoid putting real WiFi passwords or API keys in command lines, get consent before camera or speech-result access, and manually run preflight before any movement because the CLI will not enforce it.

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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/robot_cmd.py:35
Finding
Credentials Can Be Redirected to an Untrusted or Cleartext API Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/robot_cmd.py:35-54` and `scripts/robot_cmd.py:531-532` **Vulnerability Type**: Unrestricted destination for authenticated network requests **Risk Level**: High ### Complete Code Snippet ```python def get_config() -> dict: return { "base_url": _env("JOYIN_API_BASE", "https://api-open-test.joyin-ai.com").rstrip("/"), "auth_key": _env("JOYIN_AUTH_KEY"), "device_sn": _env("JOYIN_DEVICE_SN"), "device_type_id": _env("JOYIN_DEVICE_TYPE_ID", "3"), } def _headers(cfg: dict) -> dict: return { "Content-Type": "application/json", "Authorization": cfg["auth_key"], "Device-Sn": cfg["device_sn"], "Device-Type-Id": str(cfg["device_type_id"]), } ``` The command-line override is applied without validation: ```python if args.base_url: cfg["base_url"] = args.base_url.rstrip("/") ``` ### Technical Analysis The API destination can be supplied through `JOYIN_API_BASE` or the global `--base-url` option. The implementation does not require HTTPS, validate the hostname, reject embedded credentials, or restrict the destination to an approved JoyIn domain. All API requests use `_headers()`, which attaches the JoyIn authorization key, device serial number, and device type identifier. Consequently, any command can disclose these values to the configured host. If an attacker can influence the environment or command arguments, authenticated requests can be redirected to an attacker-controlled server. A cleartext `http://` endpoint would also expose credentials and request contents to network observers. This behavior exceeds the minimum privilege necessary for robot control because the official functionality only requires communication with a trusted JoyIn API endpoint. There is no evidence that the default JoyIn URL is malicious, but the missing destination and transport controls create an exploitable credential-disclosure path. ### Attac ...[truncated 1108 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require `https://` for every API base URL and reject cleartext HTTP. 2. Parse the URL with `urllib.parse.urlparse()` and reject user-info, fragments, malformed ports, and unexpected schemes. 3. Allowlist official JoyIn API hostnames by default. 4. If custom endpoints are necessary, require an explicit opt-in configuration and display the destination before transmitting credentials. 5. Prevent authenticated cross-host redirects, or verify the redirect destination before forwarding the `Authorization` and device headers. 6. Consider certificate pinning where operationally feasible. 7. Keep separate credentials for development and production endpoints, with the narrowest available device-level permissions. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/robot_cmd.py:474
Finding
Sensitive Credentials Are Accepted Through Observable Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/robot_cmd.py:474-490` and `scripts/robot_cmd.py:525-536`; documented in `SKILL.md:247-255` **Vulnerability Type**: Secret exposure through process arguments and command history **Risk Level**: Medium ### Complete Code Snippet ```python # -- WiFi -- s = sub.add_parser("wifi", help="Configure robot WiFi") s.add_argument("--ssid", required=True, help="WiFi SSID") s.add_argument("--password", required=True, help="WiFi password") s.set_defaults(func=cmd_wifi) # -- LLM config -- s = sub.add_parser("llm_register", help="Register a custom LLM") s.add_argument("--name", required=True, help="Display name") s.add_argument("--base-url", required=True, help="OpenAI-compatible endpoint") s.add_argument("--api-key", required=True, help="API key") s.add_argument("--model", default=None, help="Model name (optional)") s.set_defaults(func=cmd_llm_register) ``` The global JoyIn authorization key can also be supplied as an argument: ```python p.add_argument("--base-url", help="Override JOYIN_API_BASE") p.add_argument("--auth-key", help="Override JOYIN_AUTH_KEY") p.add_argument("--device-sn", help="Override JOYIN_DEVICE_SN") p.add_argument("--device-type-id", help="Override JOYIN_DEVICE_TYPE_ID (3=Walle, 2=Mini)") ``` The documentation recommends secret-bearing commands: ```bash python3 {baseDir}/scripts/robot_cmd.py wifi --ssid "MyWiFi" --password "12345678" python3 {baseDir}/scripts/robot_cmd.py llm_register --name "My GPT" --base-url "https://api.openai.com/v1" --api-key "sk-xxx" --model "gpt-4" ``` ### Technical Analysis Wi-Fi passwords, third-party LLM API keys, and optionally the JoyIn authorization key are accepted directly in the process argument vector. Command-line arguments may be exposed through process inspection, shell history, terminal recording, automation logs, Agent tool-call traces, crash diagnostics, or monitoring systems. The application does not print these input arguments directly, but avo ...[truncated 1107 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Read secrets from protected environment variables, standard input, or an OpenClaw credential provider rather than ordinary command arguments. 2. Provide a non-echoing interactive prompt using `getpass.getpass()` for direct human use. 3. Support file-descriptor or protected-file input when noninteractive execution is required. 4. Remove secret-bearing examples from `SKILL.md` and replace them with credential-provider or environment-reference examples. 5. Deprecate `--auth-key`, `--api-key`, and `--password`, or retain them only behind an explicit warning for compatibility. 6. Ensure execution logs and Agent tool traces redact known secret fields. 7. Rotate any credential that may already have been included in retained command histories or logs. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/robot_cmd.py:323
Finding
Wi-Fi Password Is Only Base64-Encoded Before Network Transmission<![CDATA[ ## Vulnerability Details **File Location**: `scripts/robot_cmd.py:323-333` **Vulnerability Type**: Reversible encoding of sensitive network credentials **Risk Level**: Medium ### Complete Code Snippet ```python def cmd_wifi(cfg, args): """Set robot WiFi (SSID and password are base64-encoded automatically).""" body = { "wifi": base64.b64encode(args.ssid.encode()).decode(), "passwd": base64.b64encode(args.password.encode()).decode(), "device_sn": cfg["device_sn"], "device_type_id": cfg["device_type_id"], } return api_post(cfg, "/v1/device/wifi/set", body) ``` ### Technical Analysis Base64 is reversible encoding and provides no confidentiality. The flagged code places the encoded password in an HTTP request body; it does not directly print the password or encoded value to stdout. The value printed by `main()` is the API response, not the outbound request body. Therefore, this line is not independently evidence of a covert stdout exfiltration channel. The encoding appears to implement the documented JoyIn API protocol and is functionally necessary if that protocol requires Base64 fields. The security weakness arises because the code treats the reversible representation as suitable for transmission while the destination and HTTPS transport are not enforced. An attacker controlling the configured API endpoint, or a network observer when HTTP is used, can trivially decode the password. ### Attack Path 1. The API base is configured as an attacker-controlled endpoint or a cleartext HTTP endpoint. 2. A user invokes the `wifi` command with a real SSID and password. 3. `cmd_wifi()` Base64-encodes both values and submits them to `/v1/device/wifi/set`. 4. The receiver or network observer captures the request body. 5. The attacker Base64-decodes the `passwd` field and obtains the original Wi-Fi password. ### Impact Assessment The exposed password may allow unauthorized access to the configured wireless network, ...[truncated 345 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce HTTPS and trusted endpoint validation before accepting Wi-Fi credentials. 2. Treat Base64 data as plaintext-sensitive throughout logging, tracing, and error handling. 3. Never log request bodies for the Wi-Fi provisioning endpoint. 4. If the official API supports it, use application-level authenticated encryption for Wi-Fi credentials in addition to TLS. 5. If the protocol cannot be changed, document clearly that Base64 is protocol encoding rather than encryption. 6. Obtain the Wi-Fi password through a protected secret-input mechanism instead of a command-line argument. ]]>

other

Warning
Location
scripts/robot_cmd.py:542
Finding
Documented Physical Safety Preflight Checks Are Not Enforced by the CLI<![CDATA[ ## Vulnerability Details **File Location**: `scripts/robot_cmd.py:97-153`, `scripts/robot_cmd.py:248-298`, and `scripts/robot_cmd.py:542-543`; safety requirement documented in `SKILL.md:53-92` **Vulnerability Type**: Physical command safety-control bypass **Risk Level**: Medium ### Complete Code Snippet Movement commands submit requests directly: ```python def cmd_move(cfg, args): """Chassis joystick movement — 8 directions. Send at ~100ms intervals.""" d = args.direction if d not in VALID_DIRECTIONS: return {"code": 400, "msg": f"Invalid direction '{d}'. Valid: {VALID_DIRECTIONS}"} return post_cmd(cfg, { "cmd_type": "remote_control", "status": "on", "data": {"type": "1", "command": d}, }) ``` A separate preflight function detects unsafe states: ```python def cmd_preflight(cfg, _args): """Pre-flight check: verify device is online and ready. Run this BEFORE sending any command.""" result = api_get(cfg, "/v1/device/status") if result.get("code") != 200: return { "ready": False, "reason": f"Failed to reach device API: {result.get('msg', 'unknown error')}", "raw": result, } data = result.get("data", {}) current_status = data.get("current_status", "offline") battery = data.get("battery", -1) is_charging = data.get("is_charging", False) issues = [] if current_status == "offline": issues.append("Device is OFFLINE — cannot accept commands") if current_status == "ota": issues.append("Device is updating firmware (OTA) — wait until complete") if isinstance(battery, (int, float)) and battery != -1 and battery < 10 and not is_charging: issues.append(f"Battery critically low ({battery}%) — send 'charge' command first") ``` However, `main()` directly invokes the selected command: ```python result = args.func(cfg, args) print(json.dumps(result, ensure_ascii=False, indent=2)) ``` The docume ...[truncated 1902 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Automatically execute the status/preflight check before every physical or state-changing command. 2. Define command classes so that read-only operations and emergency stop remain available while unsafe operations are blocked. 3. Enforce hard denial for offline, OTA, map-building, and battery-critical states. 4. Implement the documented rule that movement is refused below 5 percent battery, except for charging and status operations. 5. Require explicit confirmation or a dedicated override for commands that conflict with follow, patrol, guard, remote-control, or active-action modes. 6. Keep `stop` available without a successful preflight so emergency response is never blocked by an API status failure. 7. Return machine-readable denial reasons and record safety decisions without logging credentials. 8. Add automated tests proving that direct command invocation cannot bypass the safety policy. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (9)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill exposes meaningful capabilities via environment variables and networked robot control, but it does not declare any explicit tool scope such as permissions or allowed-tools. That weakens policy enforcement and increases the chance an agent can invoke networked actions or access secrets without clear least-privilege boundaries.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill includes live video streaming and ASR-related capabilities without an explicit privacy notice, consent requirement, or warning about capturing people, surroundings, or speech. In a robot-control context, these features can enable covert surveillance or unexpected collection/transmission of sensitive data if triggered casually by an agent.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The WiFi and LLM configuration commands handle highly sensitive secrets such as wireless credentials and third-party API keys, but the skill does not warn against exposing them in command history, logs, or downstream services. Because these values may be transmitted to the robot vendor API or external model providers, accidental credential leakage could compromise networks or cloud accounts.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
# Register a custom LLM
python3 {baseDir}/scripts/robot_cmd.py llm_register --name "My GPT" --base-url "https://api.openai.com/v1" --api-key "sk-xxx" --model "gpt-4"

# List registered LLMs
python3 {baseDir}/scripts/robot_cmd.py llm_list
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The CLI exposes commands that directly cause physical movement and operational state changes, including remote control, arm/head motion, charging behavior, and emergency stop, without any built-in confirmation, safety interlock, or operator warning. In a robot-control context this is materially dangerous because accidental, scripted, or unauthorized invocation can cause physical harm, collisions, or equipment misuse.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The code can retrieve the robot's latest ASR result, which may contain sensitive user speech content, but this capability is not disclosed in the skill description. Undisclosed access to recognized speech is privacy-relevant because users may not expect a robot-control skill to expose conversation transcripts or recent utterances.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The skill implements WiFi SSID/password configuration but the advertised skill description does not disclose that it can change network settings or transmit WiFi credentials. This creates a consent and transparency problem: a user may invoke a seemingly robot-control skill without realizing it can alter connectivity or handle sensitive credentials, increasing the risk of unsafe or unauthorized reconfiguration.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill can transmit sensitive WiFi credentials and third-party LLM API keys to remote endpoints, yet there is no explicit disclosure or confirmation at invocation time. This is dangerous because users may unknowingly send highly sensitive secrets or alter the robot's network/AI backend configuration, leading to credential exposure, service hijacking, or persistent compromise of device behavior.

Natural-Language Policy Violations

Low
Confidence
80% confidence
Finding
The TTS example and several command annotations are presented only in Chinese, and the skill does not indicate that language is configurable or user-selectable. Under the policy, forcing a specific language without user opt-in can be a locale/language policy violation.

Static analysis

No suspicious patterns detected.